From f67c12c51f4c6441d23dfff492884e39f52294de Mon Sep 17 00:00:00 2001 From: megaproxy Date: Fri, 15 May 2026 19:31:55 +0100 Subject: [PATCH 1/2] 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). --- autoload/world.gd | 13 +++++++++++++ scenes/entities/bed.gd | 1 + scenes/entities/big_rock.gd | 3 +++ scenes/entities/door.gd | 1 + scenes/entities/floor.gd | 1 + scenes/entities/grave_slot.gd | 1 + scenes/entities/rock.gd | 1 + scenes/entities/torch.gd | 1 + scenes/entities/tree.gd | 1 + scenes/entities/wall.gd | 1 + scenes/entities/workbench.gd | 1 + scenes/world/crate.gd | 1 + scenes/world/world.gd | 3 +++ 13 files changed, 29 insertions(+) diff --git a/autoload/world.gd b/autoload/world.gd index 93a0c92..9169dac 100644 --- a/autoload/world.gd +++ b/autoload/world.gd @@ -28,6 +28,11 @@ var stockpiles: Array = [] # Array of StorageDestination (StockpileZone for # its _ready(). Don't access before the world scene is mounted. var pathfinder = null +## Reference to the in-scene Designation controller. Wired by world.gd _ready +## so entities completing a job can call World.clear_designation_at(tile) to +## remove the lingering ghost paint without depending on the scene tree path. +var designation_ctl = null + # Phase 5 — build queue. Holds Wall/Floor/Door/Crate ghost entities (not yet # completed). ConstructionProvider iterates this for the nearest buildable site. # Entities call register_build_site() in _ready and unregister_build_site() when @@ -83,6 +88,14 @@ var grave_markers: Array = [] var items_needing_haul: Dictionary = {} +## Clear the designation ghost at `tile`, if any. Entities call this from +## their _complete / fell / mined handlers so the visual highlight disappears +## once the job is done. Safe no-op if designation_ctl isn't wired (headless). +func clear_designation_at(tile: Vector2i) -> void: + if designation_ctl != null: + designation_ctl.clear_cell(tile) + + func register_work_provider(wp) -> void: assert(wp != null, "World.register_work_provider: provider is null") if not work_providers.has(wp): diff --git a/scenes/entities/bed.gd b/scenes/entities/bed.gd index aca6e25..47cb226 100644 --- a/scenes/entities/bed.gd +++ b/scenes/entities/bed.gd @@ -279,6 +279,7 @@ func _complete() -> void: if sprite != null: sprite.modulate.a = 1.0 queue_redraw() + World.clear_designation_at(tile) Audit.log("bed", "%s built at %s" % [label_text, tile]) # Phase 13 — notify BeautySystem so nearby tile beauty scores update. var bs = World.get("beauty_system") diff --git a/scenes/entities/big_rock.gd b/scenes/entities/big_rock.gd index cf567c7..93123c6 100644 --- a/scenes/entities/big_rock.gd +++ b/scenes/entities/big_rock.gd @@ -149,6 +149,9 @@ func mined() -> void: Audit.log("big_rock", "mined 2×2 at %s; %d stone drops" % [origin_tile, STONE_DROPS_ON_MINE]) if Audio != null: Audio.play_sfx(&"mine_tick") + # BigRocks span 2×2 — clear the designation stamp from every footprint tile. + for ft in footprint_tiles(): + World.clear_designation_at(ft) queue_free() diff --git a/scenes/entities/door.gd b/scenes/entities/door.gd index 9fc1556..2cf6516 100644 --- a/scenes/entities/door.gd +++ b/scenes/entities/door.gd @@ -181,4 +181,5 @@ func _complete() -> void: if sprite != null: sprite.modulate.a = 1.0 queue_redraw() + World.clear_designation_at(tile) Audit.log("door", "door completed at %s" % tile) diff --git a/scenes/entities/floor.gd b/scenes/entities/floor.gd index 1c5210b..f447c71 100644 --- a/scenes/entities/floor.gd +++ b/scenes/entities/floor.gd @@ -172,4 +172,5 @@ func _complete() -> void: # 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]) diff --git a/scenes/entities/grave_slot.gd b/scenes/entities/grave_slot.gd index 178a63f..87650ea 100644 --- a/scenes/entities/grave_slot.gd +++ b/scenes/entities/grave_slot.gd @@ -224,4 +224,5 @@ func from_dict(d: Dictionary) -> void: func _complete_dig() -> void: _dug = true queue_redraw() + World.clear_designation_at(tile) Audit.log("grave_slot", "grave dug at %s (ready for burial)" % tile) diff --git a/scenes/entities/rock.gd b/scenes/entities/rock.gd index 656cd57..3dc4ea6 100644 --- a/scenes/entities/rock.gd +++ b/scenes/entities/rock.gd @@ -124,6 +124,7 @@ func mined() -> void: Audit.log("rock", "mined at %s; %d stone drop" % [tile, STONE_DROPS_ON_MINE]) if Audio != null: Audio.play_sfx(&"mine_tick") + World.clear_designation_at(tile) queue_free() diff --git a/scenes/entities/torch.gd b/scenes/entities/torch.gd index f189cfc..7c73013 100644 --- a/scenes/entities/torch.gd +++ b/scenes/entities/torch.gd @@ -245,6 +245,7 @@ func _complete() -> void: if _light != null: _light.enabled = _is_on queue_redraw() + World.clear_designation_at(tile) Audit.log("torch", "built at %s" % tile) # Phase 13 — notify BeautySystem so nearby tile beauty scores update. var bs = World.get("beauty_system") diff --git a/scenes/entities/tree.gd b/scenes/entities/tree.gd index 61b1acb..52177bf 100644 --- a/scenes/entities/tree.gd +++ b/scenes/entities/tree.gd @@ -119,6 +119,7 @@ func fell() -> void: Audit.log("tree", "felled at %s; %d wood drops" % [tile, drops_count]) if Audio != null: Audio.play_sfx(&"tree_fell") + World.clear_designation_at(tile) queue_free() diff --git a/scenes/entities/wall.gd b/scenes/entities/wall.gd index 425531e..dd7d85e 100644 --- a/scenes/entities/wall.gd +++ b/scenes/entities/wall.gd @@ -241,4 +241,5 @@ func _complete() -> void: queue_redraw() queue_redraw() + World.clear_designation_at(tile) Audit.log("wall", "%s wall completed at %s" % [wall_material, tile]) diff --git a/scenes/entities/workbench.gd b/scenes/entities/workbench.gd index 9d330b6..51af0fd 100644 --- a/scenes/entities/workbench.gd +++ b/scenes/entities/workbench.gd @@ -419,6 +419,7 @@ func _complete() -> void: if _light != null: _light.enabled = is_on() queue_redraw() + World.clear_designation_at(tile) Audit.log("workbench", "%s built at %s" % [label_text, tile]) # Phase 13 — notify BeautySystem so nearby tile beauty scores update. # Hearth gets base beauty 4 (warm glow); other benches get 1. diff --git a/scenes/world/crate.gd b/scenes/world/crate.gd index dfabf6a..8ab464e 100644 --- a/scenes/world/crate.gd +++ b/scenes/world/crate.gd @@ -143,6 +143,7 @@ func on_build_tick() -> void: if build_progress >= BUILD_TICKS: _completed = true emit_signal("contents_changed") + World.clear_designation_at(tile) Audit.log("crate", "built at %s (capacity %d)" % [tile, CAPACITY]) queue_redraw() diff --git a/scenes/world/world.gd b/scenes/world/world.gd index 1d36b7a..4d0137f 100644 --- a/scenes/world/world.gd +++ b/scenes/world/world.gd @@ -185,6 +185,9 @@ func _ready() -> void: # Designation: bind the paint surface + the Selection guard. designation_ctl.bind(designation_layer, selection) + # Expose on the autoload so entities can clear their ghost stamp on + # completion (Tree.fell, Wall._complete, etc.). + World.designation_ctl = designation_ctl # Register all 10 providers — Decision iterates by .priority desc. # doctor=9 > sleep=8 > eat=7 > construction=6 > chop=5 ≈ plant=5 > mine=4 From c93f889ff1a406dbb38544b4b8bb2ccad4a0d5ca Mon Sep 17 00:00:00 2001 From: megaproxy Date: Fri, 15 May 2026 19:39:57 +0100 Subject: [PATCH 2/2] =?UTF-8?q?Crop=20sprites=20=E2=80=94=20atlas=20art=20?= =?UTF-8?q?+=20four=20growable=20kinds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the procedural stem-and-circle draw with an ElvGames atlas-backed Sprite2D. Crops now pick a per-kind 64×32 (or 80×32) sheet from art/sprites/crops/ and slice cols 0..3 across the SOWN..READY stage range (TILLED keeps the bare dirt rect). The plant sprite is anchored so its bottom edge sits at the tile bottom, matching the tree convention. Four kinds wired in: wheat, potato, corn, strawberry. The boot demo's crop patch now plants one column per kind so all four show up in the spring start state. Harvest outputs map: wheat/corn → grain, potato/strawberry → vegetable. Tooltip already capitalises crop_kind so 'Corn' / 'Strawberry' show through with no UI change. --- art/sprites/crops/FG_Crops_Corns.png | Bin 0 -> 907 bytes art/sprites/crops/FG_Crops_Corns.png.import | 40 ++++++ art/sprites/crops/FG_Crops_Potato.png | Bin 0 -> 658 bytes art/sprites/crops/FG_Crops_Potato.png.import | 40 ++++++ art/sprites/crops/FG_Crops_Strawberry.png | Bin 0 -> 765 bytes .../crops/FG_Crops_Strawberry.png.import | 40 ++++++ art/sprites/crops/FG_Crops_Wheat.png | Bin 0 -> 472 bytes art/sprites/crops/FG_Crops_Wheat.png.import | 40 ++++++ scenes/entities/crop.gd | 114 ++++++++++++------ scenes/world/world.gd | 29 +++-- 10 files changed, 261 insertions(+), 42 deletions(-) create mode 100644 art/sprites/crops/FG_Crops_Corns.png create mode 100644 art/sprites/crops/FG_Crops_Corns.png.import create mode 100644 art/sprites/crops/FG_Crops_Potato.png create mode 100644 art/sprites/crops/FG_Crops_Potato.png.import create mode 100644 art/sprites/crops/FG_Crops_Strawberry.png create mode 100644 art/sprites/crops/FG_Crops_Strawberry.png.import create mode 100644 art/sprites/crops/FG_Crops_Wheat.png create mode 100644 art/sprites/crops/FG_Crops_Wheat.png.import diff --git a/art/sprites/crops/FG_Crops_Corns.png b/art/sprites/crops/FG_Crops_Corns.png new file mode 100644 index 0000000000000000000000000000000000000000..2144b8295f84408008346eac8c3a001639083878 GIT binary patch literal 907 zcmV;619bd}P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TC=QLFa|dTZ5GT``>-R67hIE9*@W4={-~D zrNI$&6Jyb5f_7j~gTF3_Jh>`Ty5Bm7xwrB5Ce;rG?ZAMs<;kEEfFrDIRzDD&JS2_A zi{^2&)zS|xnMk4^m=w-lDmo*x3pJ_j%F5cFiW`T~`C@w~AmOkTLk0On}ZzC{&qR>v8+<>e78fx@&d%dM$0$rRl2cV>}D-@XV5Xh6S9Yj389E$c z0VgHV_f-UvI!u;HGkI1$Y=otT0nV<@tjo9Si=rvbGjuWHP<`R-h|0!2J3 zeLGj86--*_)YIzRgIj*1s{;vleo&XMmD~SLSm?zrs`ZD7YvHD^n@bcJ*|`8t#tt)seYprp35AQ z3FPeDflmmbJE|w9-E>`;X5ukO~rqu$9e8w*e~Ut hI??0tcszrO$S>x;!d^B;Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TYg|DX|6P)}Yx=pSG%dP;f_L82!G zMX-1sZrF`@h&n%5=Dp3%%Y5^__ec-~K@bE%5ClOGo*ysF z_$=^#=_;B-I+@v0+MiSdtO5ax1o&<8YgUaTQ~t-<06Pb*SVeAc>GW+bM}>EO^8CoE zIQZ#;_L9f6pSYmE@l^FS;64sosW*Zu|9y|9L=Ft;)Y+B0Z3e$*@u2D&P^p7tP1o7&)8a=D3jM{WARiu_&Ib$zJN~Yl_M(p&RvNgnI(Gd zeMyN;4{4WqB_7QS4V}?u(mz&6X2;FXsm6>|0fga!NH_@RuoDn5cM{KV4jhMg>;zt` zl(LJ3J6@^t6y5pf_=j$Ay1*EA5XCMmX{va35a)Sf_@+_j=eF=&0QlT9rMb2{;A3-L zvFAe*h5Nd18{rlRwLbg~XPeKr6O}k<2SoP&JXGVzK~P1^?HX6Elg4OoV=VCY4K)S= z6kXnMy%Sh0U>7mx;&HJ(q=R#0Elq=Jy{;r2f0sUPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TCc@|)WiHp^yrvwJxsydMmk*`4=h{{NXb5yLPH!!QiPFbu;m z48v#iT-oz~W+HF=KPQg>VrS>Jd0BI$jUVx!LkA1LbMk8@6E*h9>!O_B`>XF9{lpKY zfw>KM*K)CQ-`kyjKlBqnoCanz0HbL)FaGr3EJI(v`L80$5ZOXNJA=RG>6YC|MWSkOQtS&w*0;2LIjT!WJXV6krJTb v$5_8VP)g;kt1{0@vZ@9dhG7{0Ga`QhBIXy^l|QpL00000NkvXXu0mjfd-_r| literal 0 HcmV?d00001 diff --git a/art/sprites/crops/FG_Crops_Strawberry.png.import b/art/sprites/crops/FG_Crops_Strawberry.png.import new file mode 100644 index 0000000..d5a7897 --- /dev/null +++ b/art/sprites/crops/FG_Crops_Strawberry.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c3wjj1t2ye2wr" +path="res://.godot/imported/FG_Crops_Strawberry.png-76d99d39b51e1c404f00c643db378da4.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/sprites/crops/FG_Crops_Strawberry.png" +dest_files=["res://.godot/imported/FG_Crops_Strawberry.png-76d99d39b51e1c404f00c643db378da4.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/art/sprites/crops/FG_Crops_Wheat.png b/art/sprites/crops/FG_Crops_Wheat.png new file mode 100644 index 0000000000000000000000000000000000000000..9334247dad54a7d478ccf5af1c265cb70f049b0e GIT binary patch literal 472 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3-p)I`?e@QjEnx?oJHr&dIz4a#+$GeH|GX zHuiJ>Nn{1`6$5-iTy4(8|G%CpH`AJ@UgQ5>1_mFYGt(K;ycv8f8DCq<@hJknNgU2hA&m2Ekd$sm`;EU$XKidW#`W(I|WGpb(g zjZ%Mn`2Am#^^I$+A^tkvK{s=usg~7 zGFixoJIP|Y=L-qLA96E7o0m?A`W|)RG<(MrhFk5A3U9UVFkPa_{JLYepB$g|zMr=2 zbvw2&Eb;6;Dfm%3VPXUiqwI%+d0YDLGx$lFUATH?UYVKaSy65Y3t&hxc)I$ztaD0e F0syf@xUT>J literal 0 HcmV?d00001 diff --git a/art/sprites/crops/FG_Crops_Wheat.png.import b/art/sprites/crops/FG_Crops_Wheat.png.import new file mode 100644 index 0000000..2810c12 --- /dev/null +++ b/art/sprites/crops/FG_Crops_Wheat.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://hbjgdtt0tkpe" +path="res://.godot/imported/FG_Crops_Wheat.png-e1d42717b3eaa01ef1c1aae3ae9445e7.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://art/sprites/crops/FG_Crops_Wheat.png" +dest_files=["res://.godot/imported/FG_Crops_Wheat.png-e1d42717b3eaa01ef1c1aae3ae9445e7.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/scenes/entities/crop.gd b/scenes/entities/crop.gd index 7e914d3..f47ed74 100644 --- a/scenes/entities/crop.gd +++ b/scenes/entities/crop.gd @@ -15,9 +15,12 @@ class_name Crop extends Node2D const TILE_SIZE_PX: int = 16 -## Phase 7 ships wheat and potato. Phase 17 expands (berry, hop) per design.md. -const KIND_WHEAT: StringName = &"wheat" -const KIND_POTATO: StringName = &"potato" +## Available crop kinds. Each maps to an ElvGames 64×32 sprite sheet +## (4 stages × 16w × 32h, plant anchored to bottom row). +const KIND_WHEAT: StringName = &"wheat" +const KIND_POTATO: StringName = &"potato" +const KIND_CORN: StringName = &"corn" +const KIND_STRAWBERRY: StringName = &"strawberry" ## Sim ticks per growth stage. 200 ticks × 4 stages = 800 total. ## At 20 Hz × 5× speed = 100 ticks/sec → 8 real seconds per stage, 32 seconds full grow. @@ -26,6 +29,20 @@ const STAGE_COUNT: int = 4 enum Stage { TILLED, SOWN, GROWING_1, GROWING_2, GROWING_3, READY } +## Per-kind sprite atlas. Strawberry / Corn are 80×32 (5 stages) — we still slice +## the first 4 cols, which gives the same progression (the 5th col is a +## post-harvest regrow frame we don't use). +const _CROP_TEXTURES: Dictionary = { + KIND_WHEAT: preload("res://art/sprites/crops/FG_Crops_Wheat.png"), + KIND_POTATO: preload("res://art/sprites/crops/FG_Crops_Potato.png"), + KIND_CORN: preload("res://art/sprites/crops/FG_Crops_Corns.png"), + KIND_STRAWBERRY: preload("res://art/sprites/crops/FG_Crops_Strawberry.png"), +} + +## Width / height of one stage cell in pixels. +const _STAGE_W: int = 16 +const _STAGE_H: int = 32 + @export var crop_kind: StringName = KIND_WHEAT @export var tile: Vector2i = Vector2i.ZERO @@ -37,6 +54,9 @@ var stage_progress: int = 0 # indoor detection for this crop instance so we don't flood the audit log. var _logged_indoor: bool = false +# Child Sprite2D — created in _ready, region_rect updated whenever stage flips. +var _sprite: Sprite2D = null + const ITEM_SCENE: PackedScene = preload("res://scenes/entities/item.tscn") @@ -44,6 +64,7 @@ const ITEM_SCENE: PackedScene = preload("res://scenes/entities/item.tscn") func _ready() -> void: position = _tile_to_world(tile) + _build_sprite() World.register_crop(self) EventBus.sim_tick.connect(_on_sim_tick) queue_redraw() @@ -62,6 +83,9 @@ func setup(p_tile: Vector2i, p_kind: StringName, p_stage: Stage = Stage.SOWN) -> stage = p_stage stage_progress = 0 position = _tile_to_world(tile) + if _sprite != null: + _sprite.texture = _texture_for(crop_kind) + _refresh_sprite_region() queue_redraw() Audit.log("crop", "spawned %s at %s (stage=%s)" % [crop_kind, tile, Stage.keys()[stage]]) @@ -87,6 +111,7 @@ func on_harvest_tick() -> void: it.setup(item_type, 1, tile) stage = Stage.TILLED stage_progress = 0 + _refresh_sprite_region() Audit.log("crop", "harvested %s at %s → %s" % [crop_kind, tile, item_type]) queue_redraw() @@ -98,6 +123,7 @@ func on_sow_tick() -> void: return stage = Stage.SOWN stage_progress = 0 + _refresh_sprite_region() Audit.log("crop", "sown %s at %s" % [crop_kind, tile]) queue_redraw() @@ -122,6 +148,7 @@ func _on_sim_tick(_n: int) -> void: if stage_progress >= STAGE_TICKS: stage_progress = 0 stage = (int(stage) + 1) as Stage + _refresh_sprite_region() queue_redraw() if stage == Stage.READY: Audit.log("crop", "%s ready at %s" % [crop_kind, tile]) @@ -156,53 +183,70 @@ static func from_dict(d: Dictionary) -> Dictionary: # ── render ──────────────────────────────────────────────────────────────────── func _draw() -> void: - # Tilled-soil base: a small dark-earth square. + # Tilled-soil base draws under the plant sprite. Stays visible at every stage + # so the player sees the patch as cultivated. var soil_color := Color(0.32, 0.20, 0.10) var soil_dark := Color(0.22, 0.14, 0.06) draw_rect(Rect2(Vector2(-7.0, -7.0), Vector2(14.0, 14.0)), soil_color) draw_rect(Rect2(Vector2(-7.0, -7.0), Vector2(14.0, 14.0)), soil_dark, false, 1.0) - if stage == Stage.TILLED: - return # Bare soil — no plant drawn. - # stage_idx: 0 = SOWN, 4 = READY - var stage_idx := int(stage) - int(Stage.SOWN) - var height: float = lerp(2.0, 12.0, float(stage_idx) / float(STAGE_COUNT)) - var plant_color := _plant_color_for(crop_kind) +# ── sprite helpers ──────────────────────────────────────────────────────────── - # Stem - draw_rect(Rect2(Vector2(-2.0, 5.0 - height), Vector2(4.0, height)), plant_color) +func _build_sprite() -> void: + _sprite = Sprite2D.new() + _sprite.name = "Sprite" + _sprite.texture = _texture_for(crop_kind) + _sprite.region_enabled = true + _sprite.centered = true + # 32-tall sprite anchored so its bottom edge sits at the tile's bottom row + # (local +8 from tile centre). Sprite half-height = 16 → offset.y = 8 - 16 = -8. + _sprite.offset = Vector2(0, -8) + # Draw the plant above the soil rect but below pawns/items. + _sprite.z_index = 0 + add_child(_sprite) + _refresh_sprite_region() - # Foliage circle grows in from GROWING_2 onward - if stage_idx >= 2: - draw_circle(Vector2(0.0, 5.0 - height), 3.0 + float(stage_idx), plant_color) - # Ready accent — grain head or potato cap - if stage == Stage.READY: - draw_circle(Vector2(0.0, 5.0 - height), 2.0, _ready_accent_for(crop_kind)) +func _refresh_sprite_region() -> void: + if _sprite == null: + return + var idx := _sprite_stage_index(stage) + if idx < 0: + _sprite.visible = false + return + _sprite.visible = true + _sprite.region_rect = Rect2(idx * _STAGE_W, 0, _STAGE_W, _STAGE_H) + + +## Map game-stage to one of the 4 sprite columns. TILLED has no plant frame. +## SOWN..GROWING_2 step through cols 0..2; GROWING_3 and READY both land on +## col 3 (mature). The harvest designation overlay is what cues the player +## that READY is ready — sprite alone doesn't need a fifth frame. +func _sprite_stage_index(s: Stage) -> int: + match s: + Stage.TILLED: return -1 + Stage.SOWN: return 0 + Stage.GROWING_1: return 1 + Stage.GROWING_2: return 2 + Stage.GROWING_3: return 3 + Stage.READY: return 3 + _: return 0 # ── helpers ─────────────────────────────────────────────────────────────────── +func _texture_for(kind: StringName) -> Texture2D: + return _CROP_TEXTURES.get(kind, _CROP_TEXTURES[KIND_WHEAT]) + + func _harvest_output_for(kind: StringName) -> StringName: match kind: - KIND_WHEAT: return Item.TYPE_GRAIN - KIND_POTATO: return Item.TYPE_VEGETABLE - _: return Item.TYPE_VEGETABLE # fallback - - -func _plant_color_for(kind: StringName) -> Color: - match kind: - KIND_WHEAT: return Color(0.50, 0.65, 0.20) # bright green sprout - KIND_POTATO: return Color(0.30, 0.55, 0.20) # darker green - _: return Color(0.40, 0.60, 0.20) - - -func _ready_accent_for(kind: StringName) -> Color: - match kind: - KIND_WHEAT: return Color(0.95, 0.85, 0.20) # golden grain head - KIND_POTATO: return Color(0.95, 0.60, 0.30) # orange potato cap - _: return Color(1.0, 0.4, 0.4) + KIND_WHEAT: return Item.TYPE_GRAIN + KIND_POTATO: return Item.TYPE_VEGETABLE + KIND_CORN: return Item.TYPE_GRAIN + KIND_STRAWBERRY: return Item.TYPE_VEGETABLE + _: return Item.TYPE_VEGETABLE func _tile_to_world(t: Vector2i) -> Vector2: diff --git a/scenes/world/world.gd b/scenes/world/world.gd index 4d0137f..734cc1c 100644 --- a/scenes/world/world.gd +++ b/scenes/world/world.gd @@ -561,15 +561,30 @@ func _seed_phase5_demo_buildings() -> void: meal_bill.mode = Bill.Mode.FOREVER hearth.add_bill(meal_bill) - # Wheat crops east of the cabin, near the trees. - var crop_tiles: Array[Vector2i] = [ - Vector2i(54, 24), Vector2i(54, 25), Vector2i(54, 26), - Vector2i(55, 24), Vector2i(55, 25), Vector2i(55, 26), + # Mixed crops east of the cabin, near the trees. One column per kind so the + # player sees the four atlas variants side-by-side from the boot demo. + var crop_plan: Array = [ + [Vector2i(54, 24), Crop.KIND_WHEAT], + [Vector2i(54, 25), Crop.KIND_WHEAT], + [Vector2i(54, 26), Crop.KIND_WHEAT], + [Vector2i(55, 24), Crop.KIND_POTATO], + [Vector2i(55, 25), Crop.KIND_POTATO], + [Vector2i(55, 26), Crop.KIND_POTATO], + [Vector2i(56, 24), Crop.KIND_CORN], + [Vector2i(56, 25), Crop.KIND_CORN], + [Vector2i(56, 26), Crop.KIND_CORN], + [Vector2i(57, 24), Crop.KIND_STRAWBERRY], + [Vector2i(57, 25), Crop.KIND_STRAWBERRY], + [Vector2i(57, 26), Crop.KIND_STRAWBERRY], ] - for ct in crop_tiles: + var crop_tiles: Array[Vector2i] = [] + for entry in crop_plan: + var ct: Vector2i = entry[0] + var kind: StringName = entry[1] + crop_tiles.append(ct) var c: Crop = CROP_SCENE.instantiate() add_child(c) - c.setup(ct, Crop.KIND_WHEAT, Crop.Stage.SOWN) + c.setup(ct, kind, Crop.Stage.SOWN) # Pre-baked breads + a vegetable meal so pawns can eat before the # full cooking chain finishes. Phase 17 may remove these as cooking @@ -581,7 +596,7 @@ func _seed_phase5_demo_buildings() -> void: bread_item.setup(Item.TYPE_BREAD, 1, st) bread_item.quality = Item.Quality.NORMAL - Audit.log("world", "phase 7 demo: Millstone+Hearth built, %d wheat crops sown, %d pre-baked breads placed" % [ + Audit.log("world", "phase 7 demo: Millstone+Hearth built, %d crops sown (mixed kinds), %d pre-baked breads placed" % [ crop_tiles.size(), snack_tiles.size() ])