Skip to content

Grid Placement v6.3.0

Refund on Demolish

How object demolish refunds work through BuildCost, RefundService, and RefundCalculator.

Status
Current
Version
v6.3.0
Source updated
2026-10-29
Generated on
2026-10-29

6.3. These pages document the 6.3 release, where placeable is now entry (what changed) and PlacementOwner is deprecated (migration). Still on 6.2? Your version's guides ship inside the plugin itself, under addons/grid_placement/guides/; the 6.2 URLs on this site now redirect to this series.

Grid Placement can refund some or all of an object's or terrain entry's build cost when it is demolished. Your game owns the inventory; the plugin sends it the item id and amount to add.

For objects, set the cost through SpendMaterialsRuleById. For terrain, set TilePlacementEntry.build_cost. Terrain demolition shares the refunder pipeline; custom refunders receive a null object target for terrain.

Data flow

SpendMaterialsRuleById defines cost
→ BuildCost resolves that cost for the placed object
→ demolish reaches the pre-demolish/refund path
→ RefundCalculator applies refund_ratio
→ RefundService sends id + amount to your inventory bridge

Use one cost source for both spending and refunds so those values cannot drift. For terrain, TilePlacementEntry.build_cost supplies the cost instead. A brush demolish multiplies that cost by the number of erased cells.

Main types

Type Purpose
SpendMaterialsRuleById Defines id-keyed object build costs.
TilePlacementEntry Defines an optional terrain demolish cost.
BuildCost Stores the item ids and quantities to refund.
RefundCalculator Calculates refunded id→amount values.
RefundService Applies refunds through the configured inventory bridge.
Refunder Optional pre-demolish hook that can inspect/veto demolition.

The current public service name is RefundService; old RefundSystem wording is legacy.

Inventory bridge

For refunds, the inventory bridge must implement try_add_by_id. When the same inventory also handles placement costs, it must also implement get_count_by_id and try_remove_by_id:

func get_count_by_id(id:StringName)-> int:
    return GameInventory.get_item_count(id)

func try_remove_by_id(id:StringName, amount:int)-> int:
    return GameInventory.remove_items(id, amount)

func try_add_by_id(id:StringName, amount:int)-> int:
    return GameInventory.add_items(id, amount)

Return the amount actually added or removed. RefundService.refund() returns the amount the inventory accepted, so a capacity limit is visible to callers.

Configure refunds

In the standard host setup, GridPlacementHost creates and registers one RefundService when manipulation is enabled. After the host configures its session, set the service's ratio and a locator that finds the inventory under PlacementOwner.owner_root:

func configure_refunds(host:GridPlacementHost)-> void:
    var refund_service:RefundService = host.get_refund_service()
    if refund_service== null:
        push_error("Refunds require a configured manipulation service")
        return

    refund_service.refund_ratio= 0.5
    refund_service.locator= NodeLocator.new(
        NodeLocator.SEARCH_METHOD.NODE_NAME,
        "Inventory",
    )

Call this after the host has configured its session. The example returns half the cost; the default ratio is already 0.5. Use a NodeLocator for split-screen: the host rebinds the service to each session, while a direct container_override is cleared when the session changes.

Example policy:

build cost: 100 wood
refund_ratio: 0.5
refund: 50 wood

Do not attach an independent refund service to every building; multiple handlers can refund the same demolition more than once.

Use RefundCalculator directly only when your game intentionally owns a custom demolish pipeline. Use a custom Refunder when demolition itself needs a veto/inspection step.

Common mistakes

  • Spend and refund ids differ (&"Wood" vs &"wood").
  • Several handlers refund the same object.
  • The inventory bridge cannot be resolved from the active placement owner/configuration.
  • A custom runtime discount changes the paid cost but the placed object's resolved BuildCost is not updated.
  • A custom refunder assumes the demolished target is non-null; terrain demolition passes null.

Using this in a game?

Grid Placement v6.3.0 — full source, playable demos, commercial license

The guide above is the same setup the shipped demos use. Try the build in your browser before you buy it.