Linked repository source
examples/logistics_post/project/main.gd
This read-only view comes from the file in the Gododin repository. It retains its design, example or evidence context.
Download the original file · Return to the guide
extends Node2D
## Logistics Post (DX-8): a compact depot-logistics loop. Godot owns the map
## presentation; the native LogisticsPost owns job assignment, routing,
## inventory, and the fixed simulation ticks. The scene imports the map once,
## submits jobs with stable ids, drives explicit ticks, and reads one bounded
## worker snapshot per render — no engine call or signal per agent per tick.
## The job_completed signal drives the completion log; deterministic replay is
## proven by verify_probe.gd against the GDScript oracle.
const TILE := 40.0
const MAP := [
"################",
"#S...R....R...D#",
"#.R..R....R.R.R#",
"#.R..RRRRRR.R.R#",
"#.R........R.R.#",
"#.RRRRRRR..R.R.#",
"#......R...R.R.#",
"#.RRRR.R.RRR.R.#",
"#....R.R.....R.#",
"#.RRRR.RRRRRRR.#",
"#S...R.......RD#",
"################",
]
const CELL_EMPTY := 0
const CELL_ROAD := 1
const CELL_SOURCE := 2
const CELL_DEST := 3
const WORKER_COLORS := [Color(0.35, 0.75, 1.0), Color(1.0, 0.75, 0.3), Color(0.55, 1.0, 0.45), Color(1.0, 0.5, 0.8)]
@onready var world: Node2D = $Map
@onready var worker_layer: Node2D = $Workers
var sim: Node = null
var paused := true
var tick_accum := 0.0
var selected := -1
var worker_nodes: Array[Node2D] = []
var source_cells: PackedInt32Array = []
var dest_cells: PackedInt32Array = []
var pending_source := -1
func _ready() -> void:
if not ClassDB.class_exists("LogisticsPost"):
push_error("LogisticsPost is not registered; run gododin build first.")
return
sim = $Sim
var settings: Resource = load("res://logistics_settings.tres")
_build_map()
var cells := _pack_cells()
var code: int = sim.call("configure", MAP[0].length(), MAP.size(), cells,
int(settings.worker_count), int(settings.seed))
if code != 0:
push_error("configure failed with %d" % code)
return
for source in source_cells:
for dest in dest_cells:
if source % 7 == dest % 5:
sim.call("submit_job", source, dest)
_spawn_worker_sprites(int(settings.worker_count))
%PauseButton.pressed.connect(func() -> void: paused = not paused)
%StepButton.pressed.connect(_do_tick)
%ResetButton.pressed.connect(_on_reset)
func _pack_cells() -> PackedByteArray:
var cells := PackedByteArray()
cells.resize(MAP[0].length() * MAP.size())
source_cells = PackedInt32Array()
dest_cells = PackedInt32Array()
for y in MAP.size():
for x in MAP[y].length():
var kind := CELL_EMPTY
match MAP[y][x]:
".": kind = CELL_ROAD
"R": kind = CELL_ROAD
"S":
kind = CELL_SOURCE
source_cells.append(y * MAP[0].length() + x)
"D":
kind = CELL_DEST
dest_cells.append(y * MAP[0].length() + x)
cells[y * MAP[0].length() + x] = kind
return cells
func _build_map() -> void:
for y in MAP.size():
for x in MAP[y].length():
var rect := ColorRect.new()
rect.position = Vector2(x * TILE, y * TILE)
rect.size = Vector2(TILE, TILE)
match MAP[y][x]:
"#": rect.color = Color(0.12, 0.13, 0.16)
"R": rect.color = Color(0.55, 0.5, 0.42)
"S": rect.color = Color(0.95, 0.75, 0.2)
"D": rect.color = Color(0.3, 0.85, 0.5)
_: rect.color = Color(0.07, 0.08, 0.1)
world.add_child(rect)
func _spawn_worker_sprites(worker_count: int) -> void:
for index in worker_count:
var dot := Node2D.new()
var body := ColorRect.new()
body.size = Vector2(TILE * 0.5, TILE * 0.5)
body.position = -body.size / 2
body.color = WORKER_COLORS[index % WORKER_COLORS.size()]
dot.add_child(body)
worker_layer.add_child(dot)
worker_nodes.append(dot)
func _process(delta: float) -> void:
if sim == null:
return
if not paused:
tick_accum += delta
while tick_accum >= 0.25:
tick_accum -= 0.25
_do_tick()
_present()
func _do_tick() -> void:
var delivered: int = sim.call("tick")
if delivered > 0:
var total: int = sim.call("get_deliveries")
feedback("delivery complete — %d total" % total)
func _present() -> void:
var worker_count: int = sim.call("get_worker_count")
var snapshot := PackedFloat32Array()
snapshot.resize(worker_count * 5)
var written: int = sim.call("worker_snapshot", snapshot)
if written < 0:
return
for index in worker_count:
var base := index * 5
var node := worker_nodes[index]
node.position = Vector2((snapshot[base+1] + 0.5) * TILE, (snapshot[base+2] + 0.5) * TILE)
var state := int(snapshot[base+3])
var job := int(snapshot[base+4])
node.modulate = Color(1, 1, 1) if state == 0 else (Color(1.6, 1.6, 1.2) if state == 1 else Color(1.2, 1.8, 1.2))
if index == selected:
node.scale = Vector2(1.35, 1.35)
else:
node.scale = Vector2.ONE
%StatsLabel.text = "tick %d · deliveries %d · jobs %d" % [
int(sim.call("get_ticks")), int(sim.call("get_deliveries")), int(sim.call("get_job_count"))]
if selected >= 0 and selected < worker_count:
var base := selected * 5
var job_state: int = sim.call("job_state", int(snapshot[base+4])) if snapshot[base+4] >= 0 else -1
%InspectLabel.text = "worker %d · state %d · job %d (%s)" % [
selected, int(snapshot[base+3]), int(snapshot[base+4]),
["pending", "assigned", "delivered"][job_state] if job_state >= 0 and job_state < 3 else "none"]
func _unhandled_input(event: InputEvent) -> void:
if sim == null or not event is InputEventMouseButton or not event.pressed:
return
if event.button_index != MOUSE_BUTTON_LEFT:
return
var world_mouse := get_global_mouse_position()
var cell := Vector2i(world_mouse.floor())
var index := cell.y * MAP[0].length() + cell.x
# Select a worker within half a tile; otherwise queue a job from a source
# to a destination (the explicit stable-id submission).
for w_index in worker_nodes.size():
if worker_nodes[w_index].position.distance_to(world_mouse) < TILE * 0.6:
selected = w_index
feedback("selected worker %d" % w_index)
return
selected = -1
var is_source: bool = cell.x >= 0 and cell.y >= 0 and cell.y < MAP.size() and cell.x < MAP[0].length() and MAP[cell.y][cell.x] == "S"
var is_dest: bool = cell.x >= 0 and cell.y >= 0 and cell.y < MAP.size() and cell.x < MAP[0].length() and MAP[cell.y][cell.x] == "D"
if is_source:
pending_source = index
feedback("source %d picked — click a destination" % index)
elif is_dest and pending_source >= 0:
var job_id: int = sim.call("submit_job", pending_source, index)
feedback("job #%d queued" % job_id if job_id > 0 else "job refused")
pending_source = -1
func _on_reset() -> void:
var settings: Resource = load("res://logistics_settings.tres")
sim.call("configure", MAP[0].length(), MAP.size(), _pack_cells(),
int(settings.worker_count), int(settings.seed))
for node in worker_nodes:
node.queue_free()
worker_nodes.clear()
_spawn_worker_sprites(int(settings.worker_count))
pending_source = -1
selected = -1
feedback("reset — same seed, same spread")
func feedback(text: String) -> void:
%FeedbackLabel.text = text