Renewable resources: tree growth + WildGrowth + Quarry on BigRockNode

Trees: 4 growth stages (Sapling→Young→Growing→Mature), only Mature
yields wood. WildGrowth ticker fires every in-game hour; rejection-
samples grass tiles and plants a sapling with ~30% probability (capped
at MAP_TREE_LIMIT=60). New `paint_plant_tree` designation lets the
player manually plant — ghost sapling registered as a build_site that
ConstructionProvider fulfils. Stage round-trips through save/load.
Initial seed mixes 4 saplings + 6 mature so growth is visible day 1.

Quarry: new BigRockNode entity (2×2 permanent stone outcrop, never
depletes). 3 nodes seeded far from cabin. New QuarryWorkbench
(extends Workbench, auto-FOREVER `quarry_stone` bill, recipe drops
1 stone per 300 work-ticks). New `paint_quarry` designation only
accepts BigRockNode tiles. CraftingProvider now supports recipes
with `ingredient_count == 0` — skips ingredient-fetch and goes
straight to walk+craft toils. Recipe gains `ingredient_count` field
(defaults 0). Save/load layering: big_rock_node spawns at priority 0
(same as rock/tree), quarry_workbench at priority 2 (after the node).

UI: Plant tree + Build quarry buttons added to Build drawer.
build_drawer_thumb gains `plant_tree` (sapling sprout in dirt) and
`paint_quarry` (stone block + chisel + cut-stone pile) shapes.
inspect_tooltip recognises BigRockNode + shows tree growth stage on
hover.

Delegation: gdscript-refactor (Sonnet ×2) for trees full impl +
quarry skeleton; quick-edit (Haiku) for CraftingProvider no-ingredient
plumbing + TopBar polish; integration handled on Opus.
This commit is contained in:
megaproxy 2026-05-16 16:36:16 +01:00
parent 296894ff7a
commit d98d2c2425
20 changed files with 716 additions and 38 deletions

View file

@ -264,12 +264,36 @@ func _describe_wolf(w) -> String:
func _describe_tree(t) -> String:
# Growth stage label.
var stage: int = int(t.get("growth_stage")) if "growth_stage" in t else 3
var stage_key_map: Array[StringName] = [
&"tree.stage.sapling",
&"tree.stage.young",
&"tree.stage.growing",
&"tree.stage.mature",
]
var stage_label: String = Strings.t(stage_key_map[clamp(stage, 0, 3)])
# Pending-plant ghost indicator.
var pending: bool = bool(t.get("pending_plant")) if "pending_plant" in t else false
if pending:
return "[b]%s[/b]\n[color=#aaa]awaiting pawn[/color]" % stage_label
# Growth progress for sub-mature trees.
var is_mature: bool = (stage >= 3)
if not is_mature:
var progress: int = int(t.get("growth_progress")) if "growth_progress" in t else 0
var stage_ticks: int = int(t.get("STAGE_TICKS")) if "STAGE_TICKS" in t else 1
var pct_grow: int = int(100.0 * float(progress) / float(max(stage_ticks, 1)))
return "[b]%s[/b]\nGrowing %d%%" % [stage_label, pct_grow]
# Mature tree — show chop progress if any.
var pct: int = int(100.0 * float(t.chop_progress) / float(t.CHOP_TICKS))
var designated: bool = bool(t.get("chop_designated"))
var tag := " · [color=#fc6]marked[/color]" if designated else ""
if pct > 0:
return "[b]Tree[/b]\nChop %d%%%s" % [pct, tag]
return "[b]Tree[/b]%s" % tag
return "[b]%s[/b]\nChop %d%%%s" % [stage_label, pct, tag]
return "[b]%s[/b]%s" % [stage_label, tag]
func _describe_rock(r) -> String: