extends "res://scripts/building/piece.gd" ## A charcoal kiln or a smelter: E opens it like a chest. Put what it works ## on inside and it turns it into something else over time, on its own, ## while you're away (docs/PROGRESSION_DESIGN.md ยง2). It glows while it works. ## ## - Kiln: 2 wood -> 1 charcoal, 20 s. ## - Smelter: 1 iron ore + 1 charcoal -> 1 iron bar, 30 s; or 3 iron ## nuggets + 1 charcoal -> 1 iron bar. ## ## The output goes into the same slots, for you to take out. On a server it ## runs there (step() is skipped on players' copies) and the contents reach ## everyone like a chest's. Progress is saved. const SLOTS := 6 const RECIPES := { "charcoal_kiln": [{"inputs": {"wood": 2}, "output": "charcoal", "seconds": 20.0}], "smelter": [ {"inputs": {"iron_ore": 1, "charcoal": 1}, "output": "iron_bar", "seconds": 30.0}, {"inputs": {"iron_nugget": 3, "charcoal": 1}, "output": "iron_bar", "seconds": 30.0}, ], } var inventory := Inventory.new(SLOTS) var progress := 0.0 ## seconds into the current batch var _light: OmniLight3D var _glow: MeshInstance3D var _t := 0.0 func _build() -> void: add_to_group("processor") inventory.changed.connect(_on_changed) if DisplayServer.get_name() == "headless": return _light = OmniLight3D.new() _light.light_color = Color(1.0, 0.55, 0.25) _light.omni_range = 5.0 _light.light_energy = 0.0 _light.position = Vector3(0, 0.6, 0.5) add_child(_light) # The fire showing through the mouth. _glow = MeshInstance3D.new() var q := QuadMesh.new() q.size = Vector2(0.36, 0.26) _glow.mesh = q var m := StandardMaterial3D.new() m.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED m.albedo_color = Color(1.0, 0.45, 0.12) m.emission_enabled = true m.emission = Color(1.0, 0.45, 0.12) m.emission_energy_multiplier = 3.0 _glow.material_override = m _glow.position = Vector3(0, 0.3 if item_id == "smelter" else 0.25, 0.58 if item_id == "smelter" else 0.72) _glow.visible = false add_child(_glow) ## The recipe it would work on now, or {} (nothing to do, or no room for ## what it makes). func current() -> Dictionary: for r in RECIPES.get(item_id, []): if inventory.has_all(r.inputs) and inventory.space_for(r.output) > 0: return r return {} func working() -> bool: return not current().is_empty() func _process(delta: float) -> void: if WorldNet.is_client(): _show(working()) # (the server does the work; we just show it) return step(delta) ## Works for `secs` (several batches if there's time and stuff for them). func step(secs: float) -> void: var r := current() if r.is_empty(): progress = 0.0 _show(false) return progress += secs while not r.is_empty() and progress >= float(r.seconds): progress -= float(r.seconds) for id in r.inputs: inventory.remove(id, int(r.inputs[id])) inventory.add(r.output, 1) r = current() if r.is_empty(): progress = 0.0 _show(not r.is_empty()) func _show(on: bool) -> void: if _light == null: return _t += get_process_delta_time() _glow.visible = on _light.light_energy = (1.2 + 0.25 * sin(_t * 9.0) * sin(_t * 3.1)) if on else 0.0 func _on_changed() -> void: if current().is_empty(): progress = 0.0 func dismantle_block_reason() -> String: for s in inventory.slots: if s != null: return "Empty it first" return "" func interact_prompt(_player: Node) -> String: var r := current() var what := "charcoal" if item_id == "charcoal_kiln" else "an iron bar" if r.is_empty(): var needs := "put in wood" if item_id == "charcoal_kiln" else "put in iron ore (or 3 nuggets) and charcoal" return "%s (idle: %s) [E] Open" % [display_name(), needs] if WorldNet.is_client(): return "%s (making %s) [E] Open" % [display_name(), what] return "%s (making %s, %d s) [E] Open" % [display_name(), what, ceili(float(r.seconds) - progress)] func interact(player: Node) -> void: player.open_inventory(self) func save_extra() -> Dictionary: return {"progress": progress} func load_extra(d: Dictionary) -> void: progress = float(d.get("progress", 0.0))