From 5a6ec53b1263f7ac6322cea8700db5c5fdf11d08 Mon Sep 17 00:00:00 2001 From: megaproxy Date: Sat, 16 May 2026 16:42:38 +0100 Subject: [PATCH] Tree growth: dedicated stage atlas + tuned WildGrowth rate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sub-mature stages (0/1/2) now use FG_Tree_Stages.png (a 128×32 atlas with 8 progressively-larger tree cells from the bundle's "Crops with Stages 03" pack). Stage 0 = tiny sprout (col 0), Stage 1 = small leaf (col 1), Stage 2 = small tree (col 3). Stage 3 (Mature) keeps the existing 64×80 seasonal canopy atlases. Visually distinct progression replaces the previous scale-down-the- mature-texture placeholder + procedural sapling dots. WildGrowth pacing tuned: INTERVAL 1200 → 3000, PROBABILITY 0.30 → 0.12, LIMIT 60 → 80. Previous values flooded the map with saplings within ~30 seconds of 12× play. New rate gives a slow but visible regrowth over a season at default speed. _draw simplified: removed procedural sapling fallback (atlas handles all stages now). Pending-plant ghosts get the alpha tint via sprite.modulate. --- art/sprites/FG_Tree_Stages.png | Bin 0 -> 636 bytes art/sprites/FG_Tree_Stages.png.import | 40 +++++++++++++++ scenes/entities/tree.gd | 69 +++++++++++++------------- scenes/world/world.gd | 6 +-- 4 files changed, 78 insertions(+), 37 deletions(-) create mode 100644 art/sprites/FG_Tree_Stages.png create mode 100644 art/sprites/FG_Tree_Stages.png.import diff --git a/art/sprites/FG_Tree_Stages.png b/art/sprites/FG_Tree_Stages.png new file mode 100644 index 0000000000000000000000000000000000000000..9b4881d872cd2bf19f1354b9ab9a740782aa0f7d GIT binary patch literal 636 zcmeAS@N?(olHy`uVBq!ia0vp^4M42G!3-qd{%H^gQjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`%>sNvT-l-n(x2v;Y)WNxv6kpeW6)JkV^II(#o)sraE5`Qhe51}LDff2 zE=@yYQ)J|ss;Zf`wm_}6-C?>wN~k2rFZe$?V0g5DzYtKCv%n*=n1O-sFbFdq&tH)O zbkIgm7srqY_orcRCp8)H==fSzugJLnKQ&)CDN|eF{`bSFQ*kSOs|=DLq(9&gKSNV=ZCuKNOKR&cgx)BYdv`LC8`+JwD6 z?E6E&iY@XI7hA-wOJA$*o^0T$wJBke01Hg~e&PP%1`fX#zku6yER2^Xt~cGFP|{Sw z(sSZ*S8792&imsR471D+g-gy5+_mQE0S$Rm2Enx~FYToye>Gh&bldr6$K9oU4N?!+ zo}9MkX|a>JRk7=|#^~QCWM^0Rl2Nf8alFZQQsl&oIJX41yJA$Hf{xc8C0`Q`$L void: ## Rebuild the Sprite2D child to match the current growth_stage. -## Sapling (stage 0): no Sprite2D — rendered procedurally in _draw(). -## Young (1) → scale 0.35, Growing (2) → 0.65, Mature (3) → 1.0. -## Any existing "Sprite" child is removed first so re-calls don't stack. +## Stages 0-2 use _STAGE_TEX (a dedicated growth-stage atlas with progressively +## larger trees per cell). Stage 3 (Mature) uses the seasonal full-canopy +## atlases. Any existing "Sprite" child is removed first so re-calls don't stack. func _refresh_sprite() -> void: var old := get_node_or_null("Sprite") if old != null: old.queue_free() - if growth_stage == STAGE_SAPLING: - # No Sprite2D for saplings — all rendering done in _draw(). - queue_redraw() - return - var scale_map: Array[float] = [1.0, 0.35, 0.65, 1.0] # indexed by stage - var sprite_scale: float = scale_map[growth_stage] var sprite := Sprite2D.new() sprite.name = "Sprite" - var hash_seed: int = tile.x * 31 + tile.y * 17 - var silhouette: int = hash_seed % _TREE_SILHOUETTES - # Independent hash mix for season so neighbouring tiles don't all match. - var season: int = ((hash_seed / _TREE_SILHOUETTES) + tile.x * 7 + tile.y * 11) % _TREE_TEXES.size() - sprite.texture = _TREE_TEXES[season] sprite.region_enabled = true - sprite.region_rect = Rect2(silhouette * _TREE_VARIANT_W, 0, _TREE_VARIANT_W, _TREE_VARIANT_H) sprite.centered = true - # Lift the sprite up so its bottom edge sits at the tile's bottom row. - # Sprite center is at offset.y; sprite half-height is _TREE_VARIANT_H/2 = 40. - # We want bottom edge at +8 (tile bottom) → center at 8 - 40 = -32. - sprite.offset = Vector2(0, -32) - sprite.scale = Vector2(sprite_scale, sprite_scale) - # Render behind pawns/items that are at higher z_index; trees live at z=0. sprite.z_index = 0 + if growth_stage < STAGE_MATURE: + # Sub-mature: 16×32 cell from _STAGE_TEX. Bottom edge at tile bottom + # (+8); sprite half-height 16 → centre offset y = 8 - 16 = -8. + var col: int = _STAGE_COLS[growth_stage] + sprite.texture = _STAGE_TEX + sprite.region_rect = Rect2(col * _STAGE_CELL_W, 0, _STAGE_CELL_W, _STAGE_CELL_H) + sprite.offset = Vector2(0, -8) + else: + # Mature: full 64×80 seasonal canopy. Bottom at +8 → centre at -32. + var hash_seed: int = tile.x * 31 + tile.y * 17 + var silhouette: int = hash_seed % _TREE_SILHOUETTES + var season: int = ((hash_seed / _TREE_SILHOUETTES) + tile.x * 7 + tile.y * 11) % _TREE_TEXES.size() + sprite.texture = _TREE_TEXES[season] + sprite.region_rect = Rect2(silhouette * _TREE_VARIANT_W, 0, _TREE_VARIANT_W, _TREE_VARIANT_H) + sprite.offset = Vector2(0, -32) add_child(sprite) queue_redraw() @@ -280,19 +288,12 @@ static func from_dict(d: Dictionary) -> Dictionary: # ── render ──────────────────────────────────────────────────────────────────── func _draw() -> void: - # Sapling stage: draw a procedural sprout — no atlas sprite available. - # Three small green leaf-dots clustered above a thin brown stem. - if growth_stage == STAGE_SAPLING: - # Ghost tint for pending-plant saplings so the player can tell it's - # waiting for a pawn to build it. - var alpha := 0.55 if pending_plant else 1.0 - # Stem - draw_line(Vector2(0.0, 6.0), Vector2(0.0, 0.0), Color(0.35, 0.22, 0.10, alpha), 1.5) - # Three leaf dots - draw_circle(Vector2(0.0, -2.0), 2.5, Color(0.30, 0.65, 0.20, alpha)) - draw_circle(Vector2(-3.0, 1.0), 1.8, Color(0.25, 0.58, 0.18, alpha)) - draw_circle(Vector2(3.0, 0.5), 1.8, Color(0.28, 0.62, 0.19, alpha)) - return + # Ghost tint for pending-plant saplings — apply via modulate on the sprite + # child instead of drawing extra overlay shapes here. + if pending_plant and growth_stage == STAGE_SAPLING: + var s := get_node_or_null("Sprite") + if s != null: + s.modulate = Color(1, 1, 1, 0.55) # Mature / growing stages: canopy + trunk come from the Sprite2D child. # This _draw renders only the chop-progress notch overlaid on the trunk. diff --git a/scenes/world/world.gd b/scenes/world/world.gd index d861ba9..d36e2b0 100644 --- a/scenes/world/world.gd +++ b/scenes/world/world.gd @@ -98,9 +98,9 @@ const HAUL_SWEEP_INTERVAL_TICKS: int = 100 # WildGrowth — spontaneous sapling spawning on eligible grass tiles. # 1200 ticks = 1 in-game hour at 20 Hz (20 ticks/s × 60 s/min = 1200 ticks/min, # but 1 in-game minute = 20 ticks at 1× so 1 hour = 1200 ticks at 1×). -const WILD_GROWTH_INTERVAL: int = 1200 -const WILD_GROWTH_SPAWN_PROBABILITY: float = 0.30 -const MAP_TREE_LIMIT: int = 60 +const WILD_GROWTH_INTERVAL: int = 3000 # ~2.5 in-game hours between attempts +const WILD_GROWTH_SPAWN_PROBABILITY: float = 0.12 # 12% chance per attempt +const MAP_TREE_LIMIT: int = 80 # Rejection-sample attempts before giving up for this tick. const WILD_GROWTH_MAX_ATTEMPTS: int = 10