Grid Placement

GridPlacement 5.1 GDScript (Legacy)

This folder documents the legacy GDScript implementation of the GridPlacement plugin (historically shipped as the grid_building addon) used by Godot demos. The canonical implementation for new work is C# in GridPlacement 6.0+.


Goals and Scope

  • Legacy focus

    • 5.1 GDScript is feature-frozen; only low-risk bugfixes and stability work.
    • New gameplay and systems should target GridPlacement 6.0 C# instead.
  • Audience

    • People maintaining or debugging existing 5.1 GDScript projects/demos.
    • People migrating projects from 5.1 GDScript -> 6.0 C#.
  • Out of scope

    • New GDScript APIs or major redesigns.
    • Full re-port of all 6.0 features back into 5.1.

Where the GDScript Addon Lives

The 5.1-era addon is organized around the historical grid_building plugin, but the demo addon is now the canonical edit location and single source of truth for GDScript.

  • Runtime addon (canonical edit location)

    • Location: demos/grid_building_dev/godot/addons/grid_building
    • Contains the GDScript implementations of grid placement, targeting, and helpers used by the demo scenes.
    • This is where you should open files to get full Godot language-server feedback (parse errors, type hints, etc.).
    • There is no separate migration_backups/gdscript tree anymore; all 5.1 GDScript with class_name lives under this addon path in the demo project.
  • Inventory addon (canonical edit location)

    • Location: demos/grid_building_dev/godot/addons/grid_building_inventory
    • Provides the inventory factory (GridBuildingInventory) and ItemContainer/item scripts used by the demo.
    • Edits should be made here first, then synced back into the plugin repositories.
  • Mirrored plugin sources (sync targets, not edit roots)

    • GridPlacement: plugins/gameplay/GridPlacement/gdscript/grid_building
    • GridPlacementInventory: plugins/gameplay/GridPlacementInventory/gdscript/grid_building_inventory
    • These folders are kept in sync from the demo via scripts like scripts/sync_gp.sh (rsync from demo → plugins).
    • Do not make manual edits in the plugin copies; always change the demo addon first, then re-sync.
  • GDScript tests

    • Main test root: demos/grid_building_dev/godot/addons/grid_building/test/grid_building
    • Inventory tests: demos/grid_building_dev/godot/test/grid_placement_inventory
    • Tests are executed via the GodotToolkit TestOrchestrator (see testing docs below).
  • Source of truth vs C#

    • For behaviour, C# 6.0+ is still the canonical reference.
    • The 5.1 GDScript code remains a legacy reference implementation, but its authoring surface is the demo project addon.

Support and Maintenance Policy

  • What we will do

    • Fix critical crashes and severe logic bugs that block existing users.
    • Keep the demo project loading and running on supported Godot versions.
    • Maintain strong typing in runtime GDScript code (no loosening types for tests).
    • Maintain a minimal, reliable GdUnit test slice for high-value paths.
  • What we will not do

    • No new major systems or mode types in GDScript.
    • No large refactors whose only goal is style alignment.
    • No attempt to perfectly mirror every 6.0 C# feature in 5.1.

Testing Strategy (GDScript)

  • Runtime typing rules
    • Runtime GDScript must stay strongly typed (classes, members, arrays, signals).
    • If tests break due to typing, we fix the tests or harness, not the types.
    • This keeps runtime behaviour predictable and easier to migrate to C#.

Typed GDScript First (avoid duck typing)

  • Prefer precise, static types over duck-typed access.
  • Avoid runtime reflection/dynamic invocation:
    • call(), has_method(), get_method_list(), get_property_list()
  • Avoid duck-typed property access as a default pattern:
    • obj.get("name"), obj.set("name", value)
    • Use these only as a narrow exception when forced by a truly dynamic engine surface.
  • Prefer typed variables + direct member access:
    • var ctx: PlacementRuleContext = ...
    • ctx.target_map = map
  • Prefer shallow container shapes in authored code:
    • Arrays: Array[Vector2i], Array[PlaceableMetadata]
    • Dictionaries: Dictionary[StringName, int] or Dictionary[Vector2i, bool]
    • Avoid deep nesting (more than 1 level of Dictionary/Array) unless the type is wrapped behind a focused adapter.

Placement rules (5.1+): use PlacementRuleContext (not GridTargetingState)

In GridPlacement 5.1+, PlacementRule implementations are written against a data-only input called PlacementRuleContext.

  • PlacementRuleContext exists to keep rule evaluation:
    • Deterministic (no mutable engine state / no surprise side effects)
    • Unit-testable (you can construct a context and validate rules without a live targeting node)
    • Decoupled from the targeting implementation (TargetingState/TargetingService can evolve without breaking rule APIs)

What changed vs older guides

Older 5.0-era docs and examples sometimes describe rules reading directly from GridTargetingState.

For 5.1+:

  • Rules should not depend on GridTargetingState.
  • Rules should only read what they need from p_context.

Minimal rule template (5.1+)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
extends PlacementRule

@export var failed_message: String = "Blocked"

func validate_placement(p_context: PlacementRuleContext) -> RuleResult:
	if p_context == null:
		return RuleResult.build(self, ["No PlacementRuleContext provided"]) 

	# Common inputs:
	# - p_context.targeting        (GBTargetingSnapshot)
	# - p_context.placeable        (Placeable)
	# - p_context.rotation         (int)
	# - p_context.target_map       (TileMapLayer as Node)
	# - p_context.maps             (Array[Node])
	# - p_context.collision_exclusions (Array[Node])
	# - p_context.owner_root       (Node)

	# Example: require a target map
	if p_context.target_map == null:
		return RuleResult.build(self, [failed_message])

	return RuleResult.build(self, [])

Where PlacementRuleContext comes from

PlacementService provides a canonical builder:

  • PlacementService.build_rule_context_for_build(...)

That method gathers the relevant snapshot/map data and packages it into PlacementRuleContext so all rules see the same stable inputs.

  • Test selection

    • Keep a small set of high-value tests that:
      • Cover core placement flows.
      • Cover key inventory behaviours used by demos.
      • Exercise adapters and integration points (but not every internal detail).
    • Low-value or overly brittle tests are deleted or not ported.
  • Execution path

    • Tests run via GodotToolkit.TestOrchestrator using a GdUnit runner script.
    • Example (conceptual) flow:
      • Orchestrator starts Godot with the demo project.
      • GdUnit discovers and executes suites under the addons/grid_building/test tree.
      • Results surface back to the .NET test runner for CI.

Hang-safe test running rules (Godot / GdUnit)

  • Do not run automation through a login shell (avoid bash -lc).

  • Require an explicit Godot binary path:

    • Prefer GODOT_BIN=/absolute/path/to/godot.
    • Avoid PATH-probing scripts that may execute slow/interactive shell init.
  • Always run Godot-based test commands behind a timeout (hang guard):

    • Use timeout --foreground -k 5 <seconds> <command...>.
  • Prefer the repo runner scripts that already include a timeout guard (where available).

  • Boundary between Core and Godot tests

    • Core tests (C#) own behavioural rules: grid math, targeting semantics, placement validation, and state machines.
    • Godot GDScript tests are thin wiring smokes:
      • Ensure scenes load and services resolve.
      • Verify adapters call into the correct core services.
      • Avoid re-encoding full behaviour already covered by C# tests.

Naming & mapping convention

  • For each 5.1 GDScript file that declares a class_name, include a short docstring that:
    • Points to docs/6.0/Core/GRIDBUILDING_CLASS_MAPPING.md as the canonical C# mapping, and
    • Names the corresponding C# concept(s) where they exist (e.g. PlacementService, ServiceRegistry, CompositionContainer).
  • This keeps GDScript naming in sync with the C# architecture and makes future migration and test porting easier.

Docstring template (recommended):

1
2
3
4
5
6
7
8
9
## <Short summary>
##
## Canonical mapping:
## - docs/6.0/Core/GRIDBUILDING_CLASS_MAPPING.md
##
## C# equivalent:
## - GridBuilding.Core.<Namespace>.<Type>
## - GridBuilding.Godot.<Namespace>.<Type> (if applicable)
class_name ...

Production-Ready Criteria for 5.1 GDScript

For the 5.1 GDScript addon, “production-ready” means:

  • Canonical edit surface and plugin mirrors are in sync

    • Day-to-day edits happen in the demo project under demos/grid_building_dev/godot/addons/grid_building and demos/grid_building_dev/godot/addons/grid_building_inventory.
    • Plugin copies under plugins/gameplay/GridPlacement/gdscript/grid_building and plugins/gameplay/GridPlacementInventory/gdscript/grid_building_inventory are mirrors, updated via scripted sync (e.g. sync_gp.sh), not hand-edited.
  • Demo + addon load cleanly on supported Godot versions

    • Main demo scenes open and run without red errors.
    • Warnings from the addon are reduced to a small, understood set documented in HEALTH.md.
  • Curated GdUnit slice is green and runnable via orchestrator

    • A small, high-value set of GdUnit suites passes via GodotToolkit.TestOrchestrator.
    • Run commands and expected health are documented in docs/5.1/gdscript/HEALTH.md and test roadmaps.
  • Public 5.1 GDScript API surface is stable

    • External-facing scripts, nodes, and signals are treated as frozen for 5.1.
    • Changes are limited to critical bugfixes; breaking changes are avoided or clearly documented.
  • Path-first UID policy is respected in scripts

    • Authored code (runtime and tests) uses res:// preloads/loads where paths are stable.
    • Remaining uid:// references live only inside .tscn / .tres files managed by Godot.
  • Status is reflected in health and roadmap docs

    • HEALTH.md captures the current score, top risks, and recent changes.
    • Remaining 5.1 work is intentionally small, focused, and tracked in GDSCRIPT_5_1_REMAINING_WORK.md.

Migration to GridPlacement 6.0 C#

For new development and long-term maintenance, we recommend migrating to 6.0 C#.

  • High-level approach

    • Treat existing 5.1 GDScript as behavioural examples, not authoritative API.
    • Map concepts to 6.0 C# equivalents using the central migration docs under docs/grid-placement (class and service mappings, service registry, etc.).
  • Suggested migration steps

    • Identify the minimal runtime subset of GDScript your project truly uses.
    • Port those behaviours into C# core services first, with unit tests.
    • Replace GDScript wiring with C# components and service registry usage.
    • Leave the remaining GDScript addon as a reference-only artifact.

How This Folder Should Evolve

  • Capture notes and diagrams that clarify how 5.1 GDScript works today.
  • Record known limitations and intentional divergences from 6.0 C#.
  • Add focused docs per subsystem (placement, targeting, inventory) only when they help either:
    • Debug legacy behaviour, or
    • Migrate that behaviour into 6.0 C# cleanly.

When in doubt, prefer improving C# 6.0 docs and tests and keep 5.1 GDScript notes lightweight and maintenance-friendly.