rimlike/scenes/entities/floor.gd
megaproxy f67c12c51f Clear designation tile-highlight when jobs complete
Each entity completion handler (wall/floor/door/bed/torch/workbench/crate
/tree/rock/big_rock/grave_slot) now calls World.clear_designation_at(tile)
so the orange/blue/etc. highlight overlay disappears with the job.
BigRock iterates its footprint to clear all four tiles.

World.designation_ctl is set during the scene boot wire-up; the helper
no-ops when the controller is absent (e.g. headless tests).
2026-05-15 19:31:55 +01:00

176 lines
6.5 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class_name Floor extends Node2D
## Floor entity — built by a pawn with a Build job. Stamps the Floor TileMap
## layer once complete. Floors do NOT block pathfinding.
##
## Floor is ground-level: its render origin sits at the tile centre (not
## bottom-anchored like Wall/Door). Y-sort is not needed because floor sprites
## always sort below any tall entity at the same tile.
##
## Build model (docs/implementation.md Phase 5):
## A ConstructionProvider creates a Job whose BUILD toil calls on_build_tick()
## once per sim tick via JobRunner. After BUILD_TICKS ticks the floor is
## complete: it stamps the data-layer TileMap (World.mark_floor_tile) and
## transitions from ghost (40% alpha) to solid.
##
## World registration: World.register_build_site / World.unregister_build_site
## are called from _ready / _exit_tree. Methods land in the Opus integration pass.
const TILE_SIZE_PX: int = 16
## Sim ticks to lay a floor at 1× speed (30 ticks = 1.5 sim seconds).
const BUILD_TICKS: int = 30
const MATERIAL_WOOD: StringName = &"wood"
const MATERIAL_STONE: StringName = &"stone"
const MATERIAL_DIRT: StringName = &"dirt"
# ── state ──────────────────────────────────────────────────────────────────────
@export var floor_material: StringName = MATERIAL_WOOD
@export var tile: Vector2i = Vector2i.ZERO
## 0..BUILD_TICKS.
var build_progress: int = 0
var _completed: bool = false
# ── lifecycle ──────────────────────────────────────────────────────────────────
func _ready() -> void:
World.register_build_site(self)
func _exit_tree() -> void:
World.unregister_build_site(self)
# ── public API ─────────────────────────────────────────────────────────────────
## One-shot initialiser. Call after add_child() so _ready() has already fired.
func setup(p_tile: Vector2i, p_material: StringName) -> void:
tile = p_tile
floor_material = p_material
# Top-anchor (origin at tile's top edge) so Y-sort places the floor under
# pawns standing on the same tile (pawns anchor at mid-tile, so pawn.y > floor.y).
# _draw offsets compensate (rects span y=0..TILE_SIZE_PX, not -half..+half).
position = Vector2(
tile.x * TILE_SIZE_PX + TILE_SIZE_PX / 2.0,
tile.y * TILE_SIZE_PX
)
queue_redraw()
Audit.log("floor", "%s floor ghost placed at %s" % [floor_material, tile])
## True while the floor still needs construction work.
func is_buildable() -> bool:
return not _completed
## Human-readable label for job descriptions and Audit logs.
func label() -> String:
return "%s floor" % floor_material
## Called by the BUILD toil in JobRunner once per sim tick.
func on_build_tick() -> void:
if _completed:
return
build_progress += 1
queue_redraw()
if build_progress >= BUILD_TICKS:
_complete()
## True once the floor has been fully laid.
func is_completed() -> bool:
return _completed
# ── save / load ────────────────────────────────────────────────────────────────
func to_dict() -> Dictionary:
return {
"class_id": &"floor",
"tile_x": tile.x,
"tile_y": tile.y,
"material": str(floor_material),
"build_progress": build_progress,
"completed": _completed,
}
static func from_dict(d: Dictionary) -> Dictionary:
return {
"tile_x": int(d.get("tile_x", 0)),
"tile_y": int(d.get("tile_y", 0)),
"material": str(d.get("material", "wood")),
"build_progress": int(d.get("build_progress", 0)),
"completed": bool(d.get("completed", false)),
}
# ── render ─────────────────────────────────────────────────────────────────────
func _draw() -> void:
# 16×16 tile centred on origin.
var alpha: float = 1.0 if _completed else 0.4
var half: float = TILE_SIZE_PX / 2.0
match floor_material:
MATERIAL_WOOD:
_draw_wood_floor(alpha, half)
MATERIAL_STONE:
_draw_stone_floor(alpha, half)
_: # MATERIAL_DIRT and any future materials
_draw_dirt_floor(alpha, half)
func _draw_wood_floor(alpha: float, half: float) -> void:
# Top-anchored origin: rect spans (-half, 0) to (+half, TILE_SIZE_PX).
var base := Color(0.58, 0.40, 0.20, alpha)
var plank := Color(0.50, 0.34, 0.16, alpha)
draw_rect(Rect2(Vector2(-half, 0.0), Vector2(TILE_SIZE_PX, TILE_SIZE_PX)), base)
# Horizontal plank lines (offsets are from tile centre = +half from origin).
for y_offset in [5.0, 10.0]:
draw_line(
Vector2(-half, y_offset),
Vector2(half, y_offset),
plank, 1.0
)
draw_rect(Rect2(Vector2(-half, 0.0), Vector2(TILE_SIZE_PX, TILE_SIZE_PX)), Color(0.0, 0.0, 0.0, 0.3 * alpha), false, 1.0)
func _draw_stone_floor(alpha: float, half: float) -> void:
# Top-anchored origin: rect spans (-half, 0) to (+half, TILE_SIZE_PX).
var base := Color(0.60, 0.60, 0.57, alpha)
var joint := Color(0.45, 0.45, 0.43, alpha)
draw_rect(Rect2(Vector2(-half, 0.0), Vector2(TILE_SIZE_PX, TILE_SIZE_PX)), base)
# Stone tile grid lines (cross at tile centre).
draw_line(Vector2(0.0, 0.0), Vector2(0.0, TILE_SIZE_PX), joint, 1.0)
draw_line(Vector2(-half, half), Vector2(half, half), joint, 1.0)
draw_rect(Rect2(Vector2(-half, 0.0), Vector2(TILE_SIZE_PX, TILE_SIZE_PX)), Color(0.0, 0.0, 0.0, 0.3 * alpha), false, 1.0)
func _draw_dirt_floor(alpha: float, half: float) -> void:
# Top-anchored origin: rect spans (-half, 0) to (+half, TILE_SIZE_PX).
var base := Color(0.42, 0.30, 0.18, alpha)
draw_rect(Rect2(Vector2(-half, 0.0), Vector2(TILE_SIZE_PX, TILE_SIZE_PX)), base)
draw_rect(Rect2(Vector2(-half, 0.0), Vector2(TILE_SIZE_PX, TILE_SIZE_PX)), Color(0.0, 0.0, 0.0, 0.25 * alpha), false, 1.0)
# ── internal ───────────────────────────────────────────────────────────────────
func _complete() -> void:
_completed = true
# Floors do NOT block pathfinding — no pathfinder call here.
World.mark_floor_tile(tile, floor_material)
queue_redraw()
World.clear_designation_at(tile)
Audit.log("floor", "%s floor completed at %s" % [floor_material, tile])