# Jovian Zone System v0.1.0

A polygon-based zone system for defining map regions with encounter difficulty, modifiers, and safe areas.

## Overview

The Zone System lets you paint polygon regions on your map and assign encounter rules to each region. At runtime, you query a world position and get back a fully resolved `ZoneContext` with the encounter table, difficulty tier, and chance.

### Key Features

- Three zone roles: Base, Modifier, and Override for layered encounter design
- Visual polygon editing: Drag vertices, insert on edges, delete vertices in the Scene view
- Concave polygon support: Ear-clipping triangulation renders any shape correctly
- Multi-plane support: XY, XZ, or YZ — one setting controls everything
- No physics dependency: Pure math ray-casting with AABB pre-rejection
- Save workflow: Create → Edit → Save with duplicate ID/name validation
- Role-based colors: Configured in settings, auto-applied on role change
- Zone duplication: Independent copies with unique IDs and assets
- JSON export: For runtime loading or external tools
- UPM package: Standard Unity Package Manager layout

## Setup & Quick Start

### 1. Scene Setup

1. Create a GameObject and add the `ZonesObjectHolder` component.
2. Set the Map Plane field to match your map orientation.

Map Plane | Use Case | Axes | Ignored
--- | --- | --- | ---
`XY` | Flat sprite map, UI map | X, Y | Z
`XZ` | 3D world map (standard Unity 3D) | X, Z | Y
`YZ` | Side-on map | Y, Z | X

### 2. Create Your First Zone

1. Open Window → Zone System → Zone Editor.
2. Click Create New Zone.
3. Enter a name, select a shape (Square, Circle, or Polygon).
4. Click Create & Edit.
5. Edit zone data fields (role, priority, encounter settings).
6. Click Save Zone to persist the asset.
7. Use Scene view handles to adjust the polygon shape.

Note: The zone asset is not saved to disk until you click the Save button. You can edit all fields freely before committing.

## Zone Editor Window

The Zone Editor window (Window → Zone System → Zone Editor) is the primary tool for managing zones.

### Zone List View

Shows all `ZoneInstance` objects in the current scene. Each row displays:

- Color swatch: Role-based color from editor settings
- Zone name and summary (role, priority, tier, chance)
- Select button: Opens the zone for editing
- Duplicate button (`📋`): Creates an independent copy with a new asset
- Delete button (`✕`): Removes the zone and its asset

Zones with missing `ZoneData` show a warning icon with options to add data or delete the zone.

### Create Zone

Click Create New Zone to open the creation dropdown:

- Enter a Zone Name
- Select a Shape (Square, Circle, Polygon)
- Click Create & Edit

The zone is created in-memory and enters edit mode immediately. You must click Save to persist the asset.

### Edit Mode

When editing a zone, all `ZoneData` fields are available inline:

- Identity: Zone ID, Zone Name, Role, Priority, Shape
- Role-specific fields: Shown/hidden based on the selected role
- Save Zone button: Validates and persists changes
- Delete Zone button: Removes the zone entirely

#### Save Validation

On save, the editor checks for:

- Duplicate Zone ID: No two assets may share the same `zoneId`
- Duplicate asset name: No two assets may have the same file name

If a conflict is found, an error is displayed and saving is blocked.

#### Auto-applied Changes on Save

- The GameObject name is updated to match the zone name
- The asset file is renamed to match the zone name
- If the shape type was changed, the polygon resets to the new default shape

### Export Section

At the bottom of the editor window, expand Export Zones to JSON to export all scene zones to a JSON file for runtime loading.

## Shape Editing

When a zone is in edit mode, yellow handles appear in the Scene view for each polygon vertex.

### Controls

Action | Input | Description
--- | --- | ---
Move vertex | Drag handle | Drag any yellow handle to reposition a vertex
Insert vertex | Ctrl + Click edge | Adds a new vertex on the closest edge (cyan highlight shows target)
Delete vertex | Shift + Click vertex | Removes the vertex (minimum 3 vertices). Handles turn red while Shift is held.
Stop editing | Esc | Exits shape edit mode

### Shapes

Shape | Default | Notes
--- | --- | ---
`Square` | 4 vertices, 2-unit half-size | Can be reshaped into any quad
`Circle` | 24-segment approximation, radius 2 | Drag the radius handle to resize. Regenerates vertices on radius change.
`Polygon` | 12 vertices, radius 3 | Fully freeform — add, remove, and drag vertices freely

### Additional Tools (Inspector)

- Center Transform: Moves the GameObject’s transform to the polygon’s centroid without changing the zone’s world position
- Reset Shape: Resets the polygon to the default for the current shape type
- Duplicate Zone: Creates an independent copy with its own `ZoneData` asset

Scene interaction: While in shape edit mode, clicking in the Scene view will not select other objects. The default transform handle is hidden to prevent accidental movement.

## Zone Roles & Resolution

### Roles

Role | Purpose | Fields
--- | --- | ---
Base | Defines the encounter table and baseline difficulty | Encounter Table ID, Difficulty Tier, Encounter Chance
Modifier | Stacks multiplicatively on top of a Base zone | Chance Multiplier, Difficulty Tier Bonus
Override | Replaces everything — towns, story events, safe areas | Is Safe Zone, Encounter Table ID, Encounter Chance, Difficulty Tier

### Resolution Order

When querying a world position, the `ZoneResolver` follows this order:

1. If any Override zone is present → use the highest-priority Override exclusively
2. Find the highest-priority Base zone → encounter table + baseline difficulty
3. Stack all Modifier zones multiplicatively on top
4. Clamp and return a `ZoneContext`

### Modifier Stacking

Modifiers are multiplicative, so each one is independent:

    Base chance 0.30  x  Cursed Road 1.8  x  Night Modifier 1.2  =  0.648

### Priority

Higher priority values take precedence. When multiple zones of the same role overlap, the highest-priority one wins (for Base and Override) or all are stacked (for Modifier).

## Editor Settings

Access via Window → Zone System → Settings. If no settings asset exists, one is created automatically.

### Fields

Field | Description | Default
--- | --- | ---
`mapPlane` | Which two world axes your map lies on | `XZ`
`zoneDataFolder` | Folder path where new ZoneData assets are saved | `Assets/ZoneData`
`roleColors` | Debug color for each zone role (used in scene rendering) | Blue (Base), Yellow (Modifier), Green (Override)

### Role Colors

Each `ZoneRole` has a configurable color in the settings. When you change a zone’s role in the editor, its debug color is automatically updated to match. Colors are used for:

- Scene view polygon fill and border
- Zone list row background tinting
- Inspector summary background
- Scene gizmos

Dynamic: If you add new values to the `ZoneRole` enum, call `SyncRoleEntries()` on the settings asset or click the settings menu item — missing roles will be added with a default gray color.

## Runtime API

### ZoneSystemApi

The main entry point for runtime zone queries.

    // Create the API with a reference to the ZonesObjectHolder
    ZoneSystemApi api = new ZoneSystemApi(zonesObjectHolder);

    // Full zone resolution at a world position
    ZoneContext ctx = api.QueryZone(partyWorldPosition);

    if(!ctx.isSafe && Random.value < ctx.finalEncounterChance)
        TriggerEncounter(ctx.encounterTableId, ctx.finalDifficultyTier);

### Methods

Method | Returns | Description
--- | --- | ---
`QueryZone(Vector3)` | `ZoneContext` | Full resolution: finds overlapping zones, applies modifiers, returns final context
`GetOverlappingZones(Vector3)` | `List<ZoneData>` | Raw list of all zones containing the position, sorted by descending priority
`IsInSafeZone(Vector3)` | `bool` | Quick check — true if any Override zone with `isSafeZone` contains the position
`Register(ZoneInstance)` | `void` | Register a dynamically spawned zone
`Unregister(ZoneInstance)` | `void` | Unregister a zone before destroying it

### ZoneContext Struct

Field | Type | Description
--- | --- | ---
`encounterTableId` | `string` | ID of the encounter table to use
`finalEncounterChance` | `float` | Final encounter probability (0–1), after modifier stacking
`finalDifficultyTier` | `DifficultyTier` | Final difficulty tier, after modifier bonuses
`isSafe` | `bool` | True if in a safe zone (no encounters)
`resolvedZoneName` | `string` | Name of the zone that “won” resolution (for debug/UI)

### Dynamic Zones

    // After instantiating a zone at runtime:
    api.Register(zoneInstance);

    // Before destroying:
    api.Unregister(zoneInstance);

## Type Reference

### Enums

#### ZoneRole

Value | Description
--- | ---
`Base` | Provides the encounter table and baseline difficulty
`Modifier` | Mutates difficulty/chance on top of a Base zone
`Override` | Completely replaces everything (safe towns, story events)

#### ZoneShape

Value | Description
--- | ---
`Square` | 4-vertex quadrilateral
`Circle` | 24-segment circular approximation with adjustable radius
`Polygon` | Freeform polygon with 12 default vertices

#### DifficultyTier

Value | Int
--- | ---
`Safe` | 0
`Mild` | 1
`Moderate` | 2
`Dangerous` | 3
`Deadly` | 4

#### MapPlane

Value | Axes | Depth
--- | --- | ---
`XY` | X, Y | Z
`XZ` | X, Z | Y
`YZ` | Y, Z | X

### ScriptableObjects

#### ZoneData

Per-zone configuration asset. Created via the Zone Editor or `Create → ZoneSystem → Zone Data`.

Field | Type | Description
--- | --- | ---
`zoneId` | `string` | Unique identifier
`zoneName` | `string` | Display name
`role` | `ZoneRole` | Base, Modifier, or Override
`priority` | `int` | Higher wins in same-role conflicts
`debugColor` | `Color` | Scene visualization color (auto-set from role)
`shape` | `ZoneShape` | Shape type
`circleRadius` | `float` | Radius (Circle shape only)
`polygon` | `List<Vector2>` | Vertex positions (local to transform)

### MonoBehaviours

#### ZoneInstance

Placed on a scene GameObject. References a `ZoneData` asset and provides spatial queries.

Field / Method | Description
--- | ---
`data` | Reference to the `ZoneData` asset
`Contains(Vector3, MapPlane)` | Returns true if the world position is inside this zone
`RebuildBoundsCache()` | Recalculates the AABB cache (call after modifying polygon)

#### ZonesObjectHolder

Scene manager that holds the map plane setting and provides access to all zones.

Field / Property | Description
--- | ---
`mapPlane` | Which plane the map lies on (XY, XZ, or YZ)
`AllZones` | Read-only list of all `ZoneInstance` objects in the scene

## Utility Classes

### PolygonUtils

Static math utilities for polygon operations.

Method | Description
--- | ---
`PointInPolygon(Vector2, List<Vector2>)` | Ray-casting point-in-polygon test (Jordan curve theorem)
`PointInPolygon(Vector3, List<Vector2>, MapPlane)` | Projects world position to plane, then tests
`Centroid(List<Vector2>)` | Average center of polygon vertices
`Bounds(List<Vector2>)` | Axis-aligned bounding box (min, max)
`PointInBounds(Vector2, Vector2, Vector2)` | Fast AABB pre-check
`Triangulate(List<Vector2>)` | Ear-clipping triangulation for concave polygon rendering

### MapPlaneUtility

Converts between 3D world positions and 2D plane coordinates.

Method | Description
--- | ---
`ProjectToPlane(Vector3, MapPlane)` | 3D world → 2D plane coordinates
`UnprojectFromPlane(Vector2, MapPlane, float)` | 2D plane coordinates → 3D world

### ShapeFactory

Generates default polygon vertices for each shape type.

Method | Description
--- | ---
`CreateDefault(ZoneShape)` | Returns default vertices for the given shape
`CreateSquare(float)` | 4-vertex square with given half-size
`CreateCircle(float, int)` | N-segment circle approximation
`CreatePolygon(float, int)` | Regular polygon with N vertices
`RegenerateCircle(ZoneData)` | Rebuilds circle vertices from current radius

### ZoneResolver

Pure logic for resolving overlapping zones into a single `ZoneContext`.

Method | Description
--- | ---
`Resolve(List<ZoneData>)` | Takes overlapping zone data, applies role priority and modifier stacking, returns `ZoneContext`

### ZoneExporter

Serializes scene zones to a JSON structure for runtime loading.

Method | Description
--- | ---
`BuildExport(ZoneInstance[], MapPlane)` | Builds the export data structure from scene instances
`ToJson(ZoneExportRoot, bool)` | Converts to JSON string (optionally pretty-printed)

## JSON Export

In the Zone Editor window, expand Export Zones to JSON, set the output path, and click Export Now.

### Loading at Runtime

    string json = File.ReadAllText(Application.streamingAssetsPath + "/zones.json");
    ZoneExportRoot root = JsonUtility.FromJson<ZoneExportRoot>(json);

### Export Structure

Each zone is exported as a `ZoneExportEntry` containing all zone data fields plus the world-space polygon coordinates and transform position.

## Keyboard Shortcuts

Key | Context | Action
--- | --- | ---
Esc | Scene view, shape editing active | Stop editing the zone shape
Ctrl + Click | Scene view, shape editing active | Insert a vertex on the nearest edge
Shift + Click | Scene view, shape editing active | Delete the clicked vertex (min 3)

## Menu Reference

Menu Path | Description
--- | ---
Window → Zone System → Zone Editor | Opens the main Zone Editor window
Window → Zone System → Settings | Selects (or creates) the ZoneEditorSettings asset
Window → Zone System → Documentation | Opens this documentation in your default browser
Jovian → ZoneSystem → Zone Editor Settings | Create menu for new ZoneEditorSettings asset
ZoneSystem → Zone Data | Create menu for new ZoneData asset

Jovian Zone System v0.1.0 — com.jovian.zonesystem