Read-only telemetry surface for placement/manipulation state. Centralizes "how do I read placement state for a test assertion?" into one place so tests and diagnostics do not reach into private fields or re-derive map math. Each reader returns an immutable snapshot struct; tests assert against the snapshot fields. The readers are null-safe: pass whatever you have (session and/or host) and the snapshot fields default to "empty" values when the underlying state is missing — so a test can distinguish "no manipulation active" from "rotation is zero" by checking ManipulationSnapshot.is_active. Usage: var snap := PlacementTelemetry.read_manipulation(session) assert_int(snap.cardinal).is_equal(PlacementGridRotationUtils.CardinalDirection.EAST) var action := PlacementTelemetry.read_placement_action(session) assert_bool(action.last_report_success).is_true() Regressions in placement state used to slip through because tests could not easily observe it. This class closes that gap.
Source: addons/grid_placement/telemetry/placement_telemetry.gd
Syntax
class PlacementTelemetry extends RefCountedMembers
| Name | Kind | Summary |
|---|---|---|
DETAIL_NAMES | Field | Detail controls evidence production independently from PlacementLogger severity. SUMMARY is the default useful terminal signal; TRACE remains opt-in. |
level | Property | |
allows | Method | |
set_from_debug_settings | Method | |
set_level | Method | |
MAX_RECORDS | Field | |
policy | Property | |
record | Method | |
record_preview | Method | |
record_decision | Method | |
normalized | Property | |
signature | Property | |
recorded | Property | |
record_dispatch | Method | |
preview_count | Method | |
dropped_count | Method | |
evicted_count | Method | |
stats | Method | |
records | Method | |
latest | Method | |
latest_json | Method | |
clear | Method | |
snapshot_preview | Method | |
evidence | Property | |
snapshot | Property | |
snapshot_decision | Method | |
snapshot | Property | |
snapshot_dispatch | Method | |
is_active | Property | Snapshot of the active manipulation transform (rotation, flip, target). Read via read_manipulation(). True when a ManipulationParent is wired and a target is under manipulation. |
rotation_degrees | Property | Logical rotation in degrees (sprite-swap aware). 0.0 when inactive. |
cardinal | Property | Nearest cardinal facing derived from rotation_degrees. |
flipped_horizontally | Property | Horizontal mirror state (negative scale.x). |
flipped_vertically | Property | Vertical mirror state (negative scale.y). |
active_target | Property | The root node being manipulated, or null. |
to_dictionary | Method | |
mode | Property | Snapshot of placement/build state for the current selection + last action. Read via read_placement_action(). Current placement mode (OFF/INFO/MOVE/DEMOLISH/PLACE). |
has_entry | Property | True when a scene_entry is selected for building. |
selected_entry_id | Property | The selected scene_entry's id, or an empty StringName. |
has_terrain | Property | True when a terrain is selected for painting. |
selected_terrain_name | Property | The selected terrain's name, or an empty StringName. |
has_preview | Property | True when a placement preview instance currently exists. |
last_dispatch | Property | The last host dispatch result, or null when no action has dispatched. This is the canonical "what just happened" surface — same object the host tests assert against via GridPlacementHost.get_last_dispatch_result(). |
last_action_success | Property | Convenience: True when last_dispatch exists and reports success. |
last_action_issues | Property | Convenience: issues from last_dispatch, empty when no dispatch. |
to_dictionary | Method | |
mode | Property | Snapshot of targeting/positioner state (current cell, map, manual targeting). Read via read_targeting(). Current placement mode. |
positioner_cell | Property | The 2D cell under the positioner, or Vector2i.ZERO when unresolved. |
positioner_cell_3d | Property | The 3D cell under the positioner, or Vector3i.ZERO when unresolved. |
has_target_map | Property | True when a 2D target map is wired. |
target_map_name | Property | Name of the layer the next terrain paint lands on. Routing is sticky (TerrainLayerRouting falls back to the current map), so "painted somewhere else" is the common surprise: the name here is the answer, without a scan of every TileMapLayer in the scene. |
has_target_map_3d | Property | True when a 3D target map is wired. Lets tests distinguish "3D unwired" from "wired but resolving ZERO" without touching state. |
has_positioner_3d | Property | True when the session has a live 3D positioner. |
pick_evidence_3d_requested | Property | True when this call requested detailed 3D pick evidence. |
has_pick_evidence_3d | Property | True when a requested cached 3D pick snapshot was captured. |
pick_evidence_3d | Property | Cached 3D pick evidence from GridPositioner3D; no second ray is cast. |
target_3d | Property | The 3D node the targeting state points at, or null. |
has_live_pick_ray_3d | Property | True after the 3D positioner publishes an actual mouse ray. |
last_click_missed_3d | Property | True when the latest 3D click explicitly missed the grid. |
is_manual_targeting | Property | True while a build preview / move copy is driving manual targeting. |
target | Property | The node the targeting state points at right now (hover target, preview, or move copy). Read it here instead of reaching into the session's state. |
cast_provided | Property | True when a shape cast was passed to read_targeting(); without one the hover fields below stay at their defaults and cannot be read as "nothing seen". |
cast_enabled | Property | True while the hover cast itself is enabled. |
cast_has_shape | Property | True when the passed cast carries a shape to query with. A shapeless cast reports zero collisions forever, which reads exactly like empty ground unless it is named. |
cast_cache_populated | Property | True when the hover cast's own collision cache holds a read. False means the hover fields came from a live query of a cast that has not run its physics update yet: off the physics thread that cast resolves nothing, so "sees nothing" is not the same claim. |
cast_position | Property | Where the hover cast actually is — the positioner drives it every physics frame, so a position assigned by a caller does not stick. |
hover_collision_count | Property | How many colliders the hover cast's last update reported. |
hover_collider | Property | The first collider the hover cast reported, or null. |
brush_shape | Property | Brush shape the next paint or drag will use, as the host reports it. -1 when no host was passed, so "unread" never looks like SINGLE. |
hover_blocker | Method | |
to_dictionary | Method | |
read_manipulation | Method | |
read_placement_action | Method | |
read_targeting | Method | |
snap | Property | |
targeting | Property | |
positioner_3d | Property | |
cached_cast | Property | |
cached_colliders | Property |