# Coordinate Mapping

## Metadata

| Field | Value |
|---|---|
| Status | Implemented |
| Target/work package | `M01-FND-COORDS-001`, `M01-FND-COORD-TILE-AXIS-002`, `M01-QAR-COORD-FIXTURES-001`, `M01-FND-COORD-BOUNDARY-GATE-001`, `M01-QAR-SERVER-SPAWN-RENDERER-001`, `M02-GMP-TERRAIN-QUERY-001` consumer migration |
| Owners | Foundation/domain |
| Last verified | `a45d521`, 2026-07-14 |
| Profiles/capabilities | All profiles; contract version 2 |

## Purpose

Provide scene-free value objects and one pure `CoordinateMapper` for positions,
ADT grid ownership and world-facing yaw. The module makes every source space
explicit before renderer, network, gameplay and authoring consumers migrate.

## Non-goals

- Reading ADT, packet, SQL or scene data.
- Constructing `Vector3`, `Transform3D`, Node or Resource values.
- Correcting M2/WMO local axes or MDDF/MODF Euler rotations.
- Selecting maps, validating terrain height or owning streaming focus.
- Persisting coordinate values in this work package.

## Context and boundaries

```mermaid
flowchart LR
    Server[Server / movement X,Y,Z] --> ServerPosition[ServerWorldPosition]
    ADT[MDDF/MODF X,height,Z] --> AdtPosition[AdtPlacementPosition]
    ServerPosition --> Mapper[CoordinateMapper]
    AdtPosition --> Mapper
    Mapper --> Canonical[CanonicalWowWorldPosition]
    Canonical --> Tile[AdtTileCoordinate + AdtTileLocalPosition]
    Canonical --> GodotPosition[GodotWorldPosition]
    GodotPosition --> Adapter[Future scene/render adapter]
    Adapter --> Vector[Vector3 at boundary]
    Scene[Node / Camera3D / scene lookup] -. forbidden .-> Mapper
```

Allowed dependencies:

- GDScript scalar math and `RefCounted` lifetime;
- other value objects in `src/domain/coordinates`;
- callers in network, gameplay, render and server-data adapters.

Forbidden dependencies:

- `Node`, `Resource`, SceneTree, Camera3D and global scene lookup;
- packet buffers, SQL rows, asset parsers and renderer state;
- `Vector3` as a public mapper input or output;
- model-specific placement corrections.

## Public API

| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `CanonicalWowWorldPosition` | Immutable value | Client/server X/Y/Z in yards | Any thread; caller-owned reference | No bounds validation |
| `ServerWorldPosition` | Immutable value | Server/wire X/Y/Z in yards | Any thread; caller-owned reference | No adapter validation |
| `AdtPlacementPosition` | Immutable value | MDDF/MODF X/height/Z fields | Any thread; caller-owned reference | No file validation |
| `GodotWorldPosition` | Immutable value | Godot east/up/south scalars | Any thread; caller-owned reference | Does not create `Vector3` |
| `AdtTileCoordinate` | Immutable value | `Map_tileX_tileY.adt` indices | Any thread; caller-owned reference | `is_within_map_grid()` reports invalid indices |
| `AdtTileLocalPosition` | Immutable value | East/height/south offsets from tile north-west corner | Any thread; caller-owned reference | Values are intentionally unclamped |
| `AdtChunkCoordinate` | Immutable value | MCNK X-east/Y-south indices inside one tile | Any thread; caller-owned reference | `is_within_map_grid()` reports invalid indices |
| `CanonicalWowWorldYaw` | Immutable value | Canonical world yaw in radians | Any thread; caller-owned reference | Constructor does not normalize |
| `ServerWorldYaw` | Immutable value | Movement orientation in radians | Any thread; caller-owned reference | Constructor does not normalize |
| `GodotWorldYaw` | Immutable value | Godot Y-axis world yaw in radians | Any thread; caller-owned reference | Constructor does not normalize |
| `CoordinateMapper.server_to_canonical` / inverse | Pure static methods | Preserve server X/Y/Z as canonical | Synchronous; new result value | Null input is a caller contract violation |
| `CoordinateMapper.canonical_to_godot` / inverse | Pure static methods | Apply Y-up renderer basis and map offset | Synchronous; new result value | Non-finite scalars propagate |
| `CoordinateMapper.godot_to_adt_tile` | Pure static method | Resolve filename indices from typed renderer position | Synchronous; new result value | Result remains unclamped |
| `CoordinateMapper.godot_to_adt_tile_local` | Pure static method | Resolve east/height/south offsets from typed renderer position | Synchronous; new result value | Caller validates ownership/range |
| `CoordinateMapper.adt_tile_local_to_godot` | Pure static method | Compose typed renderer position from tile/local values | Synchronous; new result value | Non-finite scalars propagate |
| `CoordinateMapper.canonical_to_adt_placement` / inverse | Pure static methods | Convert ADT placement field basis | Synchronous; new result value | Non-finite scalars propagate |
| `CoordinateMapper.canonical_to_adt_tile` | Pure static method | Compute unclamped filename indices | Synchronous; new result value | Out-of-map result remains invalid |
| `CoordinateMapper.canonical_to_adt_tile_local` / inverse | Pure static methods | Split/reconstruct tile-local position | Synchronous; new result value | Caller validates tile ownership |
| `CoordinateMapper.adt_tile_local_to_chunk` | Pure static method | Compute unclamped MCNK indices | Synchronous; new result value | Boundary/out-of-range remains diagnosable |
| World-yaw mapper methods | Pure static methods | Normalize and cross the world-basis boundary | Synchronous; new result value | Non-finite input is unsupported |

## Inputs and outputs

| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | `ServerWorldPosition`, `ServerWorldYaw` | Future protocol/server adapter | `CoordinateMapper` | Immutable caller value | Any thread, call duration |
| Input | `AdtPlacementPosition` | Future ADT adapter | `CoordinateMapper` | Immutable caller value | Any thread, call duration |
| Input | `CanonicalWowWorldPosition`, `CanonicalWowWorldYaw` | Gameplay/content/domain | `CoordinateMapper` | Immutable caller value | Any thread, call duration |
| Output | `CanonicalWowWorldPosition` | `CoordinateMapper` | Gameplay/content/adapters | New immutable value | Caller-owned reference |
| Output | `AdtTileCoordinate`, `AdtTileLocalPosition`, `AdtChunkCoordinate` | `CoordinateMapper` | Streaming/content/terrain-query adapters | New immutable values | Caller-owned references |
| Output | `GodotWorldPosition`, `GodotWorldYaw` | `CoordinateMapper` | Future render/scene adapter | New immutable values | Caller-owned references |
| Test input | `coordinate_golden_points.json` schema 1 | Pinned public server row plus private numeric observations | Headless fixture verifier | Versioned repository fixture | Test-process lifetime |

Side effects:

- none;
- no scene mutation, file/cache write, network send, database access or logging.

## Data flow

```mermaid
flowchart LR
    ServerFixture[Pinned AzerothCore spawn] --> FixtureVerifier[Golden fixture verifier]
    RawADTFixture[Raw ADT numeric metadata] --> FixtureVerifier
    ClientFixture[Build 12340 observations] --> FixtureVerifier
    FixtureVerifier --> Mapper
    ServerFixture --> SpawnManifest[Server spawn render manifest]
    SpawnManifest --> SpawnVerifier[Renderer checkpoint verifier]
    SpawnVerifier --> Capture[Live renderer capture]
    Capture --> Marker[Diagnostic marker at mapped spawn]
    Server[ServerWorldPosition X,Y,Z] -->|identity components| Canonical[CanonicalWowWorldPosition]
    ADT[AdtPlacementPosition X,height,Z] -->|half extent - horizontal| Canonical
    Canonical -->|tileX from Y; tileY from X| Tile[AdtTileCoordinate]
    Canonical -->|north-west edge offsets| Local[AdtTileLocalPosition]
    Local -->|floor offset / chunk size| Chunk[AdtChunkCoordinate]
    Canonical -->|half extent - Y; Z; half extent - X| Godot[GodotWorldPosition]
    Godot -->|typed direct adapter| Tile
    Godot -->|typed direct adapter| Local
    Godot --> TerrainQuery[AdtTerrainQuery consumer]
    Godot --> TargetPlanner[StreamingTargetPlanner consumer]
    TerrainQuery --> Tile
    TerrainQuery --> Local
    TerrainQuery --> Chunk
    TargetPlanner --> Tile
    TargetPlanner --> Local
    Tile -->|tile plus local| Godot
    ServerYaw[ServerWorldYaw] -->|normalize only| CanonicalYaw[CanonicalWowWorldYaw]
    CanonicalYaw -->|same numeric world angle| GodotYaw[GodotWorldYaw]
```

## Lifecycle/state

The module is stateless. Each call reads immutable scalar inputs and returns a
new value object. There is no initialization, shutdown, cancellation or retry
state machine.

## Main sequence

No cross-layer asynchronous sequence occurs inside this module. Producers parse
or validate their external format first, call the mapper synchronously, and
then pass only typed results across the next boundary.

## Ownership, threading and resources

- Value objects own only scalar fields and, for chunks, one immutable tile reference.
- Callers own returned `RefCounted` references.
- Methods do not mutate inputs or shared state and are safe for worker use.
- No Node, RID, file, socket, database or cache resource is acquired.

## Errors, cancellation and recovery

| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Tile/chunk outside standard grid | `is_within_map_grid()` | Unclamped indices are preserved | Caller reports source and map context | Reject or apply explicit map policy |
| Tile-local value outside half-open tile | Consumer boundary validation | Value is preserved | Caller reports source coordinate | Recompute ownership or reject malformed input |
| Null value object | Static typing/call failure | Programming contract violation | GDScript error with call site | Fix producer; no fallback value |
| NaN/infinite scalar | Adapter validation | Arithmetic propagates invalid value | Adapter must report source field | Reject malformed external input |

There is no cancellation because every conversion is constant-time scalar math.

## Configuration and capabilities

| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Contract version | 2 | All | No | Corrected filename/chunk axes in ADR 0001 |
| Godot units per WoW yard | 1 | `Blizzlike335` and current tools | No | Preserves current renderer scale |
| ADT grid | 64 tiles/axis, 16 chunks/tile | WotLK 3.3.5a | No | Defines index and boundary math |

## Persistence, cache and migration

Production code adds no persisted format, cache or migration. Test schema 1 in
`coordinate_golden_points.json` persists explicit source provenance, coordinate
contract version 2, tolerances, source-space values and expected outputs. It
contains no proprietary bytes, database dump or original-client image. Existing
dictionaries, native `Vector3` payloads and renderer conversion helpers remain compatible.
Consumer migration is ordered after this contract and must remove old manual
conversions only after its producer/consumer tests pass.

Any future serialized coordinate must store a schema version and explicit space;
raw three-number arrays are not an accepted new persistence contract.

## Diagnostics and observability

- Logs: none in production mapper.
- Metrics: none; conversion cost is constant and outside frame-budget concern.
- Debug views: headless contract probe prints one PASS summary.
- Correlation IDs: supplied by external adapters if an invalid source is reported.

## Verification

- Unit/contract tests: `src/tools/verify_coordinate_mapper.gd`.
- Cross-source golden test: `src/tools/verify_coordinate_golden_fixtures.gd`.
- Boundary enforcement: `src/tools/verify_coordinate_conversion_boundaries.gd`.
- Server-spawn renderer contract: `src/tools/verify_server_spawn_renderer.gd`
  validates the dedicated manifest against the same pinned fixture before GUI capture.
- Integration/E2E: sky, player spawn, streamer, terrain probe and the extracted
  `AdtTerrainQuery` use mapper boundaries;
  `StreamingFocus` provides typed renderer focus, and the pinned AzerothCore
  Human Warrior spawn is captured in the Eastern Kingdoms renderer at ADT
  `(32,48)`, chunk `(3,12)`.
- Fidelity evidence: five build-12340 camera positions from the M00 manifest map
  within `0.002` yard; a pinned AzerothCore human-warrior spawn and one raw MCNK
  header observation independently confirm tile `(32,48)` and chunk `(3,12)`;
  independent reference formulas are recorded in ADR 0001.
- Performance budgets: constant-time arithmetic; no scene or file access.
- Manual diagnostics: not required for the pure contract; renderer checkpoints
  are required when consumers migrate.

## Extension points

- Add typed map IDs around positions without changing axis semantics.
- Add a format-specific placement-orientation adapter with its own fixtures.
- Add serialization only with explicit space/version fields and migrations.
- Add consumer adapters that construct engine-native vectors at their boundary.

## Capability status

| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Canonical/server/Godot position mapping | Implemented | Headless round-trip, five M00 golden points and migrated GDScript consumers | Network/server adapters remain future consumers |
| ADT placement mapping | Implemented | Synthetic/direct round-trip fixtures | Compare native parsed placements during migration |
| ADT tile/local/chunk ownership | Implemented | Center/extent boundaries, raw `Azeroth_31_49.adt` evidence and direct Godot adapter round-trip | Native parser remains explicit boundary |
| World-facing yaw mapping | Implemented | Cardinal/wrap round-trip fixtures | Placement Euler remains separate |
| Scene-free production contract | Implemented | Source boundary, headless tests and repository-wide conversion gate | Maintain gate with new consumers |
| Cross-source coordinate fixture | Implemented | Pinned AzerothCore spawn, raw ADT MCNK metadata and five build-12340 points pass one headless verifier | Add live adapter/renderer integration |
| TrinityCore populated spawn equivalence | Not verified | The base development schema has no pinned populated row used by this fixture | Pin and verify a TDB snapshot before claiming parity |
| Visual/server-spawn integration | Implemented | Dedicated manifest/verifier plus cold/warm GUI capture of the mapped AzerothCore spawn | This is coordinate/renderer integration evidence, not original-client visual parity |

## Known gaps and risks

- Native ADT/WDT parsing retains one shared boundary formula because worker
  parsers cannot call GDScript; the gate prevents additional definitions.
- Contract version 1 briefly transposed ADT filename/chunk axes; version 2 fixes
  the defect before any consumer migration.
- MDDF/MODF rotation comments and implementations disagree across existing paths;
  this package deliberately does not generalize them into world yaw.
- Finite-number validation belongs to external format adapters and is not yet implemented.
- Arithmetic agreement does not prove terrain, placement, camera direction or visual parity.
- The golden server row is verified against pinned AzerothCore data only; no
  TrinityCore equivalence claim is made without a pinned populated TDB snapshot.

## Source map

| Path | Responsibility |
|---|---|
| `src/domain/coordinates/coordinate_mapper.gd` | Constants and all coordinate-axis conversions |
| `src/domain/coordinates/*_position.gd` | Immutable typed position values |
| `src/domain/coordinates/adt_tile_coordinate.gd` | ADT filename tile indices and bounds |
| `src/domain/coordinates/adt_chunk_coordinate.gd` | MCNK indices and bounds |
| `src/domain/coordinates/*_yaw.gd` | Immutable typed world-facing yaw values |
| `src/tools/verify_coordinate_mapper.gd` | Headless fixtures and boundary regressions |
| `src/tests/fixtures/coordinate_golden_points.json` | Versioned cross-source values, provenance and tolerances |
| `src/tools/verify_coordinate_golden_fixtures.gd` | Cross-source fixture schema and mapper validation |
| `src/tools/verify_coordinate_conversion_boundaries.gd` | Repository-wide manual conversion and required-consumer gate |
| `src/gameplay/terrain/adt_terrain_query.gd` | Typed Godot-position consumer for ADT tile/local/chunk lookup |
| `src/render/streaming/streaming_target_planner.gd` | Typed Godot-position consumer for ADT focus tile and boundary-local lookup |
| `src/tools/server_spawn_render_manifest.json` | Pinned server-spawn camera, player, marker and expected ADT ownership |
| `src/tools/verify_server_spawn_renderer.gd` | Fixture-to-renderer manifest contract verifier |
| `docs/adr/0001-canonical-world-coordinates.md` | Normative axes, units, boundaries and rollout decision |

## Related decisions and references

- ADR: [`../adr/0001-canonical-world-coordinates.md`](../adr/0001-canonical-world-coordinates.md)
- M00 evidence: [`../M00_FINAL_PAIRED_EVIDENCE.md`](../M00_FINAL_PAIRED_EVIDENCE.md)
- Architecture: [`../ARCHITECTURE.md`](../ARCHITECTURE.md)
- Testing rules: [`../TESTING.md`](../TESTING.md)
