Grid Placement

TargetSensor2D Design (6.0)

Purpose

Define a 6.0-friendly grid targeting sensor that:

  • Replaces the old TargetingShapeCast2D* nodes.
  • Stays as a thin Godot adapter over core targeting services.
  • Supports point-based cursor targeting now, with a clear path to area/shape-based detection later without changing the public contract.

Contracts

ITargetSensor (existing)

1
2
(CoreVector2I GridPosition, bool IsValid) GetTargetAt(Vector2 worldPosition);
Vector2 GetWorldPosition(CoreVector2I gridPosition);

Responsibilities:

  • Convert between world positions and grid coordinates.
  • Return a simple IsValid flag indicating whether a usable target exists at/near the given world position.

Non-responsibilities:

  • Owning core targeting rules (valid/invalid tiles, paths, costs).
  • Owning placement / manipulation workflows.

Those remain in core services (e.g. IGridTargetingService, IPlacementService, etc.).


Option C Summary (Chosen Approach)

  • Now: Implement TargetSensor2D as a point-based sensor.
    • World→grid conversion via CellSize + Origin.
    • (Later in Phase 2) A single-hit physics check (point or very short ray) using CollisionMask and simple filters.
  • Later: Introduce area-aware detection without changing ITargetSensor by:
    • Adding an internal detection mode (Point vs Area), or
    • Providing an alternative sensor implementation that still satisfies the same interface.

TargetingController2D and other consumers continue to depend only on ITargetSensor.


Phase 1 — Point-Based Sensor (Current)

Behaviour

  • GetTargetAt(worldPosition):
    • Map worldPosition to CoreVector2I using CellSize and Origin.
    • Return (gridPos, true) for now (every mapped tile is treated as potentially valid; core services will later refine this).
  • GetWorldPosition(gridPosition):
    • Map gridPosition to the tile center in world space.

Integration

  • TargetSensor2D registers itself into the global ServiceRegistry on _Ready as ITargetSensor.
  • TargetingController2D resolves ITargetSensor from the registry and uses it to:
    • Convert mouse position → grid coordinate.
    • Convert grid coordinate → world position for cursor snapping.

Test Boundary

  • Core tests (e.g. core targeting/placement tests) define the behaviour of valid/invalid tiles and paths.
  • Godot tests for targeting stay as thin wiring smokes:
    • Verify the controller resolves ITargetSensor.
    • Verify mouse hover drives state and world position through the sensor.
    • Do not re-encode full targeting/placement rules.

Phase 2 — Physics & Filtering (Planned)

When we need real “object under cursor” detection and collision masks, TargetSensor2D will grow a physics-backed implementation:

  1. Physics Query

    • Use either:
      • PhysicsDirectSpaceState2D.IntersectPoint or a short RayCast2D at the tile center for point mode, or
      • a ShapeCast2D/IntersectShape for area mode.
    • Respect exported settings:
      • CollisionMask (layers to include).
      • Optional MaxResults / MaxDistance.
  2. Candidate Selection

    • From hits, select the best candidate based on:
      • Distance to cursor/tile center.
      • Collision mask.
      • Optional metadata or component filters (e.g. manipulatable flag, specific script, or metadata key).
  3. Grid Mapping & Validation

    • Convert candidate hit position → CoreVector2I using CellSize and Origin.
    • Optionally consult core services (e.g. IGridTargetingService.ValidateTargetAtPosition(gridPos, node)).
    • Return (gridPos, isValid) to the controller, which then updates state and visual position using GetWorldPosition.

Throughout this phase, ITargetSensor and TargetingController2D stay unchanged; only the internal implementation strategy of the sensor evolves.


Future: Area / Shape-Based Tools

For multi-tile buildings, brushes, or AoE tools, a shape-aware variant can be introduced:

  • Either extend TargetSensor2D with an area detection mode that uses a small shape under/around the cursor, or
  • Introduce a dedicated ShapeTargetSensor2D that still implements ITargetSensor and is wired in via the registry in scenes that need richer detection.

This document intentionally does not specify those tools yet; it only ensures the current design does not block them.

Possible Future: Hit-Selection Helper & Core Tests

If we later introduce probes that can return multiple hits (e.g. Shape-based sensors or richer area tools), it may be useful to factor out a small hit-selection helper into a pure C# utility and test it in a core-style test project. Example responsibilities:

  • Choose the closest hit to a reference point (cursor/tile center).
  • Apply simple ordering or filtering rules before handing the result back to the sensor.

This is not required for the initial 6.0 rollout and can be added once multi-hit scenarios are formally defined.