diff --git a/art/sprites/FG_Tree_Fall.png b/art/sprites/FG_Tree_Fall.png deleted file mode 100644 index 6f9c887..0000000 Binary files a/art/sprites/FG_Tree_Fall.png and /dev/null differ diff --git a/art/sprites/FG_Tree_Fall.png.import b/art/sprites/FG_Tree_Fall.png.import deleted file mode 100644 index c735e56..0000000 --- a/art/sprites/FG_Tree_Fall.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://qen8u4g1oee4" -path="res://.godot/imported/FG_Tree_Fall.png-63225671846c6354f20ce0b5e0a5e31e.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/FG_Tree_Fall.png" -dest_files=["res://.godot/imported/FG_Tree_Fall.png-63225671846c6354f20ce0b5e0a5e31e.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/FG_Tree_Stages.png b/art/sprites/FG_Tree_Stages.png deleted file mode 100644 index 9b4881d..0000000 Binary files a/art/sprites/FG_Tree_Stages.png and /dev/null differ diff --git a/art/sprites/FG_Tree_Stages.png.import b/art/sprites/FG_Tree_Stages.png.import deleted file mode 100644 index a22b563..0000000 --- a/art/sprites/FG_Tree_Stages.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bws0uhalpfbln" -path="res://.godot/imported/FG_Tree_Stages.png-620ce94d31b96e88794dc081cb63ea2e.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/FG_Tree_Stages.png" -dest_files=["res://.godot/imported/FG_Tree_Stages.png-620ce94d31b96e88794dc081cb63ea2e.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/FG_Tree_Summer.png b/art/sprites/FG_Tree_Summer.png deleted file mode 100644 index 7408581..0000000 Binary files a/art/sprites/FG_Tree_Summer.png and /dev/null differ diff --git a/art/sprites/FG_Tree_Summer.png.import b/art/sprites/FG_Tree_Summer.png.import deleted file mode 100644 index 3691b91..0000000 --- a/art/sprites/FG_Tree_Summer.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cepguvswk8ecj" -path="res://.godot/imported/FG_Tree_Summer.png-1bc1b92f64d4677ada2e125e991dc553.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/FG_Tree_Summer.png" -dest_files=["res://.godot/imported/FG_Tree_Summer.png-1bc1b92f64d4677ada2e125e991dc553.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/characters/Character_001_Dead.png b/art/sprites/characters/Character_001_Dead.png deleted file mode 100644 index 2340c33..0000000 Binary files a/art/sprites/characters/Character_001_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_001_Dead.png.import b/art/sprites/characters/Character_001_Dead.png.import deleted file mode 100644 index 4fdb88a..0000000 --- a/art/sprites/characters/Character_001_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dl5678nw0ak4q" -path="res://.godot/imported/Character_001_Dead.png-53f8a2c7c9d21301ad6ecc32b2e18e90.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_001_Dead.png" -dest_files=["res://.godot/imported/Character_001_Dead.png-53f8a2c7c9d21301ad6ecc32b2e18e90.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/characters/Character_001_Idle.png b/art/sprites/characters/Character_001_Idle.png deleted file mode 100644 index 9d86ee8..0000000 Binary files a/art/sprites/characters/Character_001_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_001_Idle.png.import b/art/sprites/characters/Character_001_Idle.png.import deleted file mode 100644 index 1ee7f2a..0000000 --- a/art/sprites/characters/Character_001_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://vdtgxefdrv6h" -path="res://.godot/imported/Character_001_Idle.png-949c25bb7badfcc54ea7b50733e40589.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_001_Idle.png" -dest_files=["res://.godot/imported/Character_001_Idle.png-949c25bb7badfcc54ea7b50733e40589.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/characters/Character_001_Walk.png b/art/sprites/characters/Character_001_Walk.png deleted file mode 100644 index 3177249..0000000 Binary files a/art/sprites/characters/Character_001_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_001_Walk.png.import b/art/sprites/characters/Character_001_Walk.png.import deleted file mode 100644 index 0f2c40a..0000000 --- a/art/sprites/characters/Character_001_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bx7dgdvihp3kj" -path="res://.godot/imported/Character_001_Walk.png-84dbf748230688d019f76cc700c71c17.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_001_Walk.png" -dest_files=["res://.godot/imported/Character_001_Walk.png-84dbf748230688d019f76cc700c71c17.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/characters/Character_002_Dead.png b/art/sprites/characters/Character_002_Dead.png deleted file mode 100644 index f1be9a0..0000000 Binary files a/art/sprites/characters/Character_002_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_002_Dead.png.import b/art/sprites/characters/Character_002_Dead.png.import deleted file mode 100644 index 70cfd3c..0000000 --- a/art/sprites/characters/Character_002_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bvyms2xddxkky" -path="res://.godot/imported/Character_002_Dead.png-6e0015f8f83360b89fdd2ea19f2e0a7c.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_002_Dead.png" -dest_files=["res://.godot/imported/Character_002_Dead.png-6e0015f8f83360b89fdd2ea19f2e0a7c.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/characters/Character_002_Idle.png b/art/sprites/characters/Character_002_Idle.png deleted file mode 100644 index c767dc8..0000000 Binary files a/art/sprites/characters/Character_002_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_002_Idle.png.import b/art/sprites/characters/Character_002_Idle.png.import deleted file mode 100644 index 92e87f2..0000000 --- a/art/sprites/characters/Character_002_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://docwang41u716" -path="res://.godot/imported/Character_002_Idle.png-6255c26c24043e08900d4ae605cfeb76.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_002_Idle.png" -dest_files=["res://.godot/imported/Character_002_Idle.png-6255c26c24043e08900d4ae605cfeb76.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/characters/Character_002_Walk.png b/art/sprites/characters/Character_002_Walk.png deleted file mode 100644 index 9d75294..0000000 Binary files a/art/sprites/characters/Character_002_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_002_Walk.png.import b/art/sprites/characters/Character_002_Walk.png.import deleted file mode 100644 index 9d84919..0000000 --- a/art/sprites/characters/Character_002_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bris5vy0xmie6" -path="res://.godot/imported/Character_002_Walk.png-2734f78de0275bc238cdbc3320fddda0.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_002_Walk.png" -dest_files=["res://.godot/imported/Character_002_Walk.png-2734f78de0275bc238cdbc3320fddda0.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/characters/Character_003_Dead.png b/art/sprites/characters/Character_003_Dead.png deleted file mode 100644 index eb0ff13..0000000 Binary files a/art/sprites/characters/Character_003_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_003_Dead.png.import b/art/sprites/characters/Character_003_Dead.png.import deleted file mode 100644 index f5537bf..0000000 --- a/art/sprites/characters/Character_003_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dqdudpnru30hk" -path="res://.godot/imported/Character_003_Dead.png-1d1221ee02cee53c8990f3b4ff17debf.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_003_Dead.png" -dest_files=["res://.godot/imported/Character_003_Dead.png-1d1221ee02cee53c8990f3b4ff17debf.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/characters/Character_003_Idle.png b/art/sprites/characters/Character_003_Idle.png deleted file mode 100644 index f4eb96d..0000000 Binary files a/art/sprites/characters/Character_003_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_003_Idle.png.import b/art/sprites/characters/Character_003_Idle.png.import deleted file mode 100644 index 45b1af1..0000000 --- a/art/sprites/characters/Character_003_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cmirhu1042dd2" -path="res://.godot/imported/Character_003_Idle.png-a766339ea476ee9c81ef530027361cec.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_003_Idle.png" -dest_files=["res://.godot/imported/Character_003_Idle.png-a766339ea476ee9c81ef530027361cec.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/characters/Character_003_Walk.png b/art/sprites/characters/Character_003_Walk.png deleted file mode 100644 index d09814d..0000000 Binary files a/art/sprites/characters/Character_003_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_003_Walk.png.import b/art/sprites/characters/Character_003_Walk.png.import deleted file mode 100644 index 13465a5..0000000 --- a/art/sprites/characters/Character_003_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://qqwwf465ak8y" -path="res://.godot/imported/Character_003_Walk.png-00de2bb6ade98a02f8b195e4d53e072f.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_003_Walk.png" -dest_files=["res://.godot/imported/Character_003_Walk.png-00de2bb6ade98a02f8b195e4d53e072f.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/characters/Character_004_Dead.png b/art/sprites/characters/Character_004_Dead.png deleted file mode 100644 index fe4dbc7..0000000 Binary files a/art/sprites/characters/Character_004_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_004_Dead.png.import b/art/sprites/characters/Character_004_Dead.png.import deleted file mode 100644 index 59c766b..0000000 --- a/art/sprites/characters/Character_004_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cusg1ywjaxe38" -path="res://.godot/imported/Character_004_Dead.png-981400b980c80c3710e64738bd1cf562.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_004_Dead.png" -dest_files=["res://.godot/imported/Character_004_Dead.png-981400b980c80c3710e64738bd1cf562.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/characters/Character_004_Idle.png b/art/sprites/characters/Character_004_Idle.png deleted file mode 100644 index c591502..0000000 Binary files a/art/sprites/characters/Character_004_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_004_Idle.png.import b/art/sprites/characters/Character_004_Idle.png.import deleted file mode 100644 index b335ea3..0000000 --- a/art/sprites/characters/Character_004_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bgke8dpxels81" -path="res://.godot/imported/Character_004_Idle.png-342dbbc4500cc6871b75d10b5a4fc95f.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_004_Idle.png" -dest_files=["res://.godot/imported/Character_004_Idle.png-342dbbc4500cc6871b75d10b5a4fc95f.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/characters/Character_004_Walk.png b/art/sprites/characters/Character_004_Walk.png deleted file mode 100644 index 6991792..0000000 Binary files a/art/sprites/characters/Character_004_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_004_Walk.png.import b/art/sprites/characters/Character_004_Walk.png.import deleted file mode 100644 index c0fd46d..0000000 --- a/art/sprites/characters/Character_004_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cm05cheuxkrtm" -path="res://.godot/imported/Character_004_Walk.png-d111e552e0083c96124a1b00f2599f41.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_004_Walk.png" -dest_files=["res://.godot/imported/Character_004_Walk.png-d111e552e0083c96124a1b00f2599f41.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/characters/Character_005_Dead.png b/art/sprites/characters/Character_005_Dead.png deleted file mode 100644 index 5f3a593..0000000 Binary files a/art/sprites/characters/Character_005_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_005_Dead.png.import b/art/sprites/characters/Character_005_Dead.png.import deleted file mode 100644 index d1a7970..0000000 --- a/art/sprites/characters/Character_005_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c1kkoepbenudx" -path="res://.godot/imported/Character_005_Dead.png-71cd93adab35964081cdb8b40879e3f1.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_005_Dead.png" -dest_files=["res://.godot/imported/Character_005_Dead.png-71cd93adab35964081cdb8b40879e3f1.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/characters/Character_005_Idle.png b/art/sprites/characters/Character_005_Idle.png deleted file mode 100644 index bb5b062..0000000 Binary files a/art/sprites/characters/Character_005_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_005_Idle.png.import b/art/sprites/characters/Character_005_Idle.png.import deleted file mode 100644 index 7ac7c53..0000000 --- a/art/sprites/characters/Character_005_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://doq7u1lurebfx" -path="res://.godot/imported/Character_005_Idle.png-26dd621a4b4777f2380bc2c5b6138d38.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_005_Idle.png" -dest_files=["res://.godot/imported/Character_005_Idle.png-26dd621a4b4777f2380bc2c5b6138d38.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/characters/Character_005_Walk.png b/art/sprites/characters/Character_005_Walk.png deleted file mode 100644 index 416e4d5..0000000 Binary files a/art/sprites/characters/Character_005_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_005_Walk.png.import b/art/sprites/characters/Character_005_Walk.png.import deleted file mode 100644 index fac2c69..0000000 --- a/art/sprites/characters/Character_005_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://btabpxr6tfheg" -path="res://.godot/imported/Character_005_Walk.png-3f89560bbea7fdef9a96610b7ac147fa.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_005_Walk.png" -dest_files=["res://.godot/imported/Character_005_Walk.png-3f89560bbea7fdef9a96610b7ac147fa.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/characters/Character_006_Dead.png b/art/sprites/characters/Character_006_Dead.png deleted file mode 100644 index dfc8ff6..0000000 Binary files a/art/sprites/characters/Character_006_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_006_Dead.png.import b/art/sprites/characters/Character_006_Dead.png.import deleted file mode 100644 index c75c94c..0000000 --- a/art/sprites/characters/Character_006_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://b4o73ivsxbg75" -path="res://.godot/imported/Character_006_Dead.png-8aed15fa6599b13181065136d33bde6f.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_006_Dead.png" -dest_files=["res://.godot/imported/Character_006_Dead.png-8aed15fa6599b13181065136d33bde6f.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/characters/Character_006_Idle.png b/art/sprites/characters/Character_006_Idle.png deleted file mode 100644 index 5cdea3e..0000000 Binary files a/art/sprites/characters/Character_006_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_006_Idle.png.import b/art/sprites/characters/Character_006_Idle.png.import deleted file mode 100644 index 89d1b5a..0000000 --- a/art/sprites/characters/Character_006_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c2vhpfcn6ayla" -path="res://.godot/imported/Character_006_Idle.png-87c1a57347f63318a7da74a3696653fd.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_006_Idle.png" -dest_files=["res://.godot/imported/Character_006_Idle.png-87c1a57347f63318a7da74a3696653fd.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/characters/Character_006_Walk.png b/art/sprites/characters/Character_006_Walk.png deleted file mode 100644 index 2a4e180..0000000 Binary files a/art/sprites/characters/Character_006_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_006_Walk.png.import b/art/sprites/characters/Character_006_Walk.png.import deleted file mode 100644 index bb5bb39..0000000 --- a/art/sprites/characters/Character_006_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bq5ba7puv8mx1" -path="res://.godot/imported/Character_006_Walk.png-751a8de1d914918d6e78a313db34ee8e.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_006_Walk.png" -dest_files=["res://.godot/imported/Character_006_Walk.png-751a8de1d914918d6e78a313db34ee8e.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/characters/Character_007_Dead.png b/art/sprites/characters/Character_007_Dead.png deleted file mode 100644 index 729e554..0000000 Binary files a/art/sprites/characters/Character_007_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_007_Dead.png.import b/art/sprites/characters/Character_007_Dead.png.import deleted file mode 100644 index 1d4fa58..0000000 --- a/art/sprites/characters/Character_007_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://fvs0homlm10v" -path="res://.godot/imported/Character_007_Dead.png-5786612961751424d5bf62e4dbe77874.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_007_Dead.png" -dest_files=["res://.godot/imported/Character_007_Dead.png-5786612961751424d5bf62e4dbe77874.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/characters/Character_007_Idle.png b/art/sprites/characters/Character_007_Idle.png deleted file mode 100644 index 3e3f728..0000000 Binary files a/art/sprites/characters/Character_007_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_007_Idle.png.import b/art/sprites/characters/Character_007_Idle.png.import deleted file mode 100644 index 326b047..0000000 --- a/art/sprites/characters/Character_007_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://xc2nntg87xji" -path="res://.godot/imported/Character_007_Idle.png-0fa929882b6b9f3c123a27843c440ecd.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_007_Idle.png" -dest_files=["res://.godot/imported/Character_007_Idle.png-0fa929882b6b9f3c123a27843c440ecd.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/characters/Character_007_Walk.png b/art/sprites/characters/Character_007_Walk.png deleted file mode 100644 index 179e421..0000000 Binary files a/art/sprites/characters/Character_007_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_007_Walk.png.import b/art/sprites/characters/Character_007_Walk.png.import deleted file mode 100644 index c26bdcc..0000000 --- a/art/sprites/characters/Character_007_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dkojdn2l3jl0n" -path="res://.godot/imported/Character_007_Walk.png-34af3247b8cb2b63fd059aac5242294c.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_007_Walk.png" -dest_files=["res://.godot/imported/Character_007_Walk.png-34af3247b8cb2b63fd059aac5242294c.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/characters/Character_008_Dead.png b/art/sprites/characters/Character_008_Dead.png deleted file mode 100644 index 72cba1e..0000000 Binary files a/art/sprites/characters/Character_008_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_008_Dead.png.import b/art/sprites/characters/Character_008_Dead.png.import deleted file mode 100644 index c0ac8ed..0000000 --- a/art/sprites/characters/Character_008_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://di1brsgcgwscn" -path="res://.godot/imported/Character_008_Dead.png-fd70bff68e81ace60033e4d24809d9fe.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_008_Dead.png" -dest_files=["res://.godot/imported/Character_008_Dead.png-fd70bff68e81ace60033e4d24809d9fe.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/characters/Character_008_Idle.png b/art/sprites/characters/Character_008_Idle.png deleted file mode 100644 index 59bee34..0000000 Binary files a/art/sprites/characters/Character_008_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_008_Idle.png.import b/art/sprites/characters/Character_008_Idle.png.import deleted file mode 100644 index e816ef1..0000000 --- a/art/sprites/characters/Character_008_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cvvpak354pxb7" -path="res://.godot/imported/Character_008_Idle.png-3f55478f0d4f6dabc7b8b70c3d3a37cd.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_008_Idle.png" -dest_files=["res://.godot/imported/Character_008_Idle.png-3f55478f0d4f6dabc7b8b70c3d3a37cd.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/characters/Character_008_Walk.png b/art/sprites/characters/Character_008_Walk.png deleted file mode 100644 index 771466c..0000000 Binary files a/art/sprites/characters/Character_008_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_008_Walk.png.import b/art/sprites/characters/Character_008_Walk.png.import deleted file mode 100644 index e65739e..0000000 --- a/art/sprites/characters/Character_008_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c8stsomyh2rq7" -path="res://.godot/imported/Character_008_Walk.png-2275a9017262a05b52e927ec771e2094.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_008_Walk.png" -dest_files=["res://.godot/imported/Character_008_Walk.png-2275a9017262a05b52e927ec771e2094.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/characters/Character_009_Dead.png b/art/sprites/characters/Character_009_Dead.png deleted file mode 100644 index fa1880a..0000000 Binary files a/art/sprites/characters/Character_009_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_009_Dead.png.import b/art/sprites/characters/Character_009_Dead.png.import deleted file mode 100644 index f410750..0000000 --- a/art/sprites/characters/Character_009_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bnq8iug5dhdag" -path="res://.godot/imported/Character_009_Dead.png-72043e0166ec8cec41355a2e3a00d7bd.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_009_Dead.png" -dest_files=["res://.godot/imported/Character_009_Dead.png-72043e0166ec8cec41355a2e3a00d7bd.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/characters/Character_009_Idle.png b/art/sprites/characters/Character_009_Idle.png deleted file mode 100644 index 4fbf47e..0000000 Binary files a/art/sprites/characters/Character_009_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_009_Idle.png.import b/art/sprites/characters/Character_009_Idle.png.import deleted file mode 100644 index 27428e3..0000000 --- a/art/sprites/characters/Character_009_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dhcaxufn8awxr" -path="res://.godot/imported/Character_009_Idle.png-0ab1c95e4e42dd0240b47ad8441fa4c7.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_009_Idle.png" -dest_files=["res://.godot/imported/Character_009_Idle.png-0ab1c95e4e42dd0240b47ad8441fa4c7.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/characters/Character_009_Walk.png b/art/sprites/characters/Character_009_Walk.png deleted file mode 100644 index 2ed34cd..0000000 Binary files a/art/sprites/characters/Character_009_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_009_Walk.png.import b/art/sprites/characters/Character_009_Walk.png.import deleted file mode 100644 index ac9d72e..0000000 --- a/art/sprites/characters/Character_009_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://c6ytplnnfb3ou" -path="res://.godot/imported/Character_009_Walk.png-6cd650d24ac756f6bfd224f999d84b8f.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_009_Walk.png" -dest_files=["res://.godot/imported/Character_009_Walk.png-6cd650d24ac756f6bfd224f999d84b8f.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/characters/Character_010_Dead.png b/art/sprites/characters/Character_010_Dead.png deleted file mode 100644 index 296d4db..0000000 Binary files a/art/sprites/characters/Character_010_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_010_Dead.png.import b/art/sprites/characters/Character_010_Dead.png.import deleted file mode 100644 index 71b0b4b..0000000 --- a/art/sprites/characters/Character_010_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://wjckyu6cd8v6" -path="res://.godot/imported/Character_010_Dead.png-89846b459c078f30bee90118f78a0755.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_010_Dead.png" -dest_files=["res://.godot/imported/Character_010_Dead.png-89846b459c078f30bee90118f78a0755.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/characters/Character_010_Idle.png b/art/sprites/characters/Character_010_Idle.png deleted file mode 100644 index 878f08a..0000000 Binary files a/art/sprites/characters/Character_010_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_010_Idle.png.import b/art/sprites/characters/Character_010_Idle.png.import deleted file mode 100644 index c1fe0e0..0000000 --- a/art/sprites/characters/Character_010_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cn7ar2h01prir" -path="res://.godot/imported/Character_010_Idle.png-dbb1408fdad0da2fd24a4c1fe75ae620.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_010_Idle.png" -dest_files=["res://.godot/imported/Character_010_Idle.png-dbb1408fdad0da2fd24a4c1fe75ae620.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/characters/Character_010_Walk.png b/art/sprites/characters/Character_010_Walk.png deleted file mode 100644 index 870b589..0000000 Binary files a/art/sprites/characters/Character_010_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_010_Walk.png.import b/art/sprites/characters/Character_010_Walk.png.import deleted file mode 100644 index 80b445a..0000000 --- a/art/sprites/characters/Character_010_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dkfceshiwp5jt" -path="res://.godot/imported/Character_010_Walk.png-420aca922b9f619ed9216c39355318ab.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_010_Walk.png" -dest_files=["res://.godot/imported/Character_010_Walk.png-420aca922b9f619ed9216c39355318ab.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/characters/Character_011_Dead.png b/art/sprites/characters/Character_011_Dead.png deleted file mode 100644 index 857cb83..0000000 Binary files a/art/sprites/characters/Character_011_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_011_Dead.png.import b/art/sprites/characters/Character_011_Dead.png.import deleted file mode 100644 index 7fd918d..0000000 --- a/art/sprites/characters/Character_011_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://doa0vm7xo5f1b" -path="res://.godot/imported/Character_011_Dead.png-c52196730c14299395595c4ef3547c34.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_011_Dead.png" -dest_files=["res://.godot/imported/Character_011_Dead.png-c52196730c14299395595c4ef3547c34.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/characters/Character_011_Idle.png b/art/sprites/characters/Character_011_Idle.png deleted file mode 100644 index 62eb941..0000000 Binary files a/art/sprites/characters/Character_011_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_011_Idle.png.import b/art/sprites/characters/Character_011_Idle.png.import deleted file mode 100644 index 9756dce..0000000 --- a/art/sprites/characters/Character_011_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://dgm07ylwoxuqr" -path="res://.godot/imported/Character_011_Idle.png-079aba020f914b64188e3e11000d6489.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_011_Idle.png" -dest_files=["res://.godot/imported/Character_011_Idle.png-079aba020f914b64188e3e11000d6489.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/characters/Character_011_Walk.png b/art/sprites/characters/Character_011_Walk.png deleted file mode 100644 index d18a9ea..0000000 Binary files a/art/sprites/characters/Character_011_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_011_Walk.png.import b/art/sprites/characters/Character_011_Walk.png.import deleted file mode 100644 index 82e49f4..0000000 --- a/art/sprites/characters/Character_011_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://caxnji7qtoftv" -path="res://.godot/imported/Character_011_Walk.png-e30d6c424901068d6cb1d62875371473.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_011_Walk.png" -dest_files=["res://.godot/imported/Character_011_Walk.png-e30d6c424901068d6cb1d62875371473.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/characters/Character_012_Dead.png b/art/sprites/characters/Character_012_Dead.png deleted file mode 100644 index aed9b74..0000000 Binary files a/art/sprites/characters/Character_012_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_012_Dead.png.import b/art/sprites/characters/Character_012_Dead.png.import deleted file mode 100644 index e9e91a1..0000000 --- a/art/sprites/characters/Character_012_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://pq2m7142tryg" -path="res://.godot/imported/Character_012_Dead.png-603daed2c7a3b9483442a2b9603dea68.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_012_Dead.png" -dest_files=["res://.godot/imported/Character_012_Dead.png-603daed2c7a3b9483442a2b9603dea68.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/characters/Character_012_Idle.png b/art/sprites/characters/Character_012_Idle.png deleted file mode 100644 index d91fe7e..0000000 Binary files a/art/sprites/characters/Character_012_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_012_Idle.png.import b/art/sprites/characters/Character_012_Idle.png.import deleted file mode 100644 index 3c8e414..0000000 --- a/art/sprites/characters/Character_012_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://y6bkvxi65e7v" -path="res://.godot/imported/Character_012_Idle.png-06d2acb1777ac4e3d84cc727341dcae5.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_012_Idle.png" -dest_files=["res://.godot/imported/Character_012_Idle.png-06d2acb1777ac4e3d84cc727341dcae5.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/characters/Character_012_Walk.png b/art/sprites/characters/Character_012_Walk.png deleted file mode 100644 index da7ea22..0000000 Binary files a/art/sprites/characters/Character_012_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_012_Walk.png.import b/art/sprites/characters/Character_012_Walk.png.import deleted file mode 100644 index 22ed979..0000000 --- a/art/sprites/characters/Character_012_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://bsekmrv5pihek" -path="res://.godot/imported/Character_012_Walk.png-716625a565eca83c3c36b0419ddd28f1.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_012_Walk.png" -dest_files=["res://.godot/imported/Character_012_Walk.png-716625a565eca83c3c36b0419ddd28f1.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/characters/Character_013_Dead.png b/art/sprites/characters/Character_013_Dead.png deleted file mode 100644 index b2f06dc..0000000 Binary files a/art/sprites/characters/Character_013_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_013_Dead.png.import b/art/sprites/characters/Character_013_Dead.png.import deleted file mode 100644 index 63596dc..0000000 --- a/art/sprites/characters/Character_013_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://daukgomwycsgk" -path="res://.godot/imported/Character_013_Dead.png-49b06f46b7fa82025b0265b91dca90b2.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_013_Dead.png" -dest_files=["res://.godot/imported/Character_013_Dead.png-49b06f46b7fa82025b0265b91dca90b2.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/characters/Character_013_Idle.png b/art/sprites/characters/Character_013_Idle.png deleted file mode 100644 index 0906628..0000000 Binary files a/art/sprites/characters/Character_013_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_013_Idle.png.import b/art/sprites/characters/Character_013_Idle.png.import deleted file mode 100644 index 5cbe2bd..0000000 --- a/art/sprites/characters/Character_013_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://ddkfrmcwb11bp" -path="res://.godot/imported/Character_013_Idle.png-7a5d4c80884c699f3b7a04bcfdddcc2b.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_013_Idle.png" -dest_files=["res://.godot/imported/Character_013_Idle.png-7a5d4c80884c699f3b7a04bcfdddcc2b.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/characters/Character_013_Walk.png b/art/sprites/characters/Character_013_Walk.png deleted file mode 100644 index 692f7cd..0000000 Binary files a/art/sprites/characters/Character_013_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_013_Walk.png.import b/art/sprites/characters/Character_013_Walk.png.import deleted file mode 100644 index 58876e9..0000000 --- a/art/sprites/characters/Character_013_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://be4twnnlpy6m2" -path="res://.godot/imported/Character_013_Walk.png-03bdbadd3a5e3963af2bea56f7d34b60.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_013_Walk.png" -dest_files=["res://.godot/imported/Character_013_Walk.png-03bdbadd3a5e3963af2bea56f7d34b60.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/characters/Character_014_Dead.png b/art/sprites/characters/Character_014_Dead.png deleted file mode 100644 index d4d8ae2..0000000 Binary files a/art/sprites/characters/Character_014_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_014_Dead.png.import b/art/sprites/characters/Character_014_Dead.png.import deleted file mode 100644 index 1a34f95..0000000 --- a/art/sprites/characters/Character_014_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://cvwe1xf2f73y3" -path="res://.godot/imported/Character_014_Dead.png-23c4d06b6e3863989e695afff750ed5c.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_014_Dead.png" -dest_files=["res://.godot/imported/Character_014_Dead.png-23c4d06b6e3863989e695afff750ed5c.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/characters/Character_014_Idle.png b/art/sprites/characters/Character_014_Idle.png deleted file mode 100644 index 89b99a8..0000000 Binary files a/art/sprites/characters/Character_014_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_014_Idle.png.import b/art/sprites/characters/Character_014_Idle.png.import deleted file mode 100644 index 0395279..0000000 --- a/art/sprites/characters/Character_014_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://b777g0yu7owor" -path="res://.godot/imported/Character_014_Idle.png-8314784856576367ed93bcb8e4cc27aa.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_014_Idle.png" -dest_files=["res://.godot/imported/Character_014_Idle.png-8314784856576367ed93bcb8e4cc27aa.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/characters/Character_014_Walk.png b/art/sprites/characters/Character_014_Walk.png deleted file mode 100644 index d12c507..0000000 Binary files a/art/sprites/characters/Character_014_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_014_Walk.png.import b/art/sprites/characters/Character_014_Walk.png.import deleted file mode 100644 index 7121bd1..0000000 --- a/art/sprites/characters/Character_014_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://vpdry2yyekvo" -path="res://.godot/imported/Character_014_Walk.png-bab0cfac7c66a94dcc7be5d5366bb948.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_014_Walk.png" -dest_files=["res://.godot/imported/Character_014_Walk.png-bab0cfac7c66a94dcc7be5d5366bb948.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/characters/Character_015_Dead.png b/art/sprites/characters/Character_015_Dead.png deleted file mode 100644 index da5abe1..0000000 Binary files a/art/sprites/characters/Character_015_Dead.png and /dev/null differ diff --git a/art/sprites/characters/Character_015_Dead.png.import b/art/sprites/characters/Character_015_Dead.png.import deleted file mode 100644 index 42fc6aa..0000000 --- a/art/sprites/characters/Character_015_Dead.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://n71lbmxkvioh" -path="res://.godot/imported/Character_015_Dead.png-b074949bcb971d96a392e2128c495ea5.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_015_Dead.png" -dest_files=["res://.godot/imported/Character_015_Dead.png-b074949bcb971d96a392e2128c495ea5.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/characters/Character_015_Idle.png b/art/sprites/characters/Character_015_Idle.png deleted file mode 100644 index f099ff3..0000000 Binary files a/art/sprites/characters/Character_015_Idle.png and /dev/null differ diff --git a/art/sprites/characters/Character_015_Idle.png.import b/art/sprites/characters/Character_015_Idle.png.import deleted file mode 100644 index 0005f2c..0000000 --- a/art/sprites/characters/Character_015_Idle.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://26c1bs1sbswi" -path="res://.godot/imported/Character_015_Idle.png-60b87cac6c8d2f27723308ffa4335d7d.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_015_Idle.png" -dest_files=["res://.godot/imported/Character_015_Idle.png-60b87cac6c8d2f27723308ffa4335d7d.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/characters/Character_015_Walk.png b/art/sprites/characters/Character_015_Walk.png deleted file mode 100644 index 5cccaf0..0000000 Binary files a/art/sprites/characters/Character_015_Walk.png and /dev/null differ diff --git a/art/sprites/characters/Character_015_Walk.png.import b/art/sprites/characters/Character_015_Walk.png.import deleted file mode 100644 index 7f58cbf..0000000 --- a/art/sprites/characters/Character_015_Walk.png.import +++ /dev/null @@ -1,40 +0,0 @@ -[remap] - -importer="texture" -type="CompressedTexture2D" -uid="uid://by474ej7lcv2q" -path="res://.godot/imported/Character_015_Walk.png-460f8657e875d12969b60f14c3147049.ctex" -metadata={ -"vram_texture": false -} - -[deps] - -source_file="res://art/sprites/characters/Character_015_Walk.png" -dest_files=["res://.godot/imported/Character_015_Walk.png-460f8657e875d12969b60f14c3147049.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/autoload/event_bus.gd b/autoload/event_bus.gd index 81de2c8..dc9a8b2 100644 --- a/autoload/event_bus.gd +++ b/autoload/event_bus.gd @@ -55,8 +55,6 @@ signal load_finished(slot: StringName, ok: bool, real_seconds_away: int) ## Emit # Phase 17 — Touch UX completion. signal pawn_selected(pawn) ## Emitted when Selection picks a pawn — opens PawnDetailPanel. signal pawn_deselected ## Emitted when Selection clears — closes PawnDetailPanel. -signal workbench_selected(workbench) -signal workbench_deselected signal pawn_priority_changed(pawn, category: StringName, level: int) ## Emitted when priority matrix updates a cell. signal alert_added(severity: StringName, text: String, focus_tile: Vector2i) ## Emitted by gameplay subsystems to surface a player notice. severity = info | warn | danger. signal request_wolf_spawn(count: int) ## Phase 15 EventCatalog → WolfSpawner. Decouples threat-event effects from spawner. diff --git a/autoload/save_system.gd b/autoload/save_system.gd index 6d134ce..115d730 100644 --- a/autoload/save_system.gd +++ b/autoload/save_system.gd @@ -29,7 +29,6 @@ const _PAWN_SCENE: PackedScene = preload("res://scenes/pawn/pawn.tscn") const _TREE_SCENE: PackedScene = preload("res://scenes/entities/tree.tscn") const _ROCK_SCENE: PackedScene = preload("res://scenes/entities/rock.tscn") const _BIG_ROCK_SCENE: PackedScene = preload("res://scenes/entities/big_rock.tscn") -const _BIG_ROCK_NODE_SCENE: PackedScene = preload("res://scenes/entities/big_rock_node.tscn") const _ITEM_SCENE: PackedScene = preload("res://scenes/entities/item.tscn") const _WALL_SCENE: PackedScene = preload("res://scenes/entities/wall.tscn") const _FLOOR_SCENE: PackedScene = preload("res://scenes/entities/floor.tscn") @@ -277,7 +276,6 @@ func _collect_entities() -> Array: var registries: Array = [ World.trees, World.rocks, - World.big_rock_nodes, World.items, World.build_queue, # ghost walls / floors / doors / grave_slots World.doors, @@ -318,7 +316,6 @@ const _SPAWN_PRIORITY: Dictionary = { &"tree": 0, &"rock": 0, &"big_rock": 0, - &"big_rock_node": 0, &"wall": 0, &"floor": 0, &"door": 1, @@ -354,7 +351,6 @@ func _register_factories() -> void: _factories[&"tree"] = _spawn_tree _factories[&"rock"] = _spawn_rock _factories[&"big_rock"] = _spawn_big_rock - _factories[&"big_rock_node"] = _spawn_big_rock_node _factories[&"item"] = _spawn_item _factories[&"wall"] = _spawn_wall _factories[&"floor"] = _spawn_floor @@ -381,18 +377,8 @@ func _register_factories() -> void: func _spawn_tree(world_scene: Node, d: Dictionary) -> void: var ent = _TREE_SCENE.instantiate() world_scene.add_child(ent) - # Pass growth_stage to setup() so _refresh_sprite() picks the right visual. - # Default 3 (STAGE_MATURE) so pre-growth-system saves load as mature trees. - var gs: int = int(d.get("growth_stage", 3)) - ent.setup(Vector2i(int(d.get("tile_x", 0)), int(d.get("tile_y", 0))), gs) + ent.setup(Vector2i(int(d.get("tile_x", 0)), int(d.get("tile_y", 0)))) ent.chop_progress = int(d.get("chop_progress", 0)) - ent.growth_progress = int(d.get("growth_progress", 0)) - ent.chop_designated = bool(d.get("chop_designated", false)) - ent.pending_plant = bool(d.get("pending_plant", false)) - ent._plant_progress = int(d.get("plant_progress", 0)) - # Re-register as build site if planting is still in progress. - if ent.pending_plant: - World.register_build_site(ent) ent.queue_redraw() @@ -412,14 +398,6 @@ func _spawn_big_rock(world_scene: Node, d: Dictionary) -> void: ent.queue_redraw() -func _spawn_big_rock_node(world_scene: Node, d: Dictionary) -> void: - var ent = _BIG_ROCK_NODE_SCENE.instantiate() - world_scene.add_child(ent) - ent.setup(Vector2i(int(d.get("tile_x", 0)), int(d.get("tile_y", 0)))) - ent.is_quarry_site = bool(d.get("is_quarry_site", false)) - ent.queue_redraw() - - func _spawn_item(world_scene: Node, d: Dictionary) -> void: var ent = _ITEM_SCENE.instantiate() world_scene.add_child(ent) @@ -429,8 +407,6 @@ func _spawn_item(world_scene: Node, d: Dictionary) -> void: Vector2i(int(d.get("tile_x", 0)), int(d.get("tile_y", 0))) ) ent.quality = int(d.get("quality", 1)) as Item.Quality - ent.subtype = StringName(d.get("subtype", "")) - ent.queue_redraw() func _spawn_wall(world_scene: Node, d: Dictionary) -> void: diff --git a/autoload/strings.gd b/autoload/strings.gd index a96fe0e..c67813c 100644 --- a/autoload/strings.gd +++ b/autoload/strings.gd @@ -194,27 +194,8 @@ const TABLE: Dictionary = { &"tool.workbench_millstone": "Millstone", &"tool.workbench_hearth": "Hearth", &"tool.workbench_cremation_pyre": "Cremation Pyre", - &"tool.paint_quarry": "Build quarry", &"tool.stockpile_general": "Stockpile", &"tool.graveyard": "Graveyard", - &"tool.plant_tree": "Plant tree", - # Tree growth stage names (shown in inspect tooltip). - &"tree.stage.sapling": "Sapling", - &"tree.stage.young": "Young tree", - &"tree.stage.growing": "Growing tree", - &"tree.stage.mature": "Mature tree", - &"ui.bill.mode_forever": "Forever", - &"ui.bill.mode_count": "Do X times", - &"ui.bill.mode_until_n": "Do until X", - &"ui.bill.target": "Target", - &"ui.bill.until_count": "Until count", - &"ui.bill.completed": "Done", - &"ui.bill.pause": "Pause", - &"ui.bill.remove": "Remove", - &"ui.bill.add_button": "Add bill", - &"ui.bill.no_bills_hint": "No bills. Add one to start crafting.", - &"ui.workbench.current_bill": "Current", - &"ui.workbench.idle": "Idle", } diff --git a/autoload/world.gd b/autoload/world.gd index c85dc3c..9169dac 100644 --- a/autoload/world.gd +++ b/autoload/world.gd @@ -19,7 +19,6 @@ var work_providers: Array = [] # from their _ready/_exit_tree. Phase 16 will add stable IDs and persistence wiring. var trees: Array = [] # Array of Tree var rocks: Array = [] # Array of Rock -var big_rock_nodes: Array = [] # Array of BigRockNode (permanent stone outcrops) var items: Array = [] # Array of Item (on-floor stacks) var stockpiles: Array = [] # Array of StorageDestination (StockpileZone for now; containers Phase 5) @@ -125,15 +124,6 @@ func pawn_at_tile(tile: Vector2i) -> Pawn: return null -## Returns the Workbench occupying `tile`, or null if none. Used by Selection -## to route taps on a workbench to the bill-editor panel. -func workbench_at_tile(tile: Vector2i): - for w in workbenches: - if w.tile == tile: - return w - return null - - func clear_pawns() -> void: # For save-load / new-game flow in Phase 16. pawns.clear() @@ -208,24 +198,6 @@ func unregister_rock(r) -> void: rocks.erase(r) -func register_big_rock_node(n) -> void: - if not big_rock_nodes.has(n): - big_rock_nodes.append(n) - - -func unregister_big_rock_node(n) -> void: - big_rock_nodes.erase(n) - - -## Returns the BigRockNode whose 2×2 footprint covers `tile`, or null. -## Used by `paint_quarry` designation to validate the build site. -func big_rock_node_at_tile(tile: Vector2i): - for n in big_rock_nodes: - if n.is_at(tile): - return n - return null - - func register_item(it) -> void: if items.has(it): return diff --git a/docs/implementation.md b/docs/implementation.md index 1600fe9..6c06643 100644 --- a/docs/implementation.md +++ b/docs/implementation.md @@ -395,7 +395,7 @@ The five items from `memory.md` *Open questions / Audit*. None of these need cod - [x] **Day-summary emission** — Clock emits `day_ended(summary)` at dusk→night with day/weather/season/pawns_alive/tension/wolves_alive recap. AlertsLog surfaces as a single-line entry per day. Full DaySummaryCard UI deferred. - [ ] Per-pawn / per-job views layered on the matrix — deferred (current matrix has read-write only, no view layers). - [ ] Stockpile / container 4×4 chip grid UI — deferred (paint creates 1×1 zones today; filter UI is data-only). -- [x] **Bill UI for workbenches** (shipped 2026-05-16, out-of-phase). `WorkbenchPanel` bottom-sheet mirrors PawnDetailPanel (layer 18, right-anchored 360 px); tap a workbench → bill rows with mode toggle (FOREVER/COUNT/UNTIL_N), target SpinBox, pause CheckBox, remove button; Add-bill popup filters `RecipeCatalog.all()` by `accepted_skill`. Selection chain extended: pawn wins over workbench on shared tile, mutual-exclusion via `EventBus.workbench_selected/deselected`. Closes the Phase 6 stub — player-built workbenches are now configurable. +- [ ] Bill UI for workbenches — deferred (Phase 6 stub still in use; add/edit bills programmatically only). - [ ] "No stockpile accepts X" + "Bill blocked" alerts — wiring stubs ready; emit calls in HaulingProvider / CraftingProvider deferred. - [ ] Day-summary card UI — deferred (signal emits; visual card is Phase 17.5). - [x] **Acceptance:** Hand-test verified — TopBar shows Save/Load/Settings/Build/Work/Log[N]; tap Bram → right panel shows all his state; tap Build → bottom drawer with 4 tabs; tap Work → grid of pawn priorities; tap Log → scrollable alerts list including the Spring Awakens storyteller event from boot. All UIs touch-friendly (48×48+ targets). Screenshots captured for all 4 surfaces. diff --git a/memory.md b/memory.md index ec0ba97..a40bf4c 100644 --- a/memory.md +++ b/memory.md @@ -293,23 +293,6 @@ Same scope as locked in `~/claude/ideas/rimlike/plan.md`. Realistic timeline 3 - **Pattern recorded — ".import auto-generation via `godot --headless --import`".** Writing `*.png.import` companion files by hand with placeholder paths works but Godot rewrites them with proper UIDs on the first import scan. Reliable workflow: copy PNG into `art/`, write minimal .import (UID will be ignored), then run `/mnt/d/godot/Godot_v4.6.2-stable_win64_console.exe --headless --import` once to generate the .ctex binaries. Headless boot fails until the import scan runs. Same pattern from 2026-05-12 confirmed. - Delegation report (whole-day 05-15 polish sprint): **No delegation — all on Opus.** Sprite-atlas surveys, MCP runtime verification (camera positioning, dialog dismissal, stage forcing), and the iterative play→fix→play loop kept the same files hot in context; Haiku/Sonnet handoffs would have re-read every time. The `researcher` (Haiku) dispatch from earlier in the day for the job-system audit is the only delegated work. -- **Workbenches rebuilt procedural after second atlas-misidentification.** The 2026-05-12 atlas-sprite pass for Carpenter/Smelter/Hearth/Millstone again hit the same trap as Bed: the picked tiles read as chest-of-drawers (Carpenter, FG_Interior 24,20), candle base (Smelter, FG_Marketplace 8,30), 2-burner stove (Hearth, FG_Interior 16,32), and cushion stack (Millstone, FG_Interior 17,40). User: "the carpenter is a chest of draws? no idea what the mill is, looks like a bucket". Survey of all 7 plausible source tilesets (Interior / Marketplace / Houses / Village / Fortress / Mines / Dark Castle / Extras) found a 2×2 medieval stone fireplace at FG_Interior (12,13)..(13,14) for Hearth but **2-wide tiles don't fit the current single-column sprite system**, and the other three had no clean replacement. Decision: go procedural for all four, following `CremationPyre._draw_pyre` as precedent. New methods `_draw_carpenter / _draw_smelter / _draw_hearth / _draw_millstone` in workbench.gd; `_VARIANT_SPRITES` table + `_build_sprite()` Sprite2D path removed; `_complete()` no longer touches a sprite child. Hearth is the only h=2 variant — draws stone surround + mantle + arched opening + log fire + flame teardrop extending into the tile north (Y-sort handles occlusion). Commit `c97ada8`. -- **Pattern recorded — "atlas-misidentification trap, second occurrence — prefer procedural for named furniture".** Pixel-art tile atlases pack hundreds of squat 16×16 items at low resolution; without zoomed-in inspection it's easy to mistake a furniture silhouette for what its name implies. Both Bed (2026-05-12) and the four workbenches (2026-05-15) hit this failure mode. Going forward: for furniture whose silhouette is **definitional** (workbench-with-tools, bed-with-pillow, millstone-as-grindstone), **start with procedural draws** and only swap in an atlas tile after verifying via 16× zoom that the tile actually depicts the named object. Atlas-sourced sprites are still the right call for generic decorative variation (trees, walls, floors, crops with growth stages). -- **Tree variety added via 3-season palette swap.** Copied `FG_Tree_Summer.png` + `FG_Tree_Fall.png` from the Grasslands tier-1 pack into `art/sprites/` (Spring was already there). 4 silhouettes × 3 palettes = 12 visual variants; `tree.gd` hash mixes silhouette and season independently so neighbouring tiles don't all share the same palette. Winter omitted — snowy trees would look out of place in the current biome. When a season-cycle system lands (future phase), the active texture can be swapped globally by season instead of per-tree. No growth stage / sapling system yet — bundle has no growth frames for trees, and the user's "regrow / auto-grow" ask is explicit future scope (deferred to a later phase that also covers planting designation). Commit `c97ada8`. - -### 2026-05-16 -- **Pre-made SW stockpiles + crates removed** so items sit where they're produced until the player paints storage (matches Rimworld parity). Deleted `_spawn_sample_stockpiles()` (two zones at (15,55) and (15,62)) and the two SW haul-target crates at (17,60)/(18,60). Kept the interior cabin crate at (50,23) as a starting amenity. Bills' `UNTIL_N` mode counts items anywhere in world (not just stockpiles) so the Smelter's "5 stone blocks" bill still terminates. All `World.stockpiles` consumers iterate the list — empty-list no-op, no crashes. Commit `c81e817`. -- **Workbench Bill Editor plan drafted** for the deferred Phase 17 item ("Bill UI for workbenches"). 10-step plan saved to user-memory at `~/.claude/projects/-mnt-d-godot-rimlike/memory/plan_bill_ui.md` (resilient to fresh sessions). Without this UI, every player-built workbench is functionally dead — build drawer can paint them but bills can only be set in code. Recipe.id field existence needs verification in Step 4. Starting execution 2026-05-16. -- **Workbench Bill Editor shipped same day** (commit `bdd4352`). Tap a workbench → right-side panel with bill rows (mode toggle, target spin, pause, remove) + Add-bill popup filtered by `accepted_skill`. Mirrors `PawnDetailPanel` exactly: layer 18, right-anchored 360 px, procedural `_build_ui()`, sim_tick refresh of the current-bill status only (bill list rebuilt on add/remove/select to preserve scroll). Selection chain extended in `selection.gd`: pawn-first then workbench, mutual-exclusion via new `EventBus.workbench_selected/deselected` signals. Closes Phase 17 deferred item. Code-level verification: headless boot clean, no runtime errors. Visual MCP verification still pending (need editor running). -- **Delegation report — bill UI sprint.** Steps 1, 3, 4, 6 (~5 mechanical edits across 5 files) → `quick-edit` (Haiku, 1 dispatch). Step 5 (new ~432-LOC WorkbenchPanel script needing full mirror of PawnDetailPanel + 6 source files) → `gdscript-refactor` (Sonnet, 1 dispatch). Steps 2, 7 (selection chain + main mount, needed design judgment about mutual-exclusion and main.gd's typed-var pattern) handled on Opus. Strings table follow-up (`ui.bill.until_count` key missed by Sonnet, fallback was a hardcoded literal) caught and fixed on Opus before commit. -- **Pattern recorded — "main.gd typed-var pattern requires CanvasLayer.new() + set_script(), not SCRIPT.new()".** First mount attempt used `WORKBENCH_PANEL_SCRIPT.new()` — Godot 4's parser refused with "Cannot infer the type" because `Script.new()` returns generic `Object`. Switched to `var x := CanvasLayer.new(); x.set_script(SCRIPT)` matching the rest of main.gd. Cheap parse error to surface via `--headless --quit`, but worth noting: subagents writing UI mount glue need this idiom explicitly. -- **Pattern recorded — "never free a widget from within its own signal callback".** Bill editor crashed when user changed mode FOREVER → UNTIL_N. Root cause: OptionButton.item_selected lambda called `_populate_bills()` directly, which clears + rebuilds all bill rows — freeing the very OptionButton whose signal was still emitting. Same pattern in the Remove button. Fix: `call_deferred("_populate_bills")` so the rebuild runs on the next idle frame after the signal frame completes. Commit `4e09dea`. Applies to any UI where a child Control's signal handler mutates a parent container — always defer rebuilds. - -- **Pawn reskin Slice 1 shipped** (commit `b4c9541`). Pawns now render as AnimatedSprite2D children sourced from ElvGames Farming Characters Pack (Pack 1, chars 001-015). 9 anims per pawn: idle×4dirs + walk×4dirs + dead. Atlas pick is `(absi(pawn_name.hash()) % 15) + 1` — Bram=004, Cora=013, Edda=001, all visually distinct. New `_atlas_for_pawn(pawn)` helper is the **single extension point** for the future Slice 2 (armor → atlas swap when equipment+combat phase ships). Slice 1 plan archived to user-memory at `~/.claude/projects/-mnt-d-godot-rimlike/memory/plan_pawn_reskin_slice1.md`. 45 PNGs + .import companions copied to `art/sprites/characters/`. New `scenes/pawn/pawn_sprite_frames.gd` builds the SpriteFrames from a {idle, walk, dead} atlas trio. Code-level verification clean; visual MCP verification still pending (needs editor running). -- **Pattern recorded — "class_name lookups aren't reliable during cold-start parse; use preload() for sibling scripts".** First attempt referenced `PawnSpriteFrames.build()` in pawn.gd via the global class_name registry. Headless parse failed with "Identifier 'PawnSpriteFrames' not declared in the current scope" because pawn.gd was loaded before pawn_sprite_frames.gd registered its class_name globally. Fix: `const _PAWN_SPRITE_FRAMES = preload("res://scenes/pawn/pawn_sprite_frames.gd")` at the top of pawn.gd, then call `_PAWN_SPRITE_FRAMES.build(atlases)`. Class_name is fine for public API documentation; preload is the reliable runtime resolver. -- **Pattern recorded — "deferred-init data must be wired AFTER setup(), not in _ready()".** First sprite mount happened in `_ready()` and read `pawn_name` — but pawn_name is empty at _ready time (assigned later in setup()). All three pawns got atlas idx 1 (hash of empty string). Fix: moved sprite mount to `_mount_sprite()` called from setup() AND from_dict(), both of which assign pawn_name first. Idempotent (frees prior sprite). Same shape will recur for any future render that depends on per-instance saved state — always check whether the data the renderer reads is available at _ready vs after setup/from_dict. -- **Delegation report — pawn reskin Slice 1.** `quick-edit` (Haiku, 1 dispatch) handled the mechanical edits across pawn.gd (facing field, save/load, `_canonical_facing`, `_atlas_for_pawn`). `gdscript-refactor` (Sonnet, 1 dispatch) wrote the new `pawn_sprite_frames.gd` helper (~50 LOC) and wired the AnimatedSprite2D into Pawn._ready (z_index, anim switching, _facing_suffix). Opus handled the asset copy + headless --import, the two parse/runtime fixes (preload + setup-not-ready), the hash-distribution audit, and the commit. The two patterns above were caught on Opus during verification. - ## External references - **Forgejo repo:** https://git.rdx4.com/megaproxy/rimlike (private) diff --git a/scenes/ai/crafting_provider.gd b/scenes/ai/crafting_provider.gd index fc59865..dcab18d 100644 --- a/scenes/ai/crafting_provider.gd +++ b/scenes/ai/crafting_provider.gd @@ -69,24 +69,14 @@ func find_best_for(pawn) -> Job: _emit_bill_blocked(b.recipe.label, &"skill_too_low", wb) continue - # If ingredient_count is 0, no ingredient is required; proceed directly. - # Otherwise, confirm a qualifying ingredient exists on the floor. - var src = null - if b.recipe.ingredient_count > 0: - src = _find_ingredient_item(b.recipe.ingredient_type) - if src == null: - _emit_bill_blocked(b.recipe.label, &"missing_ingredient", wb) - continue - - # Score: total Manhattan travel distance. - # If no ingredient (count==0), distance is just pawn → workbench. - # Otherwise, distance is pawn → ingredient → workbench. - var d: int - if b.recipe.ingredient_count > 0: - d = _manhattan(pawn.tile, src.tile) + _manhattan(src.tile, wb.tile) - else: - d = _manhattan(pawn.tile, wb.tile) + # Confirm a qualifying ingredient exists on the floor. + var src = _find_ingredient_item(b.recipe.ingredient_type) + if src == null: + _emit_bill_blocked(b.recipe.label, &"missing_ingredient", wb) + continue + # Score: total Manhattan travel distance pawn → ingredient → workbench. + var d: int = _manhattan(pawn.tile, src.tile) + _manhattan(src.tile, wb.tile) if d < best_dist: best_dist = d best_wb = wb @@ -97,22 +87,16 @@ func find_best_for(pawn) -> Job: if best_wb == null: return null - var src_item = null - # If ingredient_count > 0, re-resolve the source item in case multiple bills tied on the same item. - if best_bill.recipe.ingredient_count > 0: - src_item = _find_ingredient_item(best_bill.recipe.ingredient_type) - if src_item == null: - return null + # Re-resolve the source item in case multiple bills tied on the same item. + var src_item = _find_ingredient_item(best_bill.recipe.ingredient_type) + if src_item == null: + return null var j := Job.new() j.label = "Craft %s at %s" % [best_bill.recipe.label, best_wb.get("label_text") if best_wb.get("label_text") != null else "workbench"] j.target_node = best_wb - - # Only add ingredient-haul toils if ingredient is required. - if best_bill.recipe.ingredient_count > 0: - j.toils.append(Toil.walk_to(src_item.tile)) - j.toils.append(Toil.pickup()) - + j.toils.append(Toil.walk_to(src_item.tile)) + j.toils.append(Toil.pickup()) j.toils.append(Toil.walk_to(best_wb.tile)) j.toils.append(Toil.craft_at(best_wb.get_path(), best_bill_index)) return j diff --git a/scenes/ai/recipe.gd b/scenes/ai/recipe.gd index 5f7ebc8..8571b38 100644 --- a/scenes/ai/recipe.gd +++ b/scenes/ai/recipe.gd @@ -19,9 +19,6 @@ var id: StringName = &"" ## Item type consumed by this recipe (single-ingredient for Phase 6). var ingredient_type: StringName = &"" -## Count of ingredient_type required by this recipe. 0 = no ingredient (work only). -var ingredient_count: int = 0 - ## Phase 14 — optional secondary ingredient. Empty string = no secondary. ## CraftingProvider Phase 14 follow-up: enforce pickup of ingredient2 before ## assigning a pawn to this bill (currently stub — only ingredient_type enforced). @@ -49,20 +46,10 @@ var label: String = "" # ── save / load ─────────────────────────────────────────────────────────────── -## Player-visible display name for this recipe. Used by the workbench bill -## editor's recipe-picker and the bill list. Falls back to `id` if `label` -## is empty (shouldn't happen for catalog recipes, but defensive). -func display_name() -> String: - if label.is_empty(): - return str(id) - return label - - func to_dict() -> Dictionary: return { "id": String(id), "ingredient_type": String(ingredient_type), - "ingredient_count": ingredient_count, "ingredient2_type": String(ingredient2_type), "ingredient2_count": ingredient2_count, "output_type": String(output_type), @@ -77,7 +64,6 @@ static func from_dict(d: Dictionary) -> Recipe: var r := Recipe.new() r.id = StringName(d.get("id", "")) r.ingredient_type = StringName(d.get("ingredient_type", "")) - r.ingredient_count = int(d.get("ingredient_count", 0)) r.ingredient2_type = StringName(d.get("ingredient2_type", "")) r.ingredient2_count = int(d.get("ingredient2_count", 0)) r.output_type = StringName(d.get("output_type", "")) diff --git a/scenes/ai/recipe_catalog.gd b/scenes/ai/recipe_catalog.gd index 24d84e3..0645c3a 100644 --- a/scenes/ai/recipe_catalog.gd +++ b/scenes/ai/recipe_catalog.gd @@ -109,33 +109,3 @@ static func cremate_corpse() -> Recipe: r.required_skill = &"manual_labor" r.skill_threshold = 0 return r - - -## Quarry workbench — no input, produces 1 stone per work cycle. Used by the -## QuarryWorkbench placed on a BigRockNode for renewable stone supply. -static func quarry_stone() -> Recipe: - var r := Recipe.new() - r.id = &"quarry_stone" - r.label = "Quarry stone" - r.ingredient_type = &"" # no input - r.ingredient_count = 0 - r.output_type = Item.TYPE_STONE - r.work_ticks = 300 - r.required_skill = &"manual_labor" - r.skill_threshold = 0 - return r - - -## Returns one fresh instance of every recipe in the catalog. Used by UI -## recipe-pickers to enumerate available bills; callers filter by -## `recipe.required_skill` against the workbench's `accepted_skill`. -static func all() -> Array[Recipe]: - return [ - plank(), - stone_block(), - flour(), - bread(), - meal_from_vegetables(), - cremate_corpse(), - quarry_stone(), - ] diff --git a/scenes/entities/big_rock_node.gd b/scenes/entities/big_rock_node.gd deleted file mode 100644 index 3ff6da3..0000000 --- a/scenes/entities/big_rock_node.gd +++ /dev/null @@ -1,140 +0,0 @@ -class_name BigRockNode extends Node2D -## Permanent stone outcrop — a 2×2 immovable boulder that never depletes. -## -## BigRockNode marks its footprint impassable in the pathfinder and registers -## itself with World so the designation system can locate it. A QuarryWorkbench -## can be built on one of its tiles; external code sets is_quarry_site = true -## and quarry_workbench once construction completes. -## -## Draw convention: position is stamped at the TOP-LEFT pixel corner of the -## 2×2 footprint (tile * TILE_SIZE_PX). The visual centre of the 32×32 area -## sits at local (16, 16), so all _draw_* helpers are centred there. -## -## Save/load: to_dict / from_dict round-trip tile + is_quarry_site. -## The quarry_workbench reference is an entity pointer reconstructed by the -## save layer (SaveSystem wires it after both entities are spawned). - -const TILE_SIZE_PX: int = 16 - -## Top-left tile of the 2×2 footprint. -var tile: Vector2i = Vector2i.ZERO - -## Footprint size in tiles (always 2×2; declared as a var so external code can -## read it uniformly without special-casing BigRockNode vs hypothetical future nodes). -var footprint: Vector2i = Vector2i(2, 2) - -## True once a Quarry workbench has been completed on this outcrop. -## Flipped by external code (designation / world.gd) — this file only declares it. -var is_quarry_site: bool = false - -## The QuarryWorkbench that sits on this node after construction. -## null until assigned by the designation completion handler. -var quarry_workbench = null - - -# ── lifecycle ───────────────────────────────────────────────────────────────── - -func _ready() -> void: - # Position at top-left pixel corner so footprint_tiles() aligns with world coords. - position = Vector2(tile.x * TILE_SIZE_PX, tile.y * TILE_SIZE_PX) - # Block pathfinding on every tile in the footprint. - for t in footprint_tiles(): - if World.pathfinder != null: - World.pathfinder.set_cell_walkable(t, false) - World.register_big_rock_node(self) - queue_redraw() - - -func _exit_tree() -> void: - World.unregister_big_rock_node(self) - - -# ── public API ──────────────────────────────────────────────────────────────── - -## One-shot initialiser. Call after add_child() so _ready() has fired. -func setup(p_tile: Vector2i) -> void: - tile = p_tile - position = Vector2(tile.x * TILE_SIZE_PX, tile.y * TILE_SIZE_PX) - queue_redraw() - Audit.log("big_rock_node", "spawned at tile %s" % tile) - - -## Returns the four tiles covered by this node's 2×2 footprint. -## Used by designation, save/load, and inspection code. -func footprint_tiles() -> Array[Vector2i]: - return [ - tile, - tile + Vector2i(1, 0), - tile + Vector2i(0, 1), - tile + Vector2i(1, 1), - ] - - -## True when tile_to_check falls inside the 2×2 footprint. -func is_at(tile_to_check: Vector2i) -> bool: - return ( - tile_to_check.x >= tile.x and tile_to_check.x < tile.x + footprint.x - and tile_to_check.y >= tile.y and tile_to_check.y < tile.y + footprint.y - ) - - -# ── save / load ─────────────────────────────────────────────────────────────── - -func to_dict() -> Dictionary: - return { - "class_id": &"big_rock_node", - "tile_x": tile.x, - "tile_y": tile.y, - "is_quarry_site": is_quarry_site, - } - - -## Restore from a dict produced by to_dict(). quarry_workbench is reconnected -## by the save layer after both entities are spawned. -static func from_dict(d: Dictionary) -> Dictionary: - return { - "tile_x": int(d.get("tile_x", 0)), - "tile_y": int(d.get("tile_y", 0)), - "is_quarry_site": bool(d.get("is_quarry_site", false)), - } - - -# ── render ──────────────────────────────────────────────────────────────────── - -## Draw a procedural pile of grey rocks centred at local (16, 16) — -## the geometric centre of the 32×32 footprint area. -## Three layers create depth: large base blob → medium mid rock → small cap. -func _draw() -> void: - var cx: float = 16.0 - var cy: float = 16.0 - - # Colour palette — warm grey tones suggesting weathered granite. - var base_fill := Color(0.60, 0.58, 0.55) # large base ellipse - var mid_fill := Color(0.45, 0.44, 0.42) # medium middle rock - var top_fill := Color(0.55, 0.53, 0.50) # small perched cap - var outline_col := Color(0.20, 0.18, 0.16, 0.70) # subtle dark rim - - # Bottom: large flattened blob — widest at the base to read as a ground-hugging mass. - var base_w: float = 24.0 - var base_h: float = 16.0 - var base_rect := Rect2(Vector2(cx - base_w / 2.0, cy - base_h / 2.0 + 2.0), Vector2(base_w, base_h)) - draw_rect(base_rect, base_fill) - - # Dark outline arc around the base blob. - draw_rect(base_rect, outline_col, false, 1.0) - - # Middle: slightly elevated, narrower rock sitting on top of the base. - var mid_w: float = 16.0 - var mid_h: float = 12.0 - var mid_oy: float = -4.0 # shift up from centre - var mid_rect := Rect2(Vector2(cx - mid_w / 2.0, cy - mid_h / 2.0 + mid_oy), Vector2(mid_w, mid_h)) - draw_rect(mid_rect, mid_fill) - draw_rect(mid_rect, outline_col, false, 1.0) - - # Top: small cap perched on the very top. - var top_w: float = 8.0 - var top_h: float = 6.0 - var top_oy: float = -10.0 # above the mid rock - var top_rect := Rect2(Vector2(cx - top_w / 2.0, cy - top_h / 2.0 + top_oy), Vector2(top_w, top_h)) - draw_rect(top_rect, top_fill) - draw_rect(top_rect, outline_col, false, 1.0) diff --git a/scenes/entities/big_rock_node.gd.uid b/scenes/entities/big_rock_node.gd.uid deleted file mode 100644 index af363c5..0000000 --- a/scenes/entities/big_rock_node.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bhn0lknhgn1od diff --git a/scenes/entities/big_rock_node.tscn b/scenes/entities/big_rock_node.tscn deleted file mode 100644 index f93f19f..0000000 --- a/scenes/entities/big_rock_node.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://big_rock_node_entity"] - -[ext_resource type="Script" path="res://scenes/entities/big_rock_node.gd" id="1_big_rock_node"] - -[node name="BigRockNode" type="Node2D"] -script = ExtResource("1_big_rock_node") diff --git a/scenes/entities/crop.gd b/scenes/entities/crop.gd index 494d585..f47ed74 100644 --- a/scenes/entities/crop.gd +++ b/scenes/entities/crop.gd @@ -109,11 +109,6 @@ func on_harvest_tick() -> void: var it: Item = ITEM_SCENE.instantiate() get_parent().add_child(it) it.setup(item_type, 1, tile) - # Carry the crop_kind through as a visual subtype so wheat / corn / - # potato / strawberry each render distinctly, while item_type stays - # generic (TYPE_GRAIN / TYPE_VEGETABLE) for stockpile filter purposes. - it.subtype = crop_kind - it.queue_redraw() stage = Stage.TILLED stage_progress = 0 _refresh_sprite_region() diff --git a/scenes/entities/item.gd b/scenes/entities/item.gd index 465d151..e54cdce 100644 --- a/scenes/entities/item.gd +++ b/scenes/entities/item.gd @@ -84,13 +84,6 @@ enum Quality { SHODDY, NORMAL, EXCELLENT, MASTERWORK, LEGENDARY } @export var stack_size: int = 1 @export var quality: Quality = Quality.NORMAL -## Visual subtype within the broader item_type. Lets multiple harvested produce -## share one storage-filter category (TYPE_GRAIN matches both wheat AND corn) -## while still rendering distinctly. Empty string = use item_type's default -## shape. Set by the spawning code (Crop.on_harvest_tick assigns "wheat"/"corn" -## /"potato"/"strawberry" based on crop_kind). -@export var subtype: StringName = &"" - var tile: Vector2i = Vector2i.ZERO ## When true the on-floor visual is suppressed; the carrying pawn renders the @@ -161,7 +154,6 @@ func to_dict() -> Dictionary: return { "class_id": &"item", "type": String(item_type), - "subtype": String(subtype), "stack_size": stack_size, "tile_x": tile.x, "tile_y": tile.y, @@ -175,7 +167,6 @@ func to_dict() -> Dictionary: static func from_dict(d: Dictionary) -> Dictionary: return { "type": StringName(d.get("type", "wood")), - "subtype": StringName(d.get("subtype", "")), "stack_size": int(d.get("stack_size", 1)), "tile_x": int(d.get("tile_x", 0)), "tile_y": int(d.get("tile_y", 0)), @@ -185,288 +176,20 @@ static func from_dict(d: Dictionary) -> Dictionary: # ── render ──────────────────────────────────────────────────────────────────── -## Procedural shape per item type, drawn onto an arbitrary CanvasItem. All -## shapes draw inside a -6..+6 box so the quality border (half=6) wraps the -## result, and the carry indicator (Pawn._draw) can scale the same shapes down -## via draw_set_transform. Returns true if the type was handled; false sends -## the caller to its hue-hashed fallback. -## -## Shape language is loose silhouette + a category-appropriate colour: bread = -## brown loaf, grain = wheat-amber stalks, vegetable = green disc, meal = bowl, -## etc. Atlas-backed types (wood / stone / plank / iron_ore / gold) also have -## a shape here so the carry indicator works for them — the on-floor visual -## still uses the bundle icon via the Sprite2D child path. -static func draw_item_shape(target: CanvasItem, t: StringName, sub: StringName = &"") -> bool: - var dark := Color(0.10, 0.07, 0.05, 0.85) # shared outline - # Subtype dispatch — lets wheat / corn / potato / strawberry render - # distinctly even though they share TYPE_GRAIN or TYPE_VEGETABLE for - # storage-filter purposes. Falls through to the type dispatch if subtype - # is unrecognised so existing items don't blank out. - match sub: - &"wheat": - # Yellow stalks with grain heads — same as default TYPE_GRAIN shape. - var stalk := Color(0.85, 0.70, 0.20) - var tie := Color(0.55, 0.35, 0.10) - target.draw_rect(Rect2(-4.0, -6.0, 1.5, 12.0), stalk) - target.draw_rect(Rect2(-0.75, -6.0, 1.5, 12.0), stalk) - target.draw_rect(Rect2(2.5, -6.0, 1.5, 12.0), stalk) - target.draw_circle(Vector2(-3.2, -5.0), 1.2, Color(0.95, 0.78, 0.25)) - target.draw_circle(Vector2(0.0, -5.5), 1.2, Color(0.95, 0.78, 0.25)) - target.draw_circle(Vector2(3.2, -5.0), 1.2, Color(0.95, 0.78, 0.25)) - target.draw_rect(Rect2(-5.0, 0.0, 10.0, 2.0), tie) - return true - &"corn": - # Corn cob — yellow body with rows of kernel dots + green husk. - var husk := Color(0.30, 0.65, 0.20) - var cob := Color(0.95, 0.82, 0.30) - var kernel_dark := Color(0.70, 0.55, 0.15) - # Husk leaves splayed out at the top. - var husk_l: PackedVector2Array = PackedVector2Array([Vector2(-4.0, -5.0), Vector2(-2.0, -2.0), Vector2(-5.0, -1.0)]) - var husk_r: PackedVector2Array = PackedVector2Array([Vector2(4.0, -5.0), Vector2(2.0, -2.0), Vector2(5.0, -1.0)]) - target.draw_colored_polygon(husk_l, husk) - target.draw_colored_polygon(husk_r, husk) - # Cob body — vertical oval/rect with rounded corners. - target.draw_rect(Rect2(-3.0, -4.0, 6.0, 10.0), cob) - target.draw_circle(Vector2(0.0, 5.5), 3.0, cob) - # Kernel dots — 2 columns × 3 rows for texture. - for y in [-2.0, 0.0, 2.0, 4.0]: - target.draw_rect(Rect2(-2.0, y, 1.5, 1.5), kernel_dark) - target.draw_rect(Rect2(0.5, y, 1.5, 1.5), kernel_dark) - # Cob outline (open at top because of husk). - target.draw_arc(Vector2(0.0, 5.5), 3.0, 0.0, PI, 12, dark, 1.0) - return true - &"potato": - # Two brown lumps with sprout-eye dots — pile of potatoes. - var skin := Color(0.62, 0.45, 0.25) - var skin_dark := Color(0.42, 0.28, 0.15) - var eye := Color(0.25, 0.15, 0.05) - # Two overlapping potato ovals. - target.draw_circle(Vector2(-2.0, 1.0), 4.0, skin) - target.draw_circle(Vector2(2.5, -0.5), 3.5, skin) - # Outline. - target.draw_arc(Vector2(-2.0, 1.0), 4.0, 0.0, TAU, 16, skin_dark, 1.0) - target.draw_arc(Vector2(2.5, -0.5), 3.5, 0.0, TAU, 12, skin_dark, 1.0) - # Eye dots. - target.draw_circle(Vector2(-3.0, 0.0), 0.7, eye) - target.draw_circle(Vector2(-0.5, 2.0), 0.6, eye) - target.draw_circle(Vector2(3.5, -1.5), 0.7, eye) - return true - &"strawberry": - # Classic red strawberry — heart-shape body with green calyx on top - # and tiny yellow seed dots scattered on the surface. - var berry := Color(0.88, 0.18, 0.20) - var berry_dark := Color(0.62, 0.10, 0.10) - var leaf := Color(0.25, 0.60, 0.20) - var seed := Color(0.95, 0.85, 0.30) - # Body — wider top tapering to a point at the bottom. - var body: PackedVector2Array = PackedVector2Array([ - Vector2(-5.0, -1.0), Vector2(-3.5, -3.0), Vector2(3.5, -3.0), - Vector2(5.0, -1.0), Vector2(3.0, 4.0), Vector2(0.0, 6.0), Vector2(-3.0, 4.0), - ]) - target.draw_colored_polygon(body, berry) - # Body outline. - target.draw_polyline(body + PackedVector2Array([body[0]]), berry_dark, 1.0) - # Green calyx (leaves) on top. - target.draw_rect(Rect2(-3.0, -5.0, 6.0, 2.0), leaf) - var leaf_top: PackedVector2Array = PackedVector2Array([Vector2(-2.0, -5.0), Vector2(0.0, -7.0), Vector2(2.0, -5.0)]) - target.draw_colored_polygon(leaf_top, leaf) - # Seed speckles. - target.draw_circle(Vector2(-2.0, 1.0), 0.5, seed) - target.draw_circle(Vector2(2.0, 1.0), 0.5, seed) - target.draw_circle(Vector2(0.0, 3.0), 0.5, seed) - return true - # Fall through to type dispatch. - match t: - TYPE_BREAD: - var crust := Color(0.62, 0.40, 0.18) - var glaze := Color(0.82, 0.58, 0.30) - target.draw_rect(Rect2(-6.0, -1.0, 12.0, 5.0), crust) - target.draw_rect(Rect2(-5.0, -5.0, 10.0, 4.0), glaze) - target.draw_line(Vector2(-3.0, -4.0), Vector2(0.0, -2.0), dark, 1.0) - target.draw_line(Vector2(0.0, -4.0), Vector2(3.0, -2.0), dark, 1.0) - target.draw_rect(Rect2(-6.0, -5.0, 12.0, 9.0), dark, false, 1.0) - return true - TYPE_GRAIN: - var stalk := Color(0.85, 0.70, 0.20) - var tie := Color(0.55, 0.35, 0.10) - target.draw_rect(Rect2(-4.0, -6.0, 1.5, 12.0), stalk) - target.draw_rect(Rect2(-0.75, -6.0, 1.5, 12.0), stalk) - target.draw_rect(Rect2(2.5, -6.0, 1.5, 12.0), stalk) - target.draw_circle(Vector2(-3.2, -5.0), 1.2, Color(0.95, 0.78, 0.25)) - target.draw_circle(Vector2(0.0, -5.5), 1.2, Color(0.95, 0.78, 0.25)) - target.draw_circle(Vector2(3.2, -5.0), 1.2, Color(0.95, 0.78, 0.25)) - target.draw_rect(Rect2(-5.0, 0.0, 10.0, 2.0), tie) - return true - TYPE_FLOUR: - var sack := Color(0.93, 0.90, 0.78) - var shadow := Color(0.78, 0.74, 0.60) - target.draw_rect(Rect2(-5.0, -3.0, 10.0, 8.0), sack) - target.draw_rect(Rect2(3.0, -3.0, 2.0, 8.0), shadow) - target.draw_rect(Rect2(-4.0, -5.0, 8.0, 2.0), Color(0.45, 0.30, 0.10)) - target.draw_circle(Vector2(0.0, -5.5), 1.0, sack) - target.draw_rect(Rect2(-5.0, -6.0, 10.0, 11.0), dark, false, 1.0) - return true - TYPE_VEGETABLE: - var leaf := Color(0.25, 0.65, 0.20) - var root := Color(0.92, 0.88, 0.70) - target.draw_circle(Vector2(0.0, 1.0), 5.0, root) - target.draw_arc(Vector2(0.0, 1.0), 5.0, 0.0, TAU, 16, dark, 1.0) - target.draw_rect(Rect2(-3.0, -5.0, 2.0, 3.0), leaf) - target.draw_rect(Rect2(-1.0, -6.0, 2.0, 4.0), leaf) - target.draw_rect(Rect2( 1.0, -5.0, 2.0, 3.0), leaf) - return true - TYPE_MEAL: - var bowl := Color(0.50, 0.30, 0.12) - var bowl_rim := Color(0.30, 0.18, 0.08) - var food := Color(0.85, 0.55, 0.20) - target.draw_circle(Vector2(0.0, 1.0), 6.0, bowl) - target.draw_arc(Vector2(0.0, 1.0), 6.0, 0.0, PI, 16, bowl_rim, 1.0) - target.draw_line(Vector2(-6.0, 1.0), Vector2(6.0, 1.0), bowl_rim, 1.0) - target.draw_circle(Vector2(0.0, 0.0), 3.5, food) - target.draw_line(Vector2(-2.0, -5.0), Vector2(-1.0, -3.0), Color(0.9, 0.9, 0.9, 0.7), 1.0) - target.draw_line(Vector2(2.0, -5.0), Vector2(1.0, -3.0), Color(0.9, 0.9, 0.9, 0.7), 1.0) - return true - TYPE_MEAT: - var meat := Color(0.78, 0.20, 0.20) - var fat := Color(0.95, 0.85, 0.70) - target.draw_rect(Rect2(-5.0, -3.0, 10.0, 7.0), meat) - target.draw_line(Vector2(-5.0, 0.0), Vector2(5.0, 0.5), fat, 1.0) - target.draw_rect(Rect2(-5.0, -3.0, 10.0, 7.0), dark, false, 1.0) - return true - TYPE_CLOTH: - var cloth := Color(0.50, 0.65, 0.85) - var pleat := Color(0.32, 0.42, 0.58) - target.draw_rect(Rect2(-5.0, -4.0, 10.0, 8.0), cloth) - target.draw_line(Vector2(-5.0, -1.5), Vector2(5.0, -1.5), pleat, 1.0) - target.draw_line(Vector2(-5.0, 1.5), Vector2(5.0, 1.5), pleat, 1.0) - target.draw_rect(Rect2(-5.0, -4.0, 10.0, 8.0), dark, false, 1.0) - return true - TYPE_MEDICINE: - var phial := Color(0.95, 0.95, 0.95) - var cross := Color(0.80, 0.15, 0.15) - target.draw_rect(Rect2(-4.0, -5.0, 8.0, 10.0), phial) - target.draw_rect(Rect2(-1.0, -3.0, 2.0, 6.0), cross) - target.draw_rect(Rect2(-3.0, -1.0, 6.0, 2.0), cross) - target.draw_rect(Rect2(-4.0, -5.0, 8.0, 10.0), dark, false, 1.0) - return true - TYPE_TOOL: - var handle := Color(0.50, 0.30, 0.12) - var head := Color(0.45, 0.45, 0.48) - target.draw_rect(Rect2(-1.0, -2.0, 2.0, 8.0), handle) - target.draw_rect(Rect2(-5.0, -5.0, 10.0, 4.0), head) - target.draw_rect(Rect2(-5.0, -5.0, 10.0, 4.0), dark, false, 1.0) - return true - TYPE_WEAPON: - var blade := Color(0.78, 0.80, 0.85) - var guard := Color(0.45, 0.30, 0.10) - var pts: PackedVector2Array = PackedVector2Array([Vector2(0.0, -6.0), Vector2(2.5, 1.0), Vector2(-2.5, 1.0)]) - target.draw_colored_polygon(pts, blade) - target.draw_rect(Rect2(-4.0, 1.0, 8.0, 2.0), guard) - target.draw_rect(Rect2(-1.0, 3.0, 2.0, 3.0), guard) - return true - TYPE_ARMOR: - var steel := Color(0.65, 0.65, 0.70) - var visor := Color(0.20, 0.20, 0.25) - target.draw_circle(Vector2(0.0, 0.0), 5.5, steel) - target.draw_rect(Rect2(-1.0, -2.0, 2.0, 4.0), visor) - target.draw_rect(Rect2(-6.0, 4.0, 12.0, 2.0), steel) - target.draw_arc(Vector2(0.0, 0.0), 5.5, 0.0, TAU, 16, dark, 1.0) - return true - TYPE_STONE_BLOCK: - var stone := Color(0.62, 0.60, 0.58) - var stone_hi := Color(0.78, 0.76, 0.72) - target.draw_rect(Rect2(-6.0, -4.0, 12.0, 8.0), stone) - target.draw_rect(Rect2(-6.0, -4.0, 12.0, 2.0), stone_hi) - target.draw_line(Vector2(-6.0, 0.0), Vector2(6.0, 0.0), Color(0.42, 0.40, 0.38), 1.0) - target.draw_rect(Rect2(-6.0, -4.0, 12.0, 8.0), dark, false, 1.0) - return true - TYPE_COPPER_ORE: - var copper := Color(0.65, 0.35, 0.18) - var hi := Color(0.92, 0.55, 0.20) - target.draw_circle(Vector2(-2.0, 1.0), 3.5, copper) - target.draw_circle(Vector2(2.0, -1.0), 2.8, copper) - target.draw_circle(Vector2(-2.0, 1.0), 1.5, hi) - target.draw_circle(Vector2(2.0, -1.0), 1.0, hi) - return true - TYPE_SILVER: - var silver := Color(0.78, 0.80, 0.85) - var hi := Color(0.98, 0.98, 1.00) - target.draw_circle(Vector2(-2.0, 1.0), 3.5, silver) - target.draw_circle(Vector2(2.0, -1.0), 2.8, silver) - target.draw_circle(Vector2(-2.0, 0.5), 1.2, hi) - return true - TYPE_ASH: - var ash := Color(0.55, 0.55, 0.55) - var ash_hi := Color(0.78, 0.78, 0.78) - var pts: PackedVector2Array = PackedVector2Array([Vector2(-6.0, 4.0), Vector2(6.0, 4.0), Vector2(0.0, -2.0)]) - target.draw_colored_polygon(pts, ash) - target.draw_line(Vector2(-3.0, 1.0), Vector2(3.0, 1.0), ash_hi, 1.0) - target.draw_line(Vector2(-1.0, -3.0), Vector2(0.0, -5.0), Color(0.85, 0.85, 0.85, 0.6), 1.0) - target.draw_line(Vector2(1.5, -3.0), Vector2(2.5, -5.0), Color(0.85, 0.85, 0.85, 0.6), 1.0) - return true - # Atlas-backed types — the on-floor visual is the bundle icon (Sprite2D - # child), but the carry indicator needs a simple shape since pawns can't - # draw an AtlasTexture inline. Shapes below approximate the atlas look. - TYPE_WOOD: - var wood := Color(0.55, 0.35, 0.18) - var wood_hi := Color(0.75, 0.50, 0.25) - target.draw_rect(Rect2(-6.0, -3.0, 12.0, 6.0), wood) - target.draw_line(Vector2(-6.0, -1.0), Vector2(6.0, -1.0), wood_hi, 1.0) - target.draw_line(Vector2(-6.0, 1.0), Vector2(6.0, 1.0), Color(0.40, 0.25, 0.10), 1.0) - target.draw_rect(Rect2(-6.0, -3.0, 12.0, 6.0), dark, false, 1.0) - return true - TYPE_PLANK: - var plank := Color(0.80, 0.60, 0.35) - var plank_grain := Color(0.55, 0.38, 0.20) - target.draw_rect(Rect2(-6.0, -3.0, 12.0, 6.0), plank) - target.draw_line(Vector2(-5.0, -1.0), Vector2(5.0, -1.0), plank_grain, 1.0) - target.draw_line(Vector2(-5.0, 1.0), Vector2(5.0, 1.0), plank_grain, 1.0) - target.draw_rect(Rect2(-6.0, -3.0, 12.0, 6.0), dark, false, 1.0) - return true - TYPE_STONE: - var stone := Color(0.60, 0.58, 0.55) - var stone_hi := Color(0.78, 0.76, 0.72) - target.draw_circle(Vector2(-1.5, 1.0), 4.0, stone) - target.draw_circle(Vector2(2.0, -0.5), 3.0, stone) - target.draw_circle(Vector2(-1.5, 1.0), 1.5, stone_hi) - return true - TYPE_IRON_ORE: - var ore := Color(0.42, 0.42, 0.50) - var ore_hi := Color(0.60, 0.62, 0.72) - target.draw_circle(Vector2(-1.5, 1.0), 4.0, ore) - target.draw_circle(Vector2(2.0, -0.5), 3.0, ore) - target.draw_circle(Vector2(2.0, -0.5), 1.2, ore_hi) - return true - TYPE_GOLD: - var gold := Color(0.92, 0.78, 0.20) - var gold_hi := Color(1.00, 0.95, 0.55) - target.draw_circle(Vector2(-1.5, 1.0), 4.0, gold) - target.draw_circle(Vector2(2.0, -0.5), 3.0, gold) - target.draw_circle(Vector2(-1.5, 0.5), 1.5, gold_hi) - return true - _: - return false - - func _draw() -> void: - # Three render paths: - # 1. Atlas sprite (_ITEM_SPRITES): a Sprite2D child paints the icon; - # _draw() adds quality border + stack badge on top. - # 2. Procedural shape (_draw_item_shape returns true): bread/grain/ - # vegetable/meal/flour/meat/cloth/medicine/etc. get a recognisable - # silhouette in their category colour. - # 3. Unknown fallback: hue-hashed coloured square. Should be unreachable - # once every ALL_TYPES entry is handled above — kept for safety. + # Two render paths: types in _ITEM_SPRITES are painted by the Sprite2D child + # (built in setup()) — _draw() then only adds the quality border + stack + # count badge on top. Other types still use the procedural hue square so + # stockpile filtering remains visually unique while we expand the sprite set. var has_sprite: bool = _ITEM_SPRITES.has(item_type) var half: int = 6 if not has_sprite else 8 # border hugs the 16×16 sprite var square := Rect2(Vector2(-half, -half), Vector2(half * 2, half * 2)) if not has_sprite: - if not Item.draw_item_shape(self, item_type, subtype): - var hue := float(item_type.hash() % 360) / 360.0 - var fill := Color.from_hsv(hue, 0.6, 0.85) - draw_rect(square, fill) - draw_rect(square, Color(0.0, 0.0, 0.0, 0.75), false, 1.0) + var hue := float(item_type.hash() % 360) / 360.0 + var fill := Color.from_hsv(hue, 0.6, 0.85) + draw_rect(square, fill) + draw_rect(square, Color(0.0, 0.0, 0.0, 0.75), false, 1.0) # Quality border — drawn over the dark outline (or sprite), colour per quality tier. # NORMAL has no extra border. diff --git a/scenes/entities/quarry_workbench.gd b/scenes/entities/quarry_workbench.gd deleted file mode 100644 index 91ef0f7..0000000 --- a/scenes/entities/quarry_workbench.gd +++ /dev/null @@ -1,80 +0,0 @@ -class_name QuarryWorkbench extends Workbench -## Quarry workbench — built on a BigRockNode tile, produces stone indefinitely. -## -## Subclasses Workbench to reuse the build + bill machinery. A default -## FOREVER bill with the quarry_stone recipe is auto-added in _ready() so the -## workbench begins dripping stone as soon as construction completes. -## -## No-ingredient recipe: quarry_stone uses ingredient_count = 0. CraftingProvider -## must handle the zero-ingredient path before this workbench produces anything -## (see plan_quarry_bigrock.md Step 1 — handled separately). -## -## Variant appearance: overrides _draw() unconditionally so that the quarry -## silhouette is always shown regardless of label_text (mirrors CremationPyre). -## accepted_skill = manual_labor — any labourer can work the quarry. -## -## World registration: inherited from Workbench._ready / _exit_tree. - - -func _init() -> void: - label_text = "Quarry" - accepted_skill = &"manual_labor" - - -func _ready() -> void: - label_text = "Quarry" - accepted_skill = &"manual_labor" - super._ready() - # Auto-populate a default FOREVER bill so the bench is immediately usable - # once construction completes. Mirrors CremationPyre's bill wiring. - var b := Bill.new() - b.recipe = RecipeCatalog.quarry_stone() - b.mode = Bill.Mode.FOREVER - bills.append(b) - Audit.log("quarry", "QuarryWorkbench ready at %s — bill added" % tile) - - -# ── render ──────────────────────────────────────────────────────────────────── - -## Override Workbench._draw() to always dispatch to _draw_quarry(). -## Alpha is 0.4 while under construction, 1.0 once complete (same as all benches). -func _draw() -> void: - var alpha: float = 1.0 if is_completed() else 0.4 - _draw_quarry(alpha) - - -## Procedural quarry appearance: a wooden frame base with a large stone block -## on top, chisel-mark details, and a small pile of freshly cut stones to the -## right side. Local coords follow Workbench convention — (0, 0) is the -## bottom-right of the tile; the bench draws UP into negative-y space. -func _draw_quarry(alpha: float) -> void: - var frame_top := Color(0.55, 0.36, 0.18, alpha) # light wood top face - var frame_front := Color(0.42, 0.26, 0.12, alpha) # darker wood front - var frame_edge := Color(0.25, 0.14, 0.06, alpha) # wood grain seam - var stone_top := Color(0.60, 0.58, 0.55, alpha) # stone block top face - var stone_front := Color(0.44, 0.43, 0.41, alpha) # stone block front - var chisel_mark := Color(0.28, 0.27, 0.25, alpha) # cut line on stone - var pile_light := Color(0.62, 0.61, 0.59, alpha) # loose stone pile - var pile_dark := Color(0.38, 0.37, 0.35, alpha) # pile shadow - var outline := Color(0.20, 0.18, 0.16, 0.70 * alpha) - - # Wooden frame base — front face + top lip. - draw_rect(Rect2(Vector2(-8.0, -7.0), Vector2(16.0, 7.0)), frame_front) - draw_rect(Rect2(Vector2(-8.0, -10.0), Vector2(16.0, 3.0)), frame_top) - draw_line(Vector2(-8.0, -7.0), Vector2(8.0, -7.0), frame_edge, 1.0) - - # Stone block sitting on the frame — occupies most of the upper half. - draw_rect(Rect2(Vector2(-7.0, -15.0), Vector2(11.0, 5.0)), stone_top) - draw_rect(Rect2(Vector2(-7.0, -10.5), Vector2(11.0, 0.5)), stone_front) # thin visible front edge - - # Chisel marks on the stone top — short diagonal cuts suggesting work in progress. - draw_line(Vector2(-4.0, -13.0), Vector2(-2.0, -11.5), chisel_mark, 1.0) - draw_line(Vector2(-1.0, -14.0), Vector2( 1.0, -12.5), chisel_mark, 1.0) - draw_line(Vector2( 2.0, -13.0), Vector2( 4.0, -11.5), chisel_mark, 1.0) - - # Small pile of cut stones on the right — two rounded rects stacked. - draw_rect(Rect2(Vector2(5.0, -9.0), Vector2(4.0, 3.0)), pile_dark) - draw_rect(Rect2(Vector2(5.0, -11.0), Vector2(3.0, 2.0)), pile_light) - - # Tile outline. - draw_rect(Rect2(Vector2(-8.0, -16.0), Vector2(16.0, 16.0)), outline, false, 1.0) diff --git a/scenes/entities/quarry_workbench.gd.uid b/scenes/entities/quarry_workbench.gd.uid deleted file mode 100644 index a3ef967..0000000 --- a/scenes/entities/quarry_workbench.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bpbcwr4dh1736 diff --git a/scenes/entities/quarry_workbench.tscn b/scenes/entities/quarry_workbench.tscn deleted file mode 100644 index ec28b7a..0000000 --- a/scenes/entities/quarry_workbench.tscn +++ /dev/null @@ -1,6 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://quarry_workbench_entity"] - -[ext_resource type="Script" path="res://scenes/entities/quarry_workbench.gd" id="1_quarry"] - -[node name="QuarryWorkbench" type="Node2D"] -script = ExtResource("1_quarry") diff --git a/scenes/entities/tree.gd b/scenes/entities/tree.gd index 8d5919f..52177bf 100644 --- a/scenes/entities/tree.gd +++ b/scenes/entities/tree.gd @@ -1,15 +1,10 @@ ## Tree entity — choppable by a pawn with a Chop job. Drops wood Item nodes -## when felled. Trees also grow through four stages (Sapling → Young → Growing -## → Mature); only Mature trees can be chopped. +## when felled. ## ## Chopping model (docs/implementation.md Phase 4): ## A ChopProvider creates a Job whose INTERACT toil calls on_chop_tick() once ## per sim tick via JobRunner. After CHOP_TICKS ticks the tree is felled. ## -## Growth model: on_sim_tick() is called once per sim tick by world.gd's sweep. -## After STAGE_TICKS ticks at each sub-mature stage the tree advances one stage -## and _refresh_sprite() updates the visual. -## ## World registration (World.register_tree / World.unregister_tree) is called ## here but the methods land in World during Opus integration. @@ -27,19 +22,6 @@ const WOOD_DROPS_ON_FELL: int = 3 ## Stack size per dropped Item (Phase 4 simplicity: 3 items of stack 1 each). const STACK_SIZE_PER_DROP: int = 1 -# ── growth stage constants ───────────────────────────────────────────────────── - -## Growth stage indices. -const STAGE_SAPLING: int = 0 -const STAGE_YOUNG: int = 1 -const STAGE_GROWING: int = 2 -const STAGE_MATURE: int = 3 - -## Sim ticks spent in each sub-mature stage before advancing. -## 5 in-game hours per stage at 20 Hz = 1200 ticks/hour × 5 = 6000 ticks. -## At default 5× speed that is ~5 min real time per stage, ~15 min seed → mature. -const STAGE_TICKS: int = 6000 - # ── state ───────────────────────────────────────────────────────────────────── var tile: Vector2i = Vector2i.ZERO @@ -49,98 +31,48 @@ var chop_progress: int = 0 ## ignores undesignated trees (Rimworld parity — pawns don't auto-chop). var chop_designated: bool = false -## Current growth stage (STAGE_SAPLING..STAGE_MATURE). Default MATURE so -## existing seed trees load at full size with no visual regression. -var growth_stage: int = STAGE_MATURE -## Ticks elapsed within the current sub-mature stage. -var growth_progress: int = 0 - -## Set to true on a ghost tree spawned by the plant_tree designation. The -## ConstructionProvider will issue a build job; on completion this flag clears -## and the tree grows normally. -var pending_plant: bool = false - # Preloaded scene for spawned wood items. const ITEM_SCENE: PackedScene = preload("res://scenes/entities/item.tscn") -## ElvGames Grasslands tree pack — 4 silhouettes laid out left-to-right (64×80 -## each). Trunk base sits in the bottom ~10 rows; we anchor the sprite centre -## 32 px above tile origin so the trunk bottom lands at the tile's bottom edge -## and the canopy rises into the cells above. -## -## Three season palettes (Spring / Summer / Fall) give 12 visual variants from -## the same silhouette set. Winter is omitted — snowy trees look out of place -## in the current biome. When a season-cycle system lands later, swap the -## active texture by season globally instead of per-tree. -const _TREE_TEXES: Array[Texture2D] = [ - preload("res://art/sprites/FG_Tree_Spring.png"), - preload("res://art/sprites/FG_Tree_Summer.png"), - preload("res://art/sprites/FG_Tree_Fall.png"), -] +## ElvGames Grasslands tree pack — 4 variants laid out left-to-right. +## Each variant is 64×80 px; trunk base sits in the bottom ~10 rows. We anchor +## the sprite center 32 px above tile origin so the trunk bottom lands at the +## tile's bottom edge and the canopy rises into the cells above. +const _TREE_TEX: Texture2D = preload("res://art/sprites/FG_Tree_Spring.png") const _TREE_VARIANT_W: int = 64 const _TREE_VARIANT_H: int = 80 -const _TREE_SILHOUETTES: int = 4 # silhouettes per atlas (columns) - -## Growth-stage atlas — 128×32, 8 columns × 1 row of 16×32 cells, left-to-right -## progressively larger trees. Used for sub-mature stages so the player sees -## a proper sapling → small tree silhouette change instead of a scaled-down -## mature canopy. Stage MATURE keeps using _TREE_TEXES above (the full 64×80 -## canopy). -const _STAGE_TEX: Texture2D = preload("res://art/sprites/FG_Tree_Stages.png") -const _STAGE_CELL_W: int = 16 -const _STAGE_CELL_H: int = 32 -## Atlas column to use per growth_stage. Stage 3 (MATURE) is unused here. -const _STAGE_COLS: Array[int] = [0, 1, 3, -1] +const _TREE_VARIANT_COUNT: int = 4 # ── lifecycle ───────────────────────────────────────────────────────────────── func _ready() -> void: position = _tile_to_world(tile) - _refresh_sprite() + _build_sprite() # Y-sort so the canopy draws behind walls/pawns that are visually south of # the trunk base. Position.y is the trunk-base row. y_sort_enabled = true World.register_tree(self) -## Rebuild the Sprite2D child to match the current growth_stage. -## 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() +## Adds a Sprite2D child painted with one of the 4 ElvGames tree variants. +## Variant chosen deterministically from the tile coord so the same tile always +## gets the same tree silhouette across boots and load/save. +func _build_sprite() -> void: var sprite := Sprite2D.new() sprite.name = "Sprite" + sprite.texture = _TREE_TEX sprite.region_enabled = true + var variant: int = (tile.x * 31 + tile.y * 17) % _TREE_VARIANT_COUNT + sprite.region_rect = Rect2(variant * _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) + # 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() - - -## Adds a Sprite2D child painted with one of the 12 ElvGames tree variants -## (4 silhouettes × 3 season palettes). Kept for call-site compatibility but -## now delegates to _refresh_sprite(). New code should call _refresh_sprite(). -func _build_sprite() -> void: - _refresh_sprite() func _exit_tree() -> void: @@ -150,81 +82,17 @@ func _exit_tree() -> void: # ── public API ──────────────────────────────────────────────────────────────── ## One-shot initialiser. Call after add_child() so _ready() already fired. -## start_stage defaults to STAGE_MATURE for backward compatibility. -func setup(start_tile: Vector2i, start_stage: int = STAGE_MATURE) -> void: +func setup(start_tile: Vector2i) -> void: tile = start_tile chop_progress = 0 - growth_stage = start_stage - growth_progress = 0 position = _tile_to_world(tile) - _refresh_sprite() queue_redraw() - Audit.log("tree", "spawned at %s (stage=%d)" % [tile, growth_stage]) + Audit.log("tree", "spawned at %s" % tile) -## True when the tree is mature, unChopped, and not a pending-plant ghost. -## Only mature trees yield wood — saplings/young/growing cannot be felled. +## True when the tree hasn't been fully chopped yet. func is_choppable() -> bool: - return chop_progress < CHOP_TICKS and growth_stage == STAGE_MATURE and not pending_plant - - -## Called by world.gd's sim-tick sweep once per sim tick. -## Advances growth_progress and promotes the stage on threshold. No-op for -## mature trees and for pending-plant ghosts (ghost must be built first). -func on_sim_tick() -> void: - if growth_stage >= STAGE_MATURE or pending_plant: - return - growth_progress += 1 - if growth_progress >= STAGE_TICKS: - growth_stage += 1 - growth_progress = 0 - _refresh_sprite() - Audit.log("tree", "grew to stage %d at %s" % [growth_stage, tile]) - - -# ── pending-plant / build-site duck-type API ────────────────────────────────── -# ConstructionProvider requires is_buildable() / on_build_tick() / label() -# on every entity in World.build_queue. A pending_plant tree satisfies this -# interface so the provider can assign a pawn to "build" (plant) it. - -## Ticks of pawn work needed to complete a manual planting job. -const PLANT_TICKS: int = 30 - -## Progress counter within the planting job (0..PLANT_TICKS). -var _plant_progress: int = 0 - - -## True while the tree is a pending-plant ghost awaiting pawn work. -func is_buildable() -> bool: - return pending_plant and _plant_progress < PLANT_TICKS - - -## Human-readable label for the ConstructionProvider job entry. -func label() -> String: - return "Plant tree" - - -## Called by JobRunner's BUILD toil once per sim tick while a pawn works this -## site. After PLANT_TICKS the ghost becomes a real sapling. -func on_build_tick() -> void: - if not is_buildable(): - return - _plant_progress += 1 - queue_redraw() - if _plant_progress >= PLANT_TICKS: - _complete_plant() - - -## Finish the planting job: clear the pending flag, register as a real sapling, -## remove from World.build_queue, and clear the designation highlight. -func _complete_plant() -> void: - pending_plant = false - _plant_progress = 0 - World.unregister_build_site(self) - World.clear_designation_at(tile) - _refresh_sprite() - queue_redraw() - Audit.log("tree", "planted at %s — sapling begins growing" % tile) + return chop_progress < CHOP_TICKS ## Called by the INTERACT toil in JobRunner once per sim tick while the pawn @@ -264,10 +132,6 @@ func to_dict() -> Dictionary: "tile_y": tile.y, "chop_progress": chop_progress, "chop_designated": chop_designated, - "growth_stage": growth_stage, - "growth_progress": growth_progress, - "pending_plant": pending_plant, - "plant_progress": _plant_progress, } @@ -277,27 +141,15 @@ static func from_dict(d: Dictionary) -> Dictionary: "tile_y": int(d.get("tile_y", 0)), "chop_progress": int(d.get("chop_progress", 0)), "chop_designated": bool(d.get("chop_designated", false)), - # Default to STAGE_MATURE (3) so pre-growth-system saves load as mature trees. - "growth_stage": int(d.get("growth_stage", 3)), - "growth_progress": int(d.get("growth_progress", 0)), - "pending_plant": bool(d.get("pending_plant", false)), - "plant_progress": int(d.get("plant_progress", 0)), } # ── render ──────────────────────────────────────────────────────────────────── func _draw() -> void: - # 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. + # Canopy + trunk now come from the Sprite2D child (see _build_sprite). # This _draw renders only the chop-progress notch overlaid on the trunk. - if chop_progress > 0 and growth_stage == STAGE_MATURE: + if chop_progress > 0: var ratio := float(chop_progress) / float(CHOP_TICKS) var notch_depth := ratio * 3.0 draw_line( diff --git a/scenes/entities/workbench.gd b/scenes/entities/workbench.gd index a108c24..51af0fd 100644 --- a/scenes/entities/workbench.gd +++ b/scenes/entities/workbench.gd @@ -1,15 +1,13 @@ class_name Workbench extends Node2D ## Workbench entity — buildable structure where pawns craft items per bills. ## -## Rendered procedurally (Y-sorted) matching the 3/4-perspective convention -## from Wall/Door. Ghost state (40% alpha) while construction is in progress; -## solid once _completed. +## Rendered as a bottom-anchored sprite (Y-sorted) matching the 3/4-perspective +## convention from Wall/Door. Ghost state (40% alpha) while construction is +## in progress; solid once _completed. ## ## Variant appearance is driven by label_text: -## "Carpenter" → wooden workbench with saw + log slabs on top -## "Smelter" → dark stone furnace with chimney and ember glow -## "Hearth" → tall stone fireplace with mantle + log fire (h=2 tiles) -## "Millstone" → wooden frame supporting a round grindstone wheel +## "Carpenter" → warm-brown wood bench with a vise detail +## "Smelter" → dark grey stone block with an orange ember glow ## Other → generic warm-grey fallback ## ## Bill model (architecture.md "Production: workbenches, recipes, bills"): @@ -44,34 +42,45 @@ const HEARTH_LIGHT_RADIUS: int = 5 ## Pixel size of the procedural radial gradient used for PointLight2D. const LIGHT_TEXTURE_SIZE: int = 64 -# ── variant rendering ───────────────────────────────────────────────────────── -## All four named workbench variants render procedurally via _draw(). The -## atlas-sprite approach was abandoned in the 2026-05-15 polish pass after -## visual review: the chosen ElvGames atlas tiles read as a chest-of-drawers -## (Carpenter), a tiny candle base (Smelter), a 2-burner stove (Hearth), and -## a stack of cushions (Millstone). Procedural draws give us shape control to -## hit the silhouettes those names imply. See CremationPyre._draw_pyre() for -## precedent — same pattern, local coords centered at (0, 0) at the BOTTOM of -## the workbench tile, drawing UP into negative y. +# ── sprite atlas (replaces procedural _draw for the four named variants) ───── +## Variant → (texture, atlas top-left coord, height in tiles). Selected from +## the ElvGames House Interior + Marketplace tilesets in the 2026-05-12 visual +## pass; see /tmp/workbench_candidates_v2.png from that session for the diff. ## -## Tall variants (Hearth, h=2 logically) draw above y=-16 into the tile north -## of the bench; pawns stand correctly behind them because y_sort_enabled is -## on and position.y is anchored at the bench-tile's bottom edge. +## h_tiles = 2 sprites bottom-anchor and extend UP into the tile above (Bed +## pattern) so the carpenter's tall cabinet reads as a piece of furniture +## standing in the room rather than a flat decal. h_tiles = 1 sprites stay +## within the workbench tile (anvil, stove top, barrel — squat shapes). +## +## Unrecognised label_texts fall through to procedural _draw_generic, so +## ad-hoc workbench variants keep rendering until a sprite is picked for them. +const _INTERIOR_TEX: Texture2D = preload("res://art/tiles/FG_Interior.png") +const _MARKETPLACE_TEX: Texture2D = preload("res://art/tiles/FG_Marketplace.png") +const _VARIANT_SPRITES: Dictionary = { + "Carpenter": {"tex": _INTERIOR_TEX, "coord": Vector2i(24, 20), "h_tiles": 2}, + "Smelter": {"tex": _MARKETPLACE_TEX, "coord": Vector2i(8, 30), "h_tiles": 1}, + "Hearth": {"tex": _INTERIOR_TEX, "coord": Vector2i(16, 32), "h_tiles": 1}, + "Millstone": {"tex": _INTERIOR_TEX, "coord": Vector2i(17, 40), "h_tiles": 1}, +} # ── exports ─────────────────────────────────────────────────────────────────── ## Tile position of this workbench in world-tile coordinates. @export var tile: Vector2i = Vector2i.ZERO -## Player-visible label. Also drives the procedural _draw() variant dispatch. -## Setter triggers a redraw + lazy light build — callers can assign label_text -## either before OR after setup() and the visual catches up. +## Player-visible label. Also drives the sprite variant (see _VARIANT_SPRITES) +## and procedural _draw fallback for unrecognised values. +## Setter rebuilds the sprite child idempotently — callers can assign +## label_text either before OR after setup() and end up with the right sprite. ## (World.gd assigns it after setup(); SaveSystem._spawn_workbench too.) @export var label_text: String = "Workbench": set(value): label_text = value + # Setter fires from .tscn property initialisation BEFORE _ready, so + # guard the rebuild until the node is actually in the tree (children + # can't be added safely before then). if is_inside_tree(): - queue_redraw() + _build_sprite() # Hearth-light catch-up: _ready() builds the PointLight2D only when # label_text is already "Hearth", but the project's call pattern # (add_child first, then set label_text) means _ready always saw the @@ -156,6 +165,8 @@ func _exit_tree() -> void: ## One-shot initialiser. Call after add_child() so _ready() has fired. +## Builds the variant sprite using the current label_text — if the caller +## hasn't assigned label_text yet, the setter rebuilds the sprite on assignment. ## Idempotent (safe under save-load's instantiate → setup → from_dict → setup chain). func setup(p_tile: Vector2i) -> void: tile = p_tile @@ -163,12 +174,52 @@ func setup(p_tile: Vector2i) -> void: tile.x * TILE_SIZE_PX + TILE_SIZE_PX / 2.0, tile.y * TILE_SIZE_PX + TILE_SIZE_PX ) - # Y-sort so tall variants (Hearth) drawing into the tile north of the bench - # occlude pawns standing behind. Matches Bed / Wall. + # Y-sort so a 16×32 Carpenter sprite (which rises into the tile north of + # the bench) occludes pawns standing behind it. Matches Bed / Wall. y_sort_enabled = true + _build_sprite() queue_redraw() +## Build the variant Sprite2D child (or no-op when label_text isn't in the +## sprite table — those fall through to procedural _draw rendering). +## Idempotent: frees any previous Sprite child first. Called from setup() AND +## from the label_text setter, so the sprite always matches the current variant. +func _build_sprite() -> void: + var prev := get_node_or_null("Sprite") + if prev != null: + prev.queue_free() + var data = _VARIANT_SPRITES.get(label_text) + if data == null: + # Generic / unknown variants keep procedural rendering. _draw_generic + # fires through the existing match in _draw(). + return + var sprite := Sprite2D.new() + sprite.name = "Sprite" + sprite.texture = data["tex"] + sprite.region_enabled = true + var coord: Vector2i = data["coord"] + var h_tiles: int = data["h_tiles"] + var pixels_h: int = TILE_SIZE_PX * h_tiles + sprite.region_rect = Rect2( + coord.x * TILE_SIZE_PX, + coord.y * TILE_SIZE_PX, + TILE_SIZE_PX, + pixels_h, + ) + sprite.centered = true + # Parent position.y is at the BOTTOM of the workbench tile (see setup()). + # Bottom-anchor the sprite by offsetting it up by half its height, so a + # 16×16 sprite spans local y −16..0 (within the bench tile) and a 16×32 + # sprite spans local y −32..0 (bench tile + the tile above it, like Bed + # but extending UPWARD — workbenches don't have a "foot tile"). + sprite.offset = Vector2(0.0, -float(pixels_h) / 2.0) + sprite.z_index = 0 + # Ghost state — translucent until built. Solidified in _complete(). + sprite.modulate.a = 1.0 if _completed else 0.4 + add_child(sprite) + + # ── BuildJob interface ──────────────────────────────────────────────────────── ## True while the workbench still needs construction work. @@ -206,15 +257,6 @@ func add_bill(b) -> void: Audit.log("workbench", "%s: bill added — recipe '%s'" % [label_text, b.recipe.id]) -## Remove a bill from this workbench's queue. If the bill is currently being -## crafted, the active toil is interrupted cleanly so the pawn re-decides. -func remove_bill(b) -> void: - if current_bill == b: - on_craft_interrupted() - bills.erase(b) - Audit.log("workbench", "%s: bill removed — recipe '%s'" % [label_text, b.recipe.id]) - - ## Return the first bill that is active and whose required_skill matches ## this bench's accepted_skill. Returns null when none qualify. ## CraftingProvider calls this; JobRunner also calls it when the current_bill @@ -325,156 +367,29 @@ func from_dict(d: Dictionary) -> void: # ── render ───────────────────────────────────────────────────────────────────── func _draw() -> void: + # Sprite-backed variants (Carpenter / Smelter / Hearth) render entirely + # through their Sprite2D child — no procedural fallback needed. Millstone + # also has a sprite but keeps a small dark-grey wheel overlay so the + # wood barrel below reads as "grinding station" rather than a plain barrel. + # Unrecognised label_texts fall through to _draw_generic so ad-hoc + # benches still render until a sprite is picked for them. var alpha: float = 1.0 if _completed else 0.4 - match label_text: - "Carpenter": _draw_carpenter(alpha) - "Smelter": _draw_smelter(alpha) - "Hearth": _draw_hearth(alpha) - "Millstone": _draw_millstone(alpha) - _: _draw_generic(alpha) + if label_text == "Millstone": + _draw_millstone_overlay(alpha) + return + if _VARIANT_SPRITES.has(label_text): + return + _draw_generic(alpha) -## Carpenter — wooden plank top with two visible legs and a hand-saw + log -## slabs on top. Reads as a workshop bench at 16×16 thanks to the saw blade -## silhouette breaking the plain top. -func _draw_carpenter(alpha: float) -> void: - var plank_top := Color(0.70, 0.50, 0.30, alpha) - var plank_front := Color(0.55, 0.38, 0.22, alpha) - var plank_edge := Color(0.35, 0.22, 0.12, alpha) - var leg := Color(0.30, 0.20, 0.10, alpha) - var saw_blade := Color(0.78, 0.78, 0.82, alpha) - var saw_handle := Color(0.55, 0.30, 0.15, alpha) - var log_face := Color(0.62, 0.42, 0.24, alpha) - var log_ring := Color(0.42, 0.27, 0.14, alpha) - var outline := Color(0.15, 0.10, 0.05, 0.7 * alpha) - - # Two legs at the corners (front face). - draw_rect(Rect2(Vector2(-7.0, -10.0), Vector2(2.0, 10.0)), leg) - draw_rect(Rect2(Vector2( 5.0, -10.0), Vector2(2.0, 10.0)), leg) - # Plank front face — thick band. - draw_rect(Rect2(Vector2(-8.0, -12.0), Vector2(16.0, 5.0)), plank_front) - # Plank top — slimmer band above the front, suggesting depth. - draw_rect(Rect2(Vector2(-8.0, -15.0), Vector2(16.0, 3.0)), plank_top) - # Edge highlight between top and front. - draw_line(Vector2(-8.0, -12.0), Vector2(8.0, -12.0), plank_edge, 1.0) - # Two short log slabs sitting on the left side of the top. - draw_rect(Rect2(Vector2(-6.0, -17.0), Vector2(3.0, 2.0)), log_face) - draw_rect(Rect2(Vector2(-3.0, -17.0), Vector2(3.0, 2.0)), log_face) - draw_line(Vector2(-4.5, -17.0), Vector2(-4.5, -15.0), log_ring, 1.0) - # Saw on the right — handle + blade silhouette. - draw_rect(Rect2(Vector2(1.0, -16.0), Vector2(6.0, 1.5)), saw_blade) - draw_rect(Rect2(Vector2(5.5, -17.0), Vector2(2.0, 2.0)), saw_handle) - # Outline. - draw_rect(Rect2(Vector2(-8.0, -16.0), Vector2(16.0, 16.0)), outline, false, 1.0) - - -## Smelter — stone furnace block with a stubby chimney puffing smoke and a -## bright ember-glow opening on the front face. Stone-grey base separates it -## visually from the Carpenter's warm wood. -func _draw_smelter(alpha: float) -> void: - var stone_top := Color(0.55, 0.55, 0.55, alpha) - var stone_front := Color(0.42, 0.42, 0.43, alpha) - var stone_shad := Color(0.30, 0.30, 0.32, alpha) - var ember := Color(0.98, 0.55, 0.10, alpha) - var ember_core := Color(1.00, 0.85, 0.30, alpha) - var chimney := Color(0.32, 0.30, 0.30, alpha) - var smoke := Color(0.75, 0.73, 0.70, alpha * 0.7) - var outline := Color(0.15, 0.12, 0.10, 0.7 * alpha) - - # Stone front body. - draw_rect(Rect2(Vector2(-8.0, -12.0), Vector2(16.0, 12.0)), stone_front) - # Top face — slightly lighter band. - draw_rect(Rect2(Vector2(-8.0, -15.0), Vector2(16.0, 3.0)), stone_top) - # Furnace mouth — dark recess with bright ember inside. - draw_rect(Rect2(Vector2(-4.0, -9.0), Vector2(8.0, 5.0)), stone_shad) - draw_rect(Rect2(Vector2(-3.0, -8.0), Vector2(6.0, 3.0)), ember) - draw_rect(Rect2(Vector2(-2.0, -7.0), Vector2(4.0, 1.0)), ember_core) - # Mortar lines across the front for stone-block feel. - draw_line(Vector2(-8.0, -8.0), Vector2(-4.0, -8.0), stone_shad, 1.0) - draw_line(Vector2( 4.0, -8.0), Vector2( 8.0, -8.0), stone_shad, 1.0) - # Chimney + smoke wisps rising above. - draw_rect(Rect2(Vector2(2.0, -19.0), Vector2(3.0, 4.0)), chimney) - draw_rect(Rect2(Vector2(3.0, -22.0), Vector2(1.0, 3.0)), smoke) - draw_rect(Rect2(Vector2(2.0, -24.0), Vector2(1.0, 2.0)), smoke) - # Outline. - draw_rect(Rect2(Vector2(-8.0, -16.0), Vector2(16.0, 16.0)), outline, false, 1.0) - - -## Hearth — tall (h=2) stone fireplace with mantle, arched opening, log fire -## with embers, and a flame licking up. Draws above y=-16 into the tile north -## of the bench (y_sort handles occlusion). Light-emitting via _maybe_build_light. -func _draw_hearth(alpha: float) -> void: - var stone := Color(0.60, 0.58, 0.55, alpha) - var stone_dark := Color(0.42, 0.40, 0.38, alpha) - var mantle := Color(0.50, 0.34, 0.20, alpha) - var mantle_edge := Color(0.32, 0.20, 0.10, alpha) - var opening := Color(0.08, 0.04, 0.02, alpha) - var log_wood := Color(0.55, 0.32, 0.15, alpha) - var ember := Color(0.98, 0.55, 0.10, alpha) - var flame_inner := Color(1.00, 0.85, 0.30, alpha) - var flame_outer := Color(0.95, 0.40, 0.05, alpha) - var outline := Color(0.15, 0.10, 0.05, 0.7 * alpha) - - # Stone surround — fills the bench tile (y −16..0) and the tile above - # (y −32..-16) so the fireplace is a 16×32 silhouette. - draw_rect(Rect2(Vector2(-8.0, -32.0), Vector2(16.0, 32.0)), stone) - # Stone block mortar — a couple of horizontal seams. - draw_line(Vector2(-8.0, -22.0), Vector2(8.0, -22.0), stone_dark, 1.0) - draw_line(Vector2(-8.0, -28.0), Vector2(8.0, -28.0), stone_dark, 1.0) - draw_line(Vector2(-2.0, -32.0), Vector2(-2.0, -28.0), stone_dark, 1.0) - draw_line(Vector2( 3.0, -28.0), Vector2( 3.0, -22.0), stone_dark, 1.0) - # Wooden mantle — horizontal beam across the middle. - draw_rect(Rect2(Vector2(-8.0, -19.0), Vector2(16.0, 3.0)), mantle) - draw_line(Vector2(-8.0, -19.0), Vector2(8.0, -19.0), mantle_edge, 1.0) - draw_line(Vector2(-8.0, -16.0), Vector2(8.0, -16.0), mantle_edge, 1.0) - # Arched opening — dark recess in the lower stone block. - draw_rect(Rect2(Vector2(-6.0, -14.0), Vector2(12.0, 14.0)), opening) - # Two stacked logs sitting in the opening. - draw_rect(Rect2(Vector2(-5.0, -4.0), Vector2(10.0, 2.0)), log_wood) - draw_rect(Rect2(Vector2(-4.0, -6.0), Vector2(8.0, 2.0)), log_wood) - # Ember strip glowing under the logs. - draw_rect(Rect2(Vector2(-4.0, -2.0), Vector2(8.0, 2.0)), ember) - # Flame — tapered teardrop above the logs. - draw_rect(Rect2(Vector2(-3.0, -10.0), Vector2(6.0, 4.0)), flame_outer) - draw_rect(Rect2(Vector2(-2.0, -12.0), Vector2(4.0, 2.0)), flame_outer) - draw_rect(Rect2(Vector2(-1.0, -13.0), Vector2(2.0, 1.0)), flame_outer) - draw_rect(Rect2(Vector2(-2.0, -9.0), Vector2(4.0, 2.0)), flame_inner) - draw_rect(Rect2(Vector2(-1.0, -11.0), Vector2(2.0, 2.0)), flame_inner) - # Outline around the full 16×32 silhouette. - draw_rect(Rect2(Vector2(-8.0, -32.0), Vector2(16.0, 32.0)), outline, false, 1.0) - - -## Millstone — wooden frame supporting a large round grindstone, viewed -## 3/4-perspective so the wheel reads as both round (top) and solid (front). -func _draw_millstone(alpha: float) -> void: - var frame_top := Color(0.55, 0.36, 0.18, alpha) - var frame_front := Color(0.42, 0.26, 0.12, alpha) - var frame_edge := Color(0.25, 0.14, 0.06, alpha) - var wheel := Color(0.55, 0.53, 0.50, alpha) - var wheel_dark := Color(0.34, 0.32, 0.30, alpha) - var wheel_rim := Color(0.18, 0.16, 0.14, alpha) - var groove := Color(0.28, 0.26, 0.24, alpha) - var pin := Color(0.20, 0.18, 0.16, alpha) - var outline := Color(0.15, 0.10, 0.05, 0.7 * alpha) - - # Wooden frame base — front + top faces. - draw_rect(Rect2(Vector2(-8.0, -7.0), Vector2(16.0, 7.0)), frame_front) - draw_rect(Rect2(Vector2(-8.0, -10.0), Vector2(16.0, 3.0)), frame_top) - draw_line(Vector2(-8.0, -7.0), Vector2(8.0, -7.0), frame_edge, 1.0) - # Grindstone — large dark-grey disc, rim slightly darker. Centred over - # the top of the frame, sticking up into the tile above only slightly. - var c := Vector2(0.0, -12.0) - draw_circle(c, 7.0, wheel_rim) - draw_circle(c, 6.0, wheel) - # Front-face shadow band across the lower half of the disc. - draw_rect(Rect2(Vector2(-6.0, -12.0), Vector2(12.0, 5.0)), wheel_dark) - # Two radial grooves — pie-slice indicators that the stone spins. - draw_line(c, c + Vector2(5.0, -3.5), groove, 1.0) - draw_line(c, c + Vector2(-5.0, -3.5), groove, 1.0) - # Centre pin / spindle. - draw_circle(c, 1.2, pin) - # Outline. - draw_rect(Rect2(Vector2(-8.0, -19.0), Vector2(16.0, 19.0)), outline, false, 1.0) +## Stone-wheel overlay drawn on top of the Millstone barrel sprite. Without +## this, the barrel reads as "water/grain storage" rather than a millstone. +## The circle sits inside the top half of the barrel's tile. +func _draw_millstone_overlay(alpha: float) -> void: + var wheel := Color(0.40, 0.40, 0.36, alpha) + var rim := Color(0.22, 0.22, 0.20, alpha) + draw_circle(Vector2(0.0, -10.0), 4.5, wheel) + draw_arc(Vector2(0.0, -10.0), 4.5, 0.0, TAU, 12, rim, 1.0) func _draw_generic(alpha: float) -> void: @@ -495,7 +410,11 @@ func _draw_generic(alpha: float) -> void: func _complete() -> void: _completed = true - # Procedural-only variants re-read alpha through _draw() via queue_redraw. + # Solidify the ghost: sprite child (if any) goes from 40% to full opacity. + # Procedural-only variants reread alpha through _draw() via queue_redraw. + var sprite: Sprite2D = get_node_or_null("Sprite") + if sprite != null: + sprite.modulate.a = 1.0 # Phase 11: enable PointLight2D for light-emitting workbenches on completion. if _light != null: _light.enabled = is_on() diff --git a/scenes/main/main.gd b/scenes/main/main.gd index b3e6686..692a27d 100644 --- a/scenes/main/main.gd +++ b/scenes/main/main.gd @@ -19,12 +19,6 @@ const LOAD_MENU_SCRIPT: Script = preload("res://scenes/ui/load_menu. const RESUME_TOAST_SCRIPT: Script = preload("res://scenes/ui/resume_toast.gd") # Phase 17 — PawnDetailPanel (layer 18) and SettingsMenu (layer 26). const PAWN_DETAIL_PANEL_SCRIPT: Script = preload("res://scenes/ui/pawn_detail_panel.gd") -const WORKBENCH_PANEL_SCRIPT: Script = preload("res://scenes/ui/workbench_panel.gd") -const MEDIEVAL_THEME_SCRIPT: Script = preload("res://scenes/ui/medieval_theme.gd") - -# Built once in _ready and re-applied to any CanvasLayer-rooted Control because -# CanvasLayer doesn't propagate the root-Window theme cascade. -var _app_theme: Theme = null const SETTINGS_MENU_SCRIPT: Script = preload("res://scenes/ui/settings_menu.gd") # Phase 17 (Agent B) — BuildDrawer bottom-sheet (layer 16). const BUILD_DRAWER_SCRIPT: Script = preload("res://scenes/ui/build_drawer.gd") @@ -45,13 +39,6 @@ func _ready() -> void: assert(SaveSystem != null, "SaveSystem autoload missing") assert(Autosave != null, "Autosave autoload missing") - # Medieval-warm Theme — assigned on the root Window first. Cascade alone - # doesn't reach Controls inside CanvasLayers (CanvasLayer has no theme - # property), so we also walk the tree post-mount and apply to every Control - # encountered. (2026-05-16 polish pass.) - _app_theme = MEDIEVAL_THEME_SCRIPT.build() - get_tree().root.theme = _app_theme - # Phase 15 — Storyteller UI layers. Runtime-instantiated so no .tscn edit is # needed. CanvasLayer ensures correct draw order above World/TopBar regardless # of parent-node position. @@ -92,13 +79,6 @@ func _ready() -> void: pawn_detail_panel.name = "PawnDetailPanel" add_child(pawn_detail_panel) - # Bill-editor bottom-sheet for workbenches. Same shape as PawnDetailPanel - # (right-anchored 360 px, layer 18); mutually exclusive with it via Selection. - var workbench_panel := CanvasLayer.new() - workbench_panel.set_script(WORKBENCH_PANEL_SCRIPT) - workbench_panel.name = "WorkbenchPanel" - add_child(workbench_panel) - var settings_menu := CanvasLayer.new() settings_menu.set_script(SETTINGS_MENU_SCRIPT) settings_menu.name = "SettingsMenu" @@ -111,7 +91,7 @@ func _ready() -> void: if top_bar.has_method("_add_settings_btn"): top_bar._add_settings_btn() - Audit.log("main", "Phase 17 — PawnDetailPanel + WorkbenchPanel + SettingsMenu mounted.") + Audit.log("main", "Phase 17 — PawnDetailPanel + SettingsMenu mounted.") # Phase 17 (Agent B) — BuildDrawer bottom-sheet (layer 16). # Must mount AFTER the World node is ready (World._ready seeds designation_ctl). @@ -168,33 +148,3 @@ func _ready() -> void: top_bar._add_work_log_btns() Audit.log("main", "Phase 17 (Agent C) — WorkPriorityMatrix + AlertsLog mounted.") - - # Apply the medieval theme to every Control under each CanvasLayer. - # CanvasLayers interrupt the root-Window theme cascade so we have to seed - # each one explicitly. Defer one frame so panels that build their UI in - # _ready (PawnDetailPanel, WorkbenchPanel, BuildDrawer) finish first. - call_deferred("_apply_theme_to_canvas_layers") - - -## Walks the scene tree and assigns _app_theme to every Control directly under -## a CanvasLayer (the topmost Control in each layer's branch). From there the -## standard Control-to-Control cascade carries the theme to all descendants. -## Also catches popups and modals that mount later via child_entered_tree. -func _apply_theme_to_canvas_layers() -> void: - for c in get_children(): - if c is CanvasLayer: - _seed_layer_theme(c) - # Watch for late additions (popup menus, modals). - if not c.child_entered_tree.is_connected(_on_layer_child_added): - c.child_entered_tree.connect(_on_layer_child_added) - - -func _seed_layer_theme(layer: CanvasLayer) -> void: - for c in layer.get_children(): - if c is Control and c.theme == null: - c.theme = _app_theme - - -func _on_layer_child_added(node: Node) -> void: - if node is Control and node.theme == null: - node.theme = _app_theme diff --git a/scenes/pawn/pawn.gd b/scenes/pawn/pawn.gd index c3a838a..c925b2d 100644 --- a/scenes/pawn/pawn.gd +++ b/scenes/pawn/pawn.gd @@ -26,10 +26,6 @@ class_name Pawn ## Phase 14 — corpse scene instantiated on death. const CORPSE_SCENE: PackedScene = preload("res://scenes/entities/corpse.tscn") -## Slice-1 pawn sprite helper. Preloaded (not class_name) because the global -## class registry isn't reliable during cold-start parse for sibling scripts. -const _PAWN_SPRITE_FRAMES: Script = preload("res://scenes/pawn/pawn_sprite_frames.gd") - const STEP_TICKS: int = 10 const TILE_SIZE_PX: int = 16 # Mirrors World.TILE_SIZE_PX; standalone so Pawn needs no World reference. @@ -83,11 +79,6 @@ signal arrived_at_destination(tile: Vector2i) @export var pawn_name: String = "" -## Last-known facing direction as a unit Vector2i (one of (0,1) down, (-1,0) -## left, (1,0) right, (0,-1) up). Updated when walking; persists when idle -## so the idle sprite faces the last direction walked. Defaults to down. -var facing: Vector2i = Vector2i(0, 1) - var tile: Vector2i = Vector2i.ZERO # Phase 7 — hunger need (design.md "Hungry" status). Full at spawn. @@ -189,10 +180,6 @@ var _cold_accum: float = 0.0 const SHELTER_DEBUG: bool = false var _shelter_prev: bool = false -## AnimatedSprite2D child painted with peasant atlases. Built in _ready(); -## animation switched by _update_anim() each _process tick. -var _sprite: AnimatedSprite2D = null - var _path: Array[Vector2i] = [] var _step_progress: float = 0.0 var _selected: bool = false @@ -217,10 +204,6 @@ func _ready() -> void: if EventBus.has_signal("corpse_cremated"): EventBus.corpse_cremated.connect(_on_corpse_cremated) - # Sprite mount is deferred to setup() / from_dict() because the atlas pick - # depends on pawn_name (deterministic name-hash), which isn't assigned yet - # when _ready() fires. See _mount_sprite() below. - func setup(p_name: String, start_tile: Vector2i) -> void: pawn_name = p_name @@ -232,34 +215,9 @@ func setup(p_name: String, start_tile: Vector2i) -> void: # Same formula as _draw() body disc: deterministic hue from name hash. var hue := float(pawn_name.hash() % 360) / 360.0 portrait_color = Color.from_hsv(hue, 0.7, 0.85) - # Slice-1 character sprite: depends on pawn_name, so mount here (not _ready). - _mount_sprite() Audit.log("pawn", "%s spawned at %s" % [pawn_name, start_tile]) -## Build the AnimatedSprite2D child from the peasant atlas trio picked -## deterministically from pawn_name. Idempotent — safe to call from setup() -## AND from_dict() (the save-load path also re-enters setup). -func _mount_sprite() -> void: - if _sprite != null: - _sprite.queue_free() - _sprite = null - var atlases := _atlas_for_pawn(self) - var sf: SpriteFrames = _PAWN_SPRITE_FRAMES.build(atlases) - _sprite = AnimatedSprite2D.new() - _sprite.name = "Sprite" - _sprite.sprite_frames = sf - _sprite.centered = true - _sprite.offset = Vector2(0, -8) # bottom-anchor: feet ≈ tile bottom edge - # show_behind_parent lets the sprite render at the same z_index as the - # parent Pawn (so it stays above the floor TileMap), while ALSO drawing - # beneath the parent's _draw() callback — so selection ring + carry - # indicator overlay on top. z_index=-1 would sink below the floor too. - _sprite.show_behind_parent = true - _sprite.play(&"idle_down") - add_child(_sprite) - - # ── public API ────────────────────────────────────────────────────────────── func walk_along_path(new_path: Array[Vector2i]) -> void: @@ -961,8 +919,6 @@ func to_dict() -> Dictionary: # Phase 14 — bleed-out timeout counter. Default 0 for pre-Phase-14 saves. "bleed_ticks": _bleed_ticks, "last_damage_source": String(_last_damage_source), - "facing_x": facing.x, - "facing_y": facing.y, # Phase 17 — per-pawn work-priority matrix. Keys stored as plain Strings for # JSON round-trip safety (StringName keys survive the cast back via StringName()). "work_priorities": _serialise_work_priorities(), @@ -972,9 +928,6 @@ func to_dict() -> Dictionary: func from_dict(d: Dictionary) -> void: pawn_name = d.get("name", "") tile = Vector2i(int(d.get("tile_x", 0)), int(d.get("tile_y", 0))) - facing = Vector2i(int(d.get("facing_x", 0)), int(d.get("facing_y", 1))) - # Re-mount sprite now that pawn_name is set (atlas pick is name-hash driven). - _mount_sprite() _path.clear() for entry in d.get("path", []): @@ -1117,9 +1070,6 @@ func _advance_walk() -> void: job_runner.cancel_job() Audit.log("pawn", "%s walk aborted: %s became impassable" % [pawn_name, next_tile]) return - var delta := next_tile - tile - if delta != Vector2i.ZERO: - facing = _canonical_facing(delta) tile = next_tile _path.remove_at(0) _step_progress = 0.0 @@ -1137,59 +1087,37 @@ func _process(_delta: float) -> void: var next := _path[0] if is_walking() else tile var to_world := _tile_to_world(next) position = from_world.lerp(to_world, _step_progress) - _update_anim() - - -## Pick the right animation each tick based on walk state, facing, and downed. -## Called from _process(). Cheap: only calls _sprite.play() when the target -## animation differs from the current one (AnimatedSprite2D restarts from -## frame 0 on every play() call, so we must guard). -func _update_anim() -> void: - if _sprite == null: - return - if is_downed(): - if _sprite.animation != &"dead": - _sprite.play(&"dead") - return - var prefix: StringName = &"walk" if is_walking() else &"idle" - var dir_suffix: StringName = _facing_suffix() - var target: StringName = StringName("%s_%s" % [prefix, dir_suffix]) - if _sprite.animation != target: - _sprite.play(target) - - -## Map `facing` Vector2i to the animation-name suffix (down/left/right/up). -func _facing_suffix() -> StringName: - if facing == Vector2i(0, -1): - return &"up" - if facing == Vector2i(-1, 0): - return &"left" - if facing == Vector2i(1, 0): - return &"right" - return &"down" func _draw() -> void: - # Body is the AnimatedSprite2D child (see _ready). _draw() is now overlay-only. - # Selection ring — drawn on top of the sprite (parent _draw runs after the - # child's z_index=-1 sprite draws). + # Phase 14 — use the stored portrait_color (computed once in setup()/from_dict()). + # This is the same formula as the old inline hue derivation; consolidating here + # removes the duplication and ensures the corpse head-dot matches exactly. + var body_colour := portrait_color + + if is_downed(): + # Phase 9 — Downed pawn: rotated 90° (lying on ground) + desaturated. + # draw_set_transform applies to all subsequent draw_* calls in this _draw. + draw_set_transform(Vector2.ZERO, PI / 2.0, Vector2.ONE) + draw_circle(Vector2.ZERO, 6.0, body_colour.lerp(Color(0.5, 0.5, 0.5), 0.6)) + draw_arc(Vector2.ZERO, 7.0, 0.0, TAU, 24, Color(0.0, 0.0, 0.0, 0.4), 1.0) + # Reset transform so selection ring and carry indicator render upright. + draw_set_transform(Vector2.ZERO, 0.0, Vector2.ONE) + else: + draw_circle(Vector2.ZERO, 6.0, body_colour) + # Dark outline ring. + draw_arc(Vector2.ZERO, 7.0, 0.0, TAU, 24, Color(0.0, 0.0, 0.0, 0.6), 1.0) + + # Selection ring — drawn after body regardless of downed state. if _selected: draw_arc(Vector2.ZERO, 10.0, 0.0, TAU, 32, Color(1.0, 0.9, 0.2, 0.85), 2.0) - # Carry indicator — draw a shrunk version of the actual item shape at the - # pawn's upper-right ("held out at chest height"). Uses Item.draw_item_shape - # at 0.55× scale (the shapes are authored for a 12×12 box; 0.55× → ~7×7). - # Falls back to a hue-hashed square if the item type isn't shape-registered. + # Phase 4 — carry indicator: small coloured square at upper-right of body. if carried_item != null: - var ci_center := Vector2(7.0, -12.0) - var ci_scale := 0.55 - draw_set_transform(ci_center, 0.0, Vector2(ci_scale, ci_scale)) - if not Item.draw_item_shape(self, carried_item.item_type, carried_item.subtype): - var ci_hue := float(carried_item.item_type.hash() % 360) / 360.0 - var ci_color := Color.from_hsv(ci_hue, 0.6, 0.85) - draw_rect(Rect2(-6, -6, 12, 12), ci_color) - draw_rect(Rect2(-6, -6, 12, 12), Color(0, 0, 0, 0.7), false, 1.0) - draw_set_transform(Vector2.ZERO, 0.0, Vector2.ONE) + var ci_hue := float(carried_item.item_type.hash() % 360) / 360.0 + var ci_color := Color.from_hsv(ci_hue, 0.6, 0.85) + draw_rect(Rect2(6, -10, 7, 7), ci_color) + draw_rect(Rect2(6, -10, 7, 7), Color(0, 0, 0, 0.7), false, 1.0) # ── helpers ───────────────────────────────────────────────────────────────── @@ -1199,30 +1127,3 @@ func _tile_to_world(t: Vector2i) -> Vector2: t.x * TILE_SIZE_PX + TILE_SIZE_PX / 2.0, t.y * TILE_SIZE_PX + TILE_SIZE_PX / 2.0 ) - - -## Maps any tile-delta to a cardinal Vector2i facing direction. Prefers the -## axis with larger absolute magnitude; ties favor horizontal. Returns down -## (0, 1) for zero delta as a safe default. -static func _canonical_facing(delta: Vector2i) -> Vector2i: - if delta == Vector2i.ZERO: - return Vector2i(0, 1) - if abs(delta.x) >= abs(delta.y): - return Vector2i(sign(delta.x), 0) if delta.x != 0 else Vector2i(0, 1) - return Vector2i(0, sign(delta.y)) - - -## Returns the {idle, walk, dead} atlas trio for a pawn. Slice 1: always -## peasant, picked deterministically from name hash (mod 15, +1 for 001-015 -## naming). Slice 2 will branch on equipped armor (helm + cuirass + boots → -## knight atlas, etc.) at this single extension point. -const _PEASANT_COUNT: int = 15 - -static func _atlas_for_pawn(pawn) -> Dictionary: - var idx: int = (absi(pawn.pawn_name.hash()) % _PEASANT_COUNT) + 1 - var n: String = "%03d" % idx - return { - "idle": load("res://art/sprites/characters/Character_%s_Idle.png" % n), - "walk": load("res://art/sprites/characters/Character_%s_Walk.png" % n), - "dead": load("res://art/sprites/characters/Character_%s_Dead.png" % n), - } diff --git a/scenes/pawn/pawn.tscn b/scenes/pawn/pawn.tscn index 1862d14..87e312b 100644 --- a/scenes/pawn/pawn.tscn +++ b/scenes/pawn/pawn.tscn @@ -6,7 +6,7 @@ script = ExtResource("1_pawn") [node name="NameLabel" type="Label" parent="."] -position = Vector2(-20, -38) +position = Vector2(-20, -18) size = Vector2(40, 12) theme_override_font_sizes/font_size = 8 horizontal_alignment = 1 diff --git a/scenes/pawn/pawn_sprite_frames.gd b/scenes/pawn/pawn_sprite_frames.gd deleted file mode 100644 index ca5593d..0000000 --- a/scenes/pawn/pawn_sprite_frames.gd +++ /dev/null @@ -1,51 +0,0 @@ -class_name PawnSpriteFrames extends RefCounted -## Builds a SpriteFrames resource from a {idle, walk, dead} atlas trio for a -## peasant character. Atlases are 128×128 with 4 rows (down/left/right/up) -## × 4 frames (32×32 cells). Idle + Walk produce 4 directional animations -## each (loop=true); Dead is a single frame (loop=false) from row 0. -## -## Created via PawnSpriteFrames.build(atlases) from Pawn._ready(). The -## returned SpriteFrames is assigned to an AnimatedSprite2D's sprite_frames. - -const CELL: int = 32 -const DIRS: Array[StringName] = [&"down", &"left", &"right", &"up"] - -## Build a SpriteFrames containing: -## idle_down/left/right/up — 4 frames each, loop, 4 fps -## walk_down/left/right/up — 4 frames each, loop, 8 fps -## dead — 1 frame, no loop (from idle/dead row 0 col 0) -## -## `atlases` is a Dictionary with three Texture2D values keyed by "idle", -## "walk", "dead". Each texture is 128×128. -static func build(atlases: Dictionary) -> SpriteFrames: - var sf := SpriteFrames.new() - # AnimatedSprite2D auto-creates a `default` animation; remove it so the - # scene doesn't render an empty placeholder if a caller mistypes an anim name. - if sf.has_animation(&"default"): - sf.remove_animation(&"default") - - _add_directional(sf, &"idle", atlases["idle"], true, 4.0) - _add_directional(sf, &"walk", atlases["walk"], true, 8.0) - - # Dead — single 32×32 frame from row 0 (down-facing) of the dead atlas. - sf.add_animation(&"dead") - sf.set_animation_loop(&"dead", false) - var dead_at := AtlasTexture.new() - dead_at.atlas = atlases["dead"] - dead_at.region = Rect2(0, 0, CELL, CELL) - sf.add_frame(&"dead", dead_at) - - return sf - - -static func _add_directional(sf: SpriteFrames, prefix: StringName, tex: Texture2D, loop: bool, fps: float) -> void: - for row in 4: - var anim_name := StringName("%s_%s" % [prefix, DIRS[row]]) - sf.add_animation(anim_name) - sf.set_animation_loop(anim_name, loop) - sf.set_animation_speed(anim_name, fps) - for col in 4: - var at := AtlasTexture.new() - at.atlas = tex - at.region = Rect2(col * CELL, row * CELL, CELL, CELL) - sf.add_frame(anim_name, at) diff --git a/scenes/pawn/pawn_sprite_frames.gd.uid b/scenes/pawn/pawn_sprite_frames.gd.uid deleted file mode 100644 index 42721ff..0000000 --- a/scenes/pawn/pawn_sprite_frames.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c4otxk5jg0kxr diff --git a/scenes/ui/build_drawer.gd b/scenes/ui/build_drawer.gd index ea85b1b..bacfa28 100644 --- a/scenes/ui/build_drawer.gd +++ b/scenes/ui/build_drawer.gd @@ -180,11 +180,10 @@ func _build_designate_tab() -> Control: var flow := _make_flow_grid() box.add_child(flow) - _add_tool_btn(flow, Strings.t(&"tool.chop"), &"chop", func() -> void: _activate(&"chop", &"", Strings.t(&"tool.chop"))) - _add_tool_btn(flow, Strings.t(&"tool.mine"), &"mine", func() -> void: _activate(&"mine", &"", Strings.t(&"tool.mine"))) - _add_tool_btn(flow, Strings.t(&"tool.dig_grave"), &"dig_grave", func() -> void: _activate(&"dig_grave", &"", Strings.t(&"tool.dig_grave"))) - _add_tool_btn(flow, Strings.t(&"tool.no_roof"), &"no_roof", func() -> void: _activate(&"no_roof", &"", Strings.t(&"tool.no_roof"))) - _add_tool_btn(flow, Strings.t(&"tool.plant_tree"), &"plant_tree", func() -> void: _activate(&"plant_tree", &"", Strings.t(&"tool.plant_tree"))) + _add_tool_btn(flow, Strings.t(&"tool.chop"), Color(0.3, 0.7, 0.2), func() -> void: _activate(&"chop", &"", Strings.t(&"tool.chop"))) + _add_tool_btn(flow, Strings.t(&"tool.mine"), Color(0.6, 0.6, 0.6), func() -> void: _activate(&"mine", &"", Strings.t(&"tool.mine"))) + _add_tool_btn(flow, Strings.t(&"tool.dig_grave"),Color(0.4, 0.3, 0.2), func() -> void: _activate(&"dig_grave",&"", Strings.t(&"tool.dig_grave"))) + _add_tool_btn(flow, Strings.t(&"tool.no_roof"), Color(0.7, 0.7, 0.9), func() -> void: _activate(&"no_roof", &"", Strings.t(&"tool.no_roof"))) return box @@ -198,52 +197,46 @@ func _build_build_tab() -> Control: box.add_child(flow) # Wall — show material chooser on first tap. - _add_tool_btn(flow, Strings.t(&"tool.build_wall_stone"), &"build_wall_stone", + _add_tool_btn(flow, Strings.t(&"tool.build_wall_stone"), Color(0.55, 0.55, 0.55), func() -> void: _activate_wall(&"stone")) - _add_tool_btn(flow, Strings.t(&"tool.build_wall_wood"), &"build_wall_wood", + _add_tool_btn(flow, Strings.t(&"tool.build_wall_wood"), Color(0.65, 0.45, 0.25), func() -> void: _activate_wall(&"wood")) # Floor. - _add_tool_btn(flow, Strings.t(&"tool.build_floor_wood"), &"build_floor_wood", + _add_tool_btn(flow, Strings.t(&"tool.build_floor_wood"), Color(0.60, 0.40, 0.20), func() -> void: _activate_floor(&"wood")) - _add_tool_btn(flow, Strings.t(&"tool.build_floor_stone"), &"build_floor_stone", + _add_tool_btn(flow, Strings.t(&"tool.build_floor_stone"), Color(0.60, 0.60, 0.55), func() -> void: _activate_floor(&"stone")) # Door + Crate. - _add_tool_btn(flow, Strings.t(&"tool.build_door"), &"build_door", + _add_tool_btn(flow, Strings.t(&"tool.build_door"), Color(0.55, 0.35, 0.15), func() -> void: _activate(&"build_door", &"", Strings.t(&"tool.build_door"))) - _add_tool_btn(flow, Strings.t(&"tool.build_crate"), &"build_crate", + _add_tool_btn(flow, Strings.t(&"tool.build_crate"), Color(0.65, 0.45, 0.10), func() -> void: _activate(&"build_crate", &"", Strings.t(&"tool.build_crate"))) # Bed + Torch. - _add_tool_btn(flow, Strings.t(&"tool.build_bed"), &"build_bed", + _add_tool_btn(flow, Strings.t(&"tool.build_bed"), Color(0.40, 0.40, 0.80), func() -> void: _activate(&"build_bed", &"", Strings.t(&"tool.build_bed"))) - _add_tool_btn(flow, Strings.t(&"tool.build_torch"), &"build_torch", + _add_tool_btn(flow, Strings.t(&"tool.build_torch"), Color(0.90, 0.70, 0.20), func() -> void: _activate(&"build_torch", &"", Strings.t(&"tool.build_torch"))) # Workbenches. _add_tool_btn(flow, Strings.t(&"tool.workbench_carpenter"), - &"build_workbench_carpenter", + Color(0.50, 0.35, 0.15), func() -> void: _activate(&"build_workbench_carpenter", &"", Strings.t(&"tool.workbench_carpenter"))) _add_tool_btn(flow, Strings.t(&"tool.workbench_smelter"), - &"build_workbench_smelter", + Color(0.60, 0.55, 0.45), func() -> void: _activate(&"build_workbench_smelter", &"", Strings.t(&"tool.workbench_smelter"))) _add_tool_btn(flow, Strings.t(&"tool.workbench_millstone"), - &"build_workbench_millstone", + Color(0.55, 0.55, 0.55), func() -> void: _activate(&"build_workbench_millstone", &"", Strings.t(&"tool.workbench_millstone"))) _add_tool_btn(flow, Strings.t(&"tool.workbench_hearth"), - &"build_workbench_hearth", + Color(0.80, 0.35, 0.15), func() -> void: _activate(&"build_workbench_hearth", &"", Strings.t(&"tool.workbench_hearth"))) _add_tool_btn(flow, Strings.t(&"tool.workbench_cremation_pyre"), - &"build_workbench_cremation_pyre", + Color(0.30, 0.25, 0.20), func() -> void: _activate(&"build_workbench_cremation_pyre", &"", Strings.t(&"tool.workbench_cremation_pyre"))) - # Quarry — must be painted on a stone outcrop (BigRockNode); world.gd - # rejects placements on plain ground. - _add_tool_btn(flow, Strings.t(&"tool.paint_quarry"), - &"paint_quarry", - func() -> void: _activate(&"paint_quarry", &"", Strings.t(&"tool.paint_quarry"))) - return box @@ -255,9 +248,9 @@ func _build_stockpile_tab() -> Control: var flow := _make_flow_grid() box.add_child(flow) - _add_tool_btn(flow, Strings.t(&"tool.stockpile_general"), &"paint_stockpile", + _add_tool_btn(flow, Strings.t(&"tool.stockpile_general"), Color(0.30, 0.60, 0.30), func() -> void: _activate(&"paint_stockpile", &"", Strings.t(&"tool.stockpile_general"))) - _add_tool_btn(flow, Strings.t(&"tool.graveyard"), &"graveyard", + _add_tool_btn(flow, Strings.t(&"tool.graveyard"), Color(0.25, 0.20, 0.15), func() -> void: _activate(&"graveyard", &"", Strings.t(&"tool.graveyard"))) return box @@ -298,13 +291,10 @@ func _make_flow_grid() -> GridContainer: return g -const _THUMB_SCRIPT: Script = preload("res://scenes/ui/build_drawer_thumb.gd") - - -## Add a single tool button to `container`. The button is a VBoxContainer of -## [thumb preview + Label] wrapped in a Button so the whole cell is one touch -## target. `tool_id` drives the procedural preview shape (BuildDrawerThumb). -func _add_tool_btn(container: Control, label_text: String, tool_id: StringName, callback: Callable) -> void: +## Add a single tool button to `container`. Each button is a VBoxContainer of +## [ColorRect icon area + Label] wrapped in a Button so the whole cell is one +## touch target. +func _add_tool_btn(container: Control, label_text: String, icon_color: Color, callback: Callable) -> void: var btn := Button.new() btn.custom_minimum_size = Vector2(BTN_SIZE, BTN_SIZE + LABEL_HEIGHT) btn.focus_mode = Control.FOCUS_NONE @@ -313,16 +303,13 @@ func _add_tool_btn(container: Control, label_text: String, tool_id: StringName, vb.mouse_filter = Control.MOUSE_FILTER_IGNORE vb.add_theme_constant_override("separation", 2) - # Procedural preview of the entity this tool builds. - var thumb := Control.new() - thumb.set_script(_THUMB_SCRIPT) - # Use .set() — the static type is Control (set_script doesn't refine it), - # but the runtime instance has the tool_id property from the script. - thumb.set("tool_id", tool_id) - thumb.custom_minimum_size = Vector2(BTN_SIZE - 8, BTN_SIZE - LABEL_HEIGHT - 8) - thumb.size_flags_horizontal = Control.SIZE_SHRINK_CENTER - thumb.mouse_filter = Control.MOUSE_FILTER_IGNORE - vb.add_child(thumb) + # Icon area — procedural colored rect (real sprites land with Phase 17 art pass). + var icon := ColorRect.new() + icon.color = icon_color + icon.custom_minimum_size = Vector2(BTN_SIZE - 8, BTN_SIZE - LABEL_HEIGHT - 8) + icon.size_flags_horizontal = Control.SIZE_SHRINK_CENTER + icon.mouse_filter = Control.MOUSE_FILTER_IGNORE + vb.add_child(icon) # Label. var lbl := Label.new() diff --git a/scenes/ui/build_drawer_thumb.gd b/scenes/ui/build_drawer_thumb.gd deleted file mode 100644 index d6c07d9..0000000 --- a/scenes/ui/build_drawer_thumb.gd +++ /dev/null @@ -1,378 +0,0 @@ -class_name BuildDrawerThumb extends Control -## Procedural preview thumbnail for one BuildDrawer tool button. -## -## Each tool_id dispatches to a small _draw call that renders a recognisable -## silhouette of the entity that tool builds. All shapes live inside a 40×40 -## box centred in this control's size — the parent button supplies the panel -## frame; this widget only paints the icon. -## -## Cheaper than instantiating live entity scenes into SubViewports; pure draw -## calls, no allocations, recomputes only when the button repaints. - -## Tool identifier — drives the shape dispatch in _draw. -var tool_id: StringName = &"" - - -func _draw() -> void: - var c := size / 2.0 + Vector2(0, -2) # slight upward bias for label clearance - # Soft shadow under every thumb (subtle depth cue against the parchment). - draw_circle(c + Vector2(0, 12), 14.0, Color(0, 0, 0, 0.08)) - var outline := Color(0.10, 0.07, 0.05, 0.85) - match tool_id: - &"build_wall_stone": - # Grey brick wall with staggered courses. - var brick := Color(0.62, 0.60, 0.58) - var brick_hi := Color(0.78, 0.76, 0.72) - var mortar := Color(0.30, 0.28, 0.26) - var r := Rect2(c.x - 16, c.y - 16, 32, 32) - draw_rect(r, brick) - # Three brick courses, staggered seams. - for i in 3: - var y: float = r.position.y + i * 11 + 4 - draw_line(Vector2(r.position.x, y), Vector2(r.end.x, y), mortar, 1.0) - # Vertical seams (staggered per row). - for i in 3: - var y: float = r.position.y + i * 11 + 4 - var stagger: float = 0.0 if i % 2 == 0 else 8.0 - var x_offs: Array[float] = [-8.0, 0.0, 8.0] - for x_off in x_offs: - var x: float = c.x + x_off + stagger - if x >= r.position.x and x <= r.end.x: - draw_line(Vector2(x, y - 11), Vector2(x, y), mortar, 1.0) - # Top edge highlight. - draw_rect(Rect2(r.position.x, r.position.y, r.size.x, 3), brick_hi) - draw_rect(r, outline, false, 1.0) - &"build_wall_wood": - # Brown wood-plank wall, 3 vertical planks. - var wood := Color(0.65, 0.45, 0.25) - var wood_dark := Color(0.42, 0.27, 0.12) - var r := Rect2(c.x - 16, c.y - 16, 32, 32) - draw_rect(r, wood) - draw_line(Vector2(c.x - 5.5, r.position.y), Vector2(c.x - 5.5, r.end.y), wood_dark, 1.0) - draw_line(Vector2(c.x + 5.5, r.position.y), Vector2(c.x + 5.5, r.end.y), wood_dark, 1.0) - # Knot details (small dots). - draw_circle(Vector2(c.x - 11, c.y - 6), 1.2, wood_dark) - draw_circle(Vector2(c.x + 1, c.y + 4), 1.2, wood_dark) - draw_rect(r, outline, false, 1.0) - &"build_floor_wood": - # Tan floorboard plan-view — 4 planks horizontal with grain. - var board := Color(0.78, 0.58, 0.32) - var board_dark := Color(0.50, 0.32, 0.16) - var r := Rect2(c.x - 16, c.y - 12, 32, 24) - draw_rect(r, board) - for i in 1.0: - pass # placeholder loop - draw_line(Vector2(r.position.x, c.y - 6), Vector2(r.end.x, c.y - 6), board_dark, 1.0) - draw_line(Vector2(r.position.x, c.y), Vector2(r.end.x, c.y), board_dark, 1.0) - draw_line(Vector2(r.position.x, c.y + 6), Vector2(r.end.x, c.y + 6), board_dark, 1.0) - draw_rect(r, outline, false, 1.0) - &"build_floor_stone": - # Grey paving stones — 2×2 grid with cross-mortar. - var stone := Color(0.65, 0.63, 0.60) - var mortar := Color(0.35, 0.33, 0.30) - var r := Rect2(c.x - 16, c.y - 12, 32, 24) - draw_rect(r, stone) - draw_line(Vector2(c.x, r.position.y), Vector2(c.x, r.end.y), mortar, 1.0) - draw_line(Vector2(r.position.x, c.y), Vector2(r.end.x, c.y), mortar, 1.0) - draw_rect(r, outline, false, 1.0) - &"build_door": - # Wooden door — rounded-top arch silhouette with a handle dot. - var door := Color(0.55, 0.35, 0.15) - var door_hi := Color(0.75, 0.50, 0.25) - var handle := Color(0.95, 0.80, 0.20) - # Body - var pts: PackedVector2Array = PackedVector2Array([ - Vector2(c.x - 10, c.y + 16), Vector2(c.x - 10, c.y - 6), - Vector2(c.x - 8, c.y - 12), Vector2(c.x, c.y - 16), - Vector2(c.x + 8, c.y - 12), Vector2(c.x + 10, c.y - 6), - Vector2(c.x + 10, c.y + 16), - ]) - draw_colored_polygon(pts, door) - # Inner plank seam. - draw_line(Vector2(c.x, c.y - 14), Vector2(c.x, c.y + 14), door_hi, 1.0) - # Handle. - draw_circle(Vector2(c.x + 5, c.y + 4), 1.5, handle) - draw_polyline(pts + PackedVector2Array([pts[0]]), outline, 1.0) - &"build_crate": - # Wooden crate — brown box with X cross-bracing on the front. - var wood := Color(0.65, 0.42, 0.18) - var wood_dark := Color(0.42, 0.27, 0.10) - var r := Rect2(c.x - 14, c.y - 12, 28, 24) - draw_rect(r, wood) - # Top edge bevel. - draw_rect(Rect2(r.position.x, r.position.y, r.size.x, 3), Color(0.85, 0.62, 0.30)) - # X cross-bracing. - draw_line(r.position, r.end, wood_dark, 2.0) - draw_line(Vector2(r.position.x, r.end.y), Vector2(r.end.x, r.position.y), wood_dark, 2.0) - draw_rect(r, outline, false, 1.0) - &"build_bed": - # Top-down bed view — wood frame, pillow top, blanket body. - var frame := Color(0.55, 0.35, 0.15) - var blanket := Color(0.55, 0.42, 0.78) - var pillow := Color(0.95, 0.93, 0.85) - var r := Rect2(c.x - 12, c.y - 14, 24, 28) - # Frame. - draw_rect(r, frame) - # Blanket inside (slightly inset). - draw_rect(Rect2(r.position.x + 2, r.position.y + 8, r.size.x - 4, r.size.y - 10), blanket) - # Pillow at top. - draw_rect(Rect2(r.position.x + 2, r.position.y + 2, r.size.x - 4, 5), pillow) - # Outline. - draw_rect(r, outline, false, 1.0) - &"build_torch": - # Wall torch — vertical brown shaft with orange flame above. - var shaft := Color(0.45, 0.28, 0.12) - var bracket := Color(0.35, 0.35, 0.38) - var flame_outer := Color(0.95, 0.40, 0.05) - var flame_inner := Color(1.00, 0.85, 0.30) - # Wall bracket - draw_rect(Rect2(c.x - 8, c.y + 6, 16, 4), bracket) - # Torch shaft. - draw_rect(Rect2(c.x - 2, c.y - 6, 4, 14), shaft) - # Flame teardrop. - draw_circle(Vector2(c.x, c.y - 10), 5.0, flame_outer) - draw_circle(Vector2(c.x, c.y - 12), 2.5, flame_inner) - # Smoke wisp. - draw_line(Vector2(c.x, c.y - 16), Vector2(c.x + 2, c.y - 20), Color(0.70, 0.70, 0.70, 0.6), 1.0) - &"build_workbench_carpenter": - # Wood bench with saw on top, two legs visible. - var plank_top := Color(0.78, 0.55, 0.30) - var plank_front := Color(0.55, 0.38, 0.22) - var leg := Color(0.32, 0.22, 0.10) - var saw_blade := Color(0.82, 0.82, 0.85) - var saw_handle := Color(0.55, 0.30, 0.15) - # Legs. - draw_rect(Rect2(c.x - 14, c.y, 3, 16), leg) - draw_rect(Rect2(c.x + 11, c.y, 3, 16), leg) - # Bench body. - draw_rect(Rect2(c.x - 16, c.y - 4, 32, 8), plank_front) - draw_rect(Rect2(c.x - 16, c.y - 8, 32, 4), plank_top) - # Saw blade. - draw_rect(Rect2(c.x - 4, c.y - 12, 12, 2), saw_blade) - draw_rect(Rect2(c.x + 8, c.y - 13, 4, 4), saw_handle) - draw_rect(Rect2(c.x - 16, c.y - 12, 32, 16), outline, false, 1.0) - &"build_workbench_smelter": - # Stone furnace with ember opening + smoke from chimney. - var stone := Color(0.55, 0.55, 0.55) - var stone_front := Color(0.42, 0.42, 0.43) - var ember := Color(0.98, 0.55, 0.10) - var ember_core := Color(1.00, 0.85, 0.30) - var chimney := Color(0.32, 0.30, 0.30) - var smoke := Color(0.75, 0.73, 0.70, 0.7) - # Stone body. - draw_rect(Rect2(c.x - 14, c.y - 8, 28, 22), stone_front) - draw_rect(Rect2(c.x - 14, c.y - 11, 28, 4), stone) - # Ember mouth. - draw_rect(Rect2(c.x - 7, c.y - 2, 14, 8), Color(0.18, 0.10, 0.06)) - draw_rect(Rect2(c.x - 5, c.y, 10, 4), ember) - draw_rect(Rect2(c.x - 3, c.y + 1, 6, 2), ember_core) - # Chimney + smoke. - draw_rect(Rect2(c.x + 4, c.y - 16, 5, 6), chimney) - draw_line(Vector2(c.x + 6, c.y - 18), Vector2(c.x + 5, c.y - 23), smoke, 1.5) - draw_rect(Rect2(c.x - 14, c.y - 11, 28, 25), outline, false, 1.0) - &"build_workbench_millstone": - # Wood frame + round grindstone wheel. - var frame_front := Color(0.42, 0.26, 0.12) - var frame_top := Color(0.55, 0.36, 0.18) - var wheel := Color(0.55, 0.53, 0.50) - var wheel_rim := Color(0.20, 0.18, 0.16) - var wheel_dark := Color(0.34, 0.32, 0.30) - # Frame. - draw_rect(Rect2(c.x - 14, c.y + 2, 28, 12), frame_front) - draw_rect(Rect2(c.x - 14, c.y - 1, 28, 5), frame_top) - # Wheel. - draw_circle(c + Vector2(0, -4), 11.0, wheel_rim) - draw_circle(c + Vector2(0, -4), 9.5, wheel) - # Front-face shadow (lower half). - draw_rect(Rect2(c.x - 9, c.y - 4, 18, 9), wheel_dark) - # Center pin. - draw_circle(c + Vector2(0, -4), 2.0, Color(0.18, 0.16, 0.14)) - draw_rect(Rect2(c.x - 14, c.y - 15, 28, 29), outline, false, 1.0) - &"build_workbench_hearth": - # Tall stone fireplace with wood mantle + flame. - var stone := Color(0.60, 0.58, 0.55) - var stone_dark := Color(0.42, 0.40, 0.38) - var mantle := Color(0.50, 0.34, 0.20) - var opening := Color(0.10, 0.05, 0.02) - var flame_outer := Color(0.95, 0.40, 0.05) - var flame_inner := Color(1.00, 0.85, 0.30) - var log_wood := Color(0.55, 0.32, 0.15) - # Stone surround. - draw_rect(Rect2(c.x - 14, c.y - 16, 28, 32), stone) - draw_line(Vector2(c.x - 14, c.y - 8), Vector2(c.x + 14, c.y - 8), stone_dark, 1.0) - # Mantle band. - draw_rect(Rect2(c.x - 14, c.y - 6, 28, 3), mantle) - # Opening. - draw_rect(Rect2(c.x - 9, c.y - 2, 18, 18), opening) - # Log. - draw_rect(Rect2(c.x - 6, c.y + 10, 12, 3), log_wood) - # Flame. - draw_rect(Rect2(c.x - 4, c.y + 4, 8, 6), flame_outer) - draw_rect(Rect2(c.x - 3, c.y + 1, 6, 4), flame_outer) - draw_rect(Rect2(c.x - 2, c.y + 4, 4, 4), flame_inner) - draw_rect(Rect2(c.x - 14, c.y - 16, 28, 32), outline, false, 1.0) - &"build_workbench_cremation_pyre": - # Charred wood pile with ember glow + ash smoke. - var base_top := Color(0.30, 0.22, 0.12) - var base_front := Color(0.22, 0.15, 0.08) - var ember := Color(0.95, 0.45, 0.10) - var ash_grey := Color(0.70, 0.68, 0.65, 0.7) - draw_rect(Rect2(c.x - 14, c.y - 4, 28, 16), base_front) - draw_rect(Rect2(c.x - 14, c.y - 8, 28, 4), base_top) - draw_rect(Rect2(c.x - 9, c.y + 2, 18, 4), ember) - # Smoke wisps. - draw_rect(Rect2(c.x - 5, c.y - 14, 2, 6), ash_grey) - draw_rect(Rect2(c.x + 1, c.y - 16, 2, 8), ash_grey) - draw_rect(Rect2(c.x + 5, c.y - 12, 2, 4), ash_grey) - draw_rect(Rect2(c.x - 14, c.y - 8, 28, 20), outline, false, 1.0) - &"paint_stockpile": - # Green tile with dashed boundary — a designated stockpile zone. - var fill := Color(0.35, 0.65, 0.30, 0.45) - var border := Color(0.20, 0.45, 0.15) - var r := Rect2(c.x - 14, c.y - 12, 28, 24) - draw_rect(r, fill) - # Dashed border: 4 dashes per side. - for i in 4: - draw_line(Vector2(r.position.x + i * 7, r.position.y), - Vector2(r.position.x + i * 7 + 4, r.position.y), border, 2.0) - draw_line(Vector2(r.position.x + i * 7, r.end.y), - Vector2(r.position.x + i * 7 + 4, r.end.y), border, 2.0) - for i in 3: - draw_line(Vector2(r.position.x, r.position.y + i * 8), - Vector2(r.position.x, r.position.y + i * 8 + 4), border, 2.0) - draw_line(Vector2(r.end.x, r.position.y + i * 8), - Vector2(r.end.x, r.position.y + i * 8 + 4), border, 2.0) - &"graveyard": - # Dark earth tile + grave cross marker. - var earth := Color(0.35, 0.28, 0.20) - var earth_hi := Color(0.50, 0.40, 0.28) - var cross := Color(0.78, 0.78, 0.76) - var r := Rect2(c.x - 14, c.y - 12, 28, 24) - draw_rect(r, earth) - draw_line(Vector2(r.position.x, r.position.y + 3), Vector2(r.end.x, r.position.y + 3), earth_hi, 1.0) - # Cross (gravestone marker). - draw_rect(Rect2(c.x - 1.5, c.y - 8, 3, 18), cross) - draw_rect(Rect2(c.x - 6, c.y - 4, 12, 3), cross) - draw_rect(r, outline, false, 1.0) - &"chop": - # Axe head + handle silhouette over a green target tile. - var grass := Color(0.35, 0.65, 0.30, 0.35) - var handle := Color(0.55, 0.35, 0.15) - var blade := Color(0.78, 0.80, 0.85) - var blade_dark := Color(0.45, 0.48, 0.52) - draw_rect(Rect2(c.x - 14, c.y - 12, 28, 24), grass) - # Handle (diagonal). - draw_line(Vector2(c.x - 8, c.y + 10), Vector2(c.x + 6, c.y - 8), handle, 3.0) - # Axe head. - var ax_pts: PackedVector2Array = PackedVector2Array([ - Vector2(c.x + 2, c.y - 12), Vector2(c.x + 12, c.y - 6), - Vector2(c.x + 8, c.y), Vector2(c.x - 2, c.y - 4), - ]) - draw_colored_polygon(ax_pts, blade) - draw_polyline(ax_pts + PackedVector2Array([ax_pts[0]]), blade_dark, 1.0) - &"mine": - # Pickaxe over a grey stone tile. - var stone := Color(0.62, 0.60, 0.58, 0.4) - var handle := Color(0.55, 0.35, 0.15) - var head := Color(0.48, 0.48, 0.52) - var head_dark := Color(0.28, 0.28, 0.32) - draw_rect(Rect2(c.x - 14, c.y - 12, 28, 24), stone) - # Handle. - draw_line(Vector2(c.x - 8, c.y + 10), Vector2(c.x + 6, c.y - 8), handle, 3.0) - # Two-pointed pickaxe head. - var pk_pts: PackedVector2Array = PackedVector2Array([ - Vector2(c.x - 6, c.y - 12), Vector2(c.x + 12, c.y - 6), - Vector2(c.x + 4, c.y - 2), Vector2(c.x, c.y - 6), - Vector2(c.x - 4, c.y - 4), - ]) - draw_colored_polygon(pk_pts, head) - draw_polyline(pk_pts + PackedVector2Array([pk_pts[0]]), head_dark, 1.0) - &"dig_grave": - # Shovel + earth mound. - var earth := Color(0.35, 0.25, 0.15) - var earth_hi := Color(0.55, 0.40, 0.25) - var handle := Color(0.55, 0.35, 0.15) - var blade := Color(0.55, 0.55, 0.60) - # Earth mound at bottom. - var mound: PackedVector2Array = PackedVector2Array([ - Vector2(c.x - 14, c.y + 12), Vector2(c.x + 14, c.y + 12), - Vector2(c.x + 8, c.y + 4), Vector2(c.x - 8, c.y + 4), - ]) - draw_colored_polygon(mound, earth) - draw_line(Vector2(c.x - 6, c.y + 6), Vector2(c.x + 6, c.y + 6), earth_hi, 1.0) - # Shovel handle. - draw_line(Vector2(c.x - 8, c.y + 10), Vector2(c.x + 4, c.y - 10), handle, 3.0) - # Shovel blade. - var sh_pts: PackedVector2Array = PackedVector2Array([ - Vector2(c.x + 3, c.y - 12), Vector2(c.x + 10, c.y - 10), - Vector2(c.x + 10, c.y - 4), Vector2(c.x + 4, c.y - 4), - ]) - draw_colored_polygon(sh_pts, blade) - &"no_roof": - # Open square with up-arrow (cancel-roof designation). - var sky := Color(0.55, 0.75, 0.95, 0.4) - var border := Color(0.20, 0.40, 0.65) - var arrow := Color(0.95, 0.95, 0.95) - # Square outline (dashed corners) representing the cell. - draw_rect(Rect2(c.x - 14, c.y - 12, 28, 24), sky) - # Corner brackets. - var corners: Array = [ - [Vector2(c.x - 14, c.y - 12), Vector2(1, 0), Vector2(0, 1)], - [Vector2(c.x + 14, c.y - 12), Vector2(-1, 0), Vector2(0, 1)], - [Vector2(c.x - 14, c.y + 12), Vector2(1, 0), Vector2(0, -1)], - [Vector2(c.x + 14, c.y + 12), Vector2(-1, 0), Vector2(0, -1)], - ] - for corner in corners: - var pos: Vector2 = corner[0] - var dx: Vector2 = corner[1] - var dy: Vector2 = corner[2] - draw_line(pos, pos + dx * 5.0, border, 2.0) - draw_line(pos, pos + dy * 5.0, border, 2.0) - # Up arrow centred. - draw_line(Vector2(c.x, c.y + 6), Vector2(c.x, c.y - 6), arrow, 2.0) - draw_line(Vector2(c.x, c.y - 6), Vector2(c.x - 4, c.y - 2), arrow, 2.0) - draw_line(Vector2(c.x, c.y - 6), Vector2(c.x + 4, c.y - 2), arrow, 2.0) - &"paint_quarry": - # Stone block on wood frame + small pile of cut stones beside. - var frame := Color(0.42, 0.26, 0.12) - var frame_top := Color(0.55, 0.36, 0.18) - var stone := Color(0.65, 0.63, 0.60) - var stone_hi := Color(0.82, 0.80, 0.76) - var stone_dark := Color(0.32, 0.30, 0.28) - var chisel_steel := Color(0.78, 0.80, 0.85) - # Wood frame base. - draw_rect(Rect2(c.x - 14, c.y + 6, 28, 8), frame) - draw_rect(Rect2(c.x - 14, c.y + 3, 28, 3), frame_top) - # Large stone block on top. - draw_rect(Rect2(c.x - 10, c.y - 8, 14, 11), stone) - draw_rect(Rect2(c.x - 10, c.y - 8, 14, 3), stone_hi) - # Chisel marks (3 short dark lines on the stone face). - draw_line(Vector2(c.x - 6, c.y - 3), Vector2(c.x - 4, c.y - 3), stone_dark, 1.0) - draw_line(Vector2(c.x - 2, c.y - 3), Vector2(c.x, c.y - 3), stone_dark, 1.0) - draw_line(Vector2(c.x + 2, c.y - 3), Vector2(c.x - 0, c.y - 3), stone_dark, 1.0) - # Pile of cut stones beside the block. - draw_rect(Rect2(c.x + 6, c.y, 4, 3), stone) - draw_rect(Rect2(c.x + 8, c.y - 3, 3, 3), stone_hi) - # Chisel tool on top of block. - draw_rect(Rect2(c.x - 4, c.y - 12, 2, 4), chisel_steel) - draw_rect(Rect2(c.x - 5, c.y - 13, 4, 2), frame_top) - &"plant_tree": - # Green sapling sprout rising from dirt — signals manual tree planting. - var dirt := Color(0.45, 0.32, 0.18) - var dirt_hi := Color(0.62, 0.46, 0.28) - var stem := Color(0.35, 0.22, 0.10) - var leaf_a := Color(0.30, 0.65, 0.20) - var leaf_b := Color(0.25, 0.55, 0.18) - # Dirt base rectangle. - draw_rect(Rect2(c.x - 14, c.y + 2, 28, 14), dirt) - draw_line(Vector2(c.x - 14, c.y + 5), Vector2(c.x + 14, c.y + 5), dirt_hi, 1.0) - # Stem. - draw_line(Vector2(c.x, c.y + 2), Vector2(c.x, c.y - 8), stem, 2.0) - # Three leaf dots at top of stem. - draw_circle(Vector2(c.x, c.y - 10), 4.0, leaf_a) - draw_circle(Vector2(c.x - 5, c.y - 6), 3.0, leaf_b) - draw_circle(Vector2(c.x + 5, c.y - 5), 3.0, leaf_b) - _: - # Unknown tool — small grey placeholder. - draw_rect(Rect2(c.x - 12, c.y - 12, 24, 24), Color(0.50, 0.50, 0.50)) - draw_rect(Rect2(c.x - 12, c.y - 12, 24, 24), outline, false, 1.0) diff --git a/scenes/ui/build_drawer_thumb.gd.uid b/scenes/ui/build_drawer_thumb.gd.uid deleted file mode 100644 index 6d94197..0000000 --- a/scenes/ui/build_drawer_thumb.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://w802akkpbc6l diff --git a/scenes/ui/inspect_tooltip.gd b/scenes/ui/inspect_tooltip.gd index da793cd..c3a716d 100644 --- a/scenes/ui/inspect_tooltip.gd +++ b/scenes/ui/inspect_tooltip.gd @@ -264,36 +264,12 @@ 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]%s[/b]\nChop %d%%%s" % [stage_label, pct, tag] - return "[b]%s[/b]%s" % [stage_label, tag] + return "[b]Tree[/b]\nChop %d%%%s" % [pct, tag] + return "[b]Tree[/b]%s" % tag func _describe_rock(r) -> String: diff --git a/scenes/ui/medieval_theme.gd b/scenes/ui/medieval_theme.gd deleted file mode 100644 index afecbf7..0000000 --- a/scenes/ui/medieval_theme.gd +++ /dev/null @@ -1,198 +0,0 @@ -class_name MedievalTheme extends RefCounted -## Builds a Theme resource implementing the "Medieval warm" palette: -## Panel: tan (198, 168, 128) -## Border: dark wood (90, 55, 30) -## Button: parchment (230, 210, 170), hover lighter, pressed inset shadow -## Text: ink black with off-white on dark panels -## -## Applied on Main scene root so it cascades to every Control descendant. -## Per-panel overrides are still possible — only nodes that don't explicitly -## override a style pick up the global Theme. - -# ── palette ────────────────────────────────────────────────────────────────── -const C_PANEL := Color(0.776, 0.659, 0.502) # tan -const C_PANEL_DARK := Color(0.353, 0.216, 0.118) # dark wood -const C_BUTTON := Color(0.902, 0.824, 0.667) # parchment -const C_BUTTON_HOV := Color(0.961, 0.902, 0.769) # warm parchment -const C_BUTTON_PRESS:= Color(0.776, 0.694, 0.529) # pressed shadow -const C_BUTTON_DIS := Color(0.690, 0.620, 0.510) # disabled -const C_INK := Color(0.106, 0.078, 0.039) -const C_INK_DIM := Color(0.353, 0.275, 0.196) -const C_ACCENT := Color(0.580, 0.180, 0.110) # wax-seal red, for selected tabs -const C_ACCENT_GOLD := Color(0.831, 0.620, 0.149) - - -static func build() -> Theme: - var theme := Theme.new() - # Default font size — small UI, but legible on phone. - theme.default_font_size = 14 - - # ── Button (drives OptionButton + MenuButton too) ───────────────────────── - theme.set_stylebox("normal", "Button", _btn_box(C_BUTTON, false)) - theme.set_stylebox("hover", "Button", _btn_box(C_BUTTON_HOV, false)) - theme.set_stylebox("pressed", "Button", _btn_box(C_BUTTON_PRESS, true)) - theme.set_stylebox("disabled", "Button", _btn_box(C_BUTTON_DIS, false)) - theme.set_stylebox("focus", "Button", _focus_box()) - theme.set_color("font_color", "Button", C_INK) - theme.set_color("font_hover_color", "Button", C_INK) - theme.set_color("font_pressed_color", "Button", C_INK) - theme.set_color("font_disabled_color", "Button", C_INK_DIM) - theme.set_constant("h_separation", "Button", 6) - - # OptionButton inherits Button styling automatically via class fallback in - # Godot, but we set explicit copies in case overrides land. - theme.set_stylebox("normal", "OptionButton", _btn_box(C_BUTTON, false)) - theme.set_stylebox("hover", "OptionButton", _btn_box(C_BUTTON_HOV, false)) - theme.set_stylebox("pressed", "OptionButton", _btn_box(C_BUTTON_PRESS, true)) - theme.set_stylebox("focus", "OptionButton", _focus_box()) - theme.set_color("font_color", "OptionButton", C_INK) - - # ── CheckBox ────────────────────────────────────────────────────────────── - theme.set_color("font_color", "CheckBox", C_INK) - theme.set_color("font_hover_color", "CheckBox", C_INK) - theme.set_color("font_pressed_color", "CheckBox", C_INK) - - # ── PanelContainer ──────────────────────────────────────────────────────── - theme.set_stylebox("panel", "PanelContainer", _panel_box()) - - # ── Panel (raw) — same look as PanelContainer ───────────────────────────── - theme.set_stylebox("panel", "Panel", _panel_box()) - - # ── PopupMenu (recipe picker) ───────────────────────────────────────────── - theme.set_stylebox("panel", "PopupMenu", _panel_box()) - theme.set_stylebox("hover", "PopupMenu", _btn_box(C_BUTTON_HOV, false)) - theme.set_color("font_color", "PopupMenu", C_INK) - theme.set_color("font_hover_color", "PopupMenu", C_INK) - - # ── Label — defaults to ink-on-tan; per-label modulate still works ──────── - theme.set_color("font_color", "Label", C_INK) - - # ── SpinBox / LineEdit ──────────────────────────────────────────────────── - theme.set_stylebox("normal", "LineEdit", _btn_box(C_BUTTON, true)) - theme.set_stylebox("focus", "LineEdit", _focus_box()) - theme.set_color("font_color", "LineEdit", C_INK) - theme.set_color("caret_color", "LineEdit", C_INK) - - # ── Slider (audio sliders in SettingsMenu) ──────────────────────────────── - theme.set_stylebox("slider", "HSlider", _slider_track()) - theme.set_stylebox("grabber_area", "HSlider", _slider_fill()) - theme.set_stylebox("grabber_area_highlight", "HSlider", _slider_fill()) - - # ── ScrollContainer scrollbar ───────────────────────────────────────────── - theme.set_stylebox("scroll", "VScrollBar", _scrollbar_track()) - theme.set_stylebox("grabber", "VScrollBar", _btn_box(C_PANEL_DARK, false)) - theme.set_stylebox("grabber_pressed","VScrollBar", _btn_box(C_PANEL_DARK, true)) - - # ── HSeparator / VSeparator (used between bill rows in WorkbenchPanel) ──── - var sep := StyleBoxFlat.new() - sep.bg_color = C_PANEL_DARK - sep.content_margin_top = 1 - sep.content_margin_bottom = 1 - theme.set_stylebox("separator", "HSeparator", sep) - theme.set_stylebox("separator", "VSeparator", sep) - - return theme - - -# ── helpers ────────────────────────────────────────────────────────────────── - -static func _btn_box(fill: Color, pressed: bool) -> StyleBoxFlat: - var s := StyleBoxFlat.new() - s.bg_color = fill - s.border_color = C_PANEL_DARK - s.border_width_left = 1 - s.border_width_right = 1 - s.border_width_top = 1 - s.border_width_bottom = 1 - s.corner_radius_top_left = 4 - s.corner_radius_top_right = 4 - s.corner_radius_bottom_left = 4 - s.corner_radius_bottom_right = 4 - if pressed: - # Inset shadow on top + left (pressed-in look). - s.shadow_color = Color(0, 0, 0, 0.35) - s.shadow_size = 0 - # Visually offset content slightly down/right when pressed. - s.content_margin_top = 4 - s.content_margin_left = 7 - s.content_margin_right = 5 - s.content_margin_bottom = 2 - else: - # Subtle drop shadow for depth. - s.shadow_color = Color(0, 0, 0, 0.20) - s.shadow_size = 2 - s.shadow_offset = Vector2(0, 1) - s.content_margin_top = 3 - s.content_margin_left = 6 - s.content_margin_right = 6 - s.content_margin_bottom = 3 - return s - - -static func _panel_box() -> StyleBoxFlat: - var s := StyleBoxFlat.new() - s.bg_color = C_PANEL - s.border_color = C_PANEL_DARK - s.border_width_left = 2 - s.border_width_right = 2 - s.border_width_top = 2 - s.border_width_bottom = 2 - s.corner_radius_top_left = 6 - s.corner_radius_top_right = 6 - s.corner_radius_bottom_left = 6 - s.corner_radius_bottom_right = 6 - s.shadow_color = Color(0, 0, 0, 0.35) - s.shadow_size = 4 - s.shadow_offset = Vector2(0, 2) - s.content_margin_top = 8 - s.content_margin_left = 10 - s.content_margin_right = 10 - s.content_margin_bottom = 8 - return s - - -static func _focus_box() -> StyleBoxFlat: - var s := StyleBoxFlat.new() - s.bg_color = Color(0, 0, 0, 0) # transparent - s.border_color = C_ACCENT_GOLD - s.border_width_left = 2 - s.border_width_right = 2 - s.border_width_top = 2 - s.border_width_bottom = 2 - s.corner_radius_top_left = 4 - s.corner_radius_top_right = 4 - s.corner_radius_bottom_left = 4 - s.corner_radius_bottom_right = 4 - return s - - -static func _slider_track() -> StyleBoxFlat: - var s := StyleBoxFlat.new() - s.bg_color = C_PANEL_DARK - s.corner_radius_top_left = 4 - s.corner_radius_top_right = 4 - s.corner_radius_bottom_left = 4 - s.corner_radius_bottom_right = 4 - s.content_margin_top = 4 - s.content_margin_bottom = 4 - return s - - -static func _slider_fill() -> StyleBoxFlat: - var s := StyleBoxFlat.new() - s.bg_color = C_ACCENT_GOLD - s.corner_radius_top_left = 4 - s.corner_radius_top_right = 4 - s.corner_radius_bottom_left = 4 - s.corner_radius_bottom_right = 4 - return s - - -static func _scrollbar_track() -> StyleBoxFlat: - var s := StyleBoxFlat.new() - s.bg_color = Color(C_PANEL_DARK.r, C_PANEL_DARK.g, C_PANEL_DARK.b, 0.3) - s.corner_radius_top_left = 3 - s.corner_radius_top_right = 3 - s.corner_radius_bottom_left = 3 - s.corner_radius_bottom_right = 3 - return s diff --git a/scenes/ui/medieval_theme.gd.uid b/scenes/ui/medieval_theme.gd.uid deleted file mode 100644 index cb1f529..0000000 --- a/scenes/ui/medieval_theme.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c26o807ldrrrx diff --git a/scenes/ui/top_bar.gd b/scenes/ui/top_bar.gd index d778e8d..c78ee4a 100644 --- a/scenes/ui/top_bar.gd +++ b/scenes/ui/top_bar.gd @@ -44,10 +44,6 @@ var _log_btn: Button = null func _ready() -> void: - var button_row: HBoxContainer = get_node_or_null("Anchor/ButtonRow") - if button_row != null: - button_row.add_theme_constant_override("separation", 4) - pause_btn.text = Strings.t(&"speed.pause") normal_btn.text = Strings.t(&"speed.normal") fast_btn.text = Strings.t(&"speed.fast") @@ -156,8 +152,8 @@ func _add_build_btn() -> void: return var build_btn := Button.new() build_btn.name = "BuildBtn" - build_btn.text = "🔨" - build_btn.custom_minimum_size = Vector2(40, 40) + build_btn.text = Strings.t(&"ui.build") + build_btn.custom_minimum_size = Vector2(60, 48) build_btn.focus_mode = Control.FOCUS_NONE build_btn.pressed.connect(_on_build_pressed) button_row.add_child(build_btn) @@ -180,8 +176,8 @@ func _add_settings_btn() -> void: return var settings_btn := Button.new() settings_btn.name = "SettingsBtn" - settings_btn.text = "⚙" - settings_btn.custom_minimum_size = Vector2(40, 40) + settings_btn.text = Strings.t(&"ui.settings.btn") + settings_btn.custom_minimum_size = Vector2(80, 48) settings_btn.focus_mode = Control.FOCUS_NONE settings_btn.pressed.connect(_on_settings_pressed) button_row.add_child(settings_btn) @@ -205,16 +201,16 @@ func _add_work_log_btns() -> void: var work_btn := Button.new() work_btn.name = "WorkBtn" - work_btn.text = "👷" - work_btn.custom_minimum_size = Vector2(40, 40) + work_btn.text = "Work" + work_btn.custom_minimum_size = Vector2(60, 48) work_btn.focus_mode = Control.FOCUS_NONE work_btn.pressed.connect(_on_work_pressed) button_row.add_child(work_btn) _log_btn = Button.new() _log_btn.name = "LogBtn" - _log_btn.text = "🔔" - _log_btn.custom_minimum_size = Vector2(40, 40) + _log_btn.text = "Log" + _log_btn.custom_minimum_size = Vector2(60, 48) _log_btn.focus_mode = Control.FOCUS_NONE _log_btn.pressed.connect(_on_log_pressed) button_row.add_child(_log_btn) diff --git a/scenes/ui/top_bar.tscn b/scenes/ui/top_bar.tscn index bb20de2..ae00ee1 100644 --- a/scenes/ui/top_bar.tscn +++ b/scenes/ui/top_bar.tscn @@ -20,33 +20,29 @@ offset_bottom = 40.0 [node name="PauseBtn" type="Button" parent="Anchor/ButtonRow"] focus_mode = 0 -custom_minimum_size = Vector2(36, 40) text = "‖" [node name="NormalBtn" type="Button" parent="Anchor/ButtonRow"] focus_mode = 0 -custom_minimum_size = Vector2(36, 40) text = "1×" [node name="FastBtn" type="Button" parent="Anchor/ButtonRow"] focus_mode = 0 -custom_minimum_size = Vector2(36, 40) text = "5×" [node name="UltraBtn" type="Button" parent="Anchor/ButtonRow"] focus_mode = 0 -custom_minimum_size = Vector2(36, 40) text = "12×" [node name="SaveBtn" type="Button" parent="Anchor/ButtonRow"] focus_mode = 0 -custom_minimum_size = Vector2(40, 40) +custom_minimum_size = Vector2(48, 48) text = "💾" [node name="LoadBtn" type="Button" parent="Anchor/ButtonRow"] focus_mode = 0 -custom_minimum_size = Vector2(40, 40) -text = "📂" +custom_minimum_size = Vector2(48, 48) +text = "Load" [node name="ClockLabel" type="Label" parent="Anchor"] anchor_left = 0.5 diff --git a/scenes/ui/workbench_panel.gd b/scenes/ui/workbench_panel.gd deleted file mode 100644 index 0bfe0b5..0000000 --- a/scenes/ui/workbench_panel.gd +++ /dev/null @@ -1,434 +0,0 @@ -class_name WorkbenchPanel extends CanvasLayer -## Phase 17 — Right-side bottom-sheet workbench bill editor. -## -## Layer 18: same level as PawnDetailPanel (only one is visible at a time). -## Opens when EventBus.workbench_selected fires; closes on workbench_deselected -## or when a pawn is selected (mutual-exclusion with PawnDetailPanel). -## -## Refresh model (matches PawnDetailPanel): -## - Full UI rebuild: only on workbench_selected, add_bill, remove_bill. -## - Status-line refresh: every 5 sim ticks while open (_on_sim_tick). -## - Bill rows are NOT touched during refresh to preserve scroll + dropdown state. -## -## Touch targets: all interactive controls are at least 48×48 px. -## Background elements use MOUSE_FILTER_IGNORE so world taps pass through. - -const PANEL_WIDTH: int = 360 -const REFRESH_TICKS: int = 5 # update status line every N sim ticks -const LAYER: int = 18 - -# ── internal state ──────────────────────────────────────────────────────────── -var current_workbench: Workbench = null -var _tick_counter: int = 0 - -# ── node refs (built in _build_ui) ─────────────────────────────────────────── -var _panel: PanelContainer = null -var _wb_name_label: Label = null -var _close_btn: Button = null -var _status_label: Label = null -var _bills_vbox: VBoxContainer = null -var _add_btn: Button = null -var _no_bills_label: Label = null -var _recipe_popup: PopupMenu = null - - -func _ready() -> void: - layer = LAYER - - _build_ui() - _set_visible(false) - - EventBus.workbench_selected.connect(_on_workbench_selected) - EventBus.workbench_deselected.connect(_on_workbench_deselected) - EventBus.pawn_selected.connect(_on_pawn_selected) - EventBus.sim_tick.connect(_on_sim_tick) - - Audit.log("workbench_panel", "WorkbenchPanel ready (layer %d)" % layer) - - -func _exit_tree() -> void: - if EventBus.workbench_selected.is_connected(_on_workbench_selected): - EventBus.workbench_selected.disconnect(_on_workbench_selected) - if EventBus.workbench_deselected.is_connected(_on_workbench_deselected): - EventBus.workbench_deselected.disconnect(_on_workbench_deselected) - if EventBus.pawn_selected.is_connected(_on_pawn_selected): - EventBus.pawn_selected.disconnect(_on_pawn_selected) - if EventBus.sim_tick.is_connected(_on_sim_tick): - EventBus.sim_tick.disconnect(_on_sim_tick) - - -# ── UI construction ─────────────────────────────────────────────────────────── - -func _build_ui() -> void: - # Right-side sheet anchored to the right edge, full height. - _panel = PanelContainer.new() - _panel.name = "WorkbenchSheet" - _panel.anchor_left = 1.0 - _panel.anchor_right = 1.0 - _panel.anchor_top = 0.0 - _panel.anchor_bottom = 1.0 - _panel.offset_left = -PANEL_WIDTH - _panel.offset_right = 0.0 - _panel.offset_top = 0.0 - _panel.offset_bottom = 0.0 - _panel.mouse_filter = Control.MOUSE_FILTER_PASS - add_child(_panel) - - # Scrollable inner container so content survives small screens. - var scroll := ScrollContainer.new() - scroll.name = "Scroll" - scroll.set_anchors_preset(Control.PRESET_FULL_RECT) - scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED - _panel.add_child(scroll) - - var vbox := VBoxContainer.new() - vbox.name = "Content" - vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL - vbox.add_theme_constant_override("separation", 6) - scroll.add_child(vbox) - - # ── Header ──────────────────────────────────────────────────────────────── - var header := HBoxContainer.new() - header.name = "Header" - header.add_theme_constant_override("separation", 8) - vbox.add_child(header) - - _wb_name_label = Label.new() - _wb_name_label.name = "WorkbenchName" - _wb_name_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _wb_name_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER - _wb_name_label.mouse_filter = Control.MOUSE_FILTER_IGNORE - header.add_child(_wb_name_label) - - _close_btn = Button.new() - _close_btn.name = "CloseBtn" - _close_btn.text = Strings.t(&"ui.detail.close") - _close_btn.custom_minimum_size = Vector2(48, 48) - _close_btn.focus_mode = Control.FOCUS_NONE - _close_btn.pressed.connect(_on_close_pressed) - header.add_child(_close_btn) - - _add_separator(vbox) - - # ── Status line (live-refreshed) ────────────────────────────────────────── - _status_label = Label.new() - _status_label.name = "StatusLabel" - _status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - _status_label.mouse_filter = Control.MOUSE_FILTER_IGNORE - vbox.add_child(_status_label) - - _add_separator(vbox) - - # ── Bill list ───────────────────────────────────────────────────────────── - var bills_header := Label.new() - bills_header.text = Strings.t(&"ui.bill.no_bills_hint") - bills_header.name = "BillsHeader" - bills_header.mouse_filter = Control.MOUSE_FILTER_IGNORE - # Header is just section spacing; actual content is in _bills_vbox below. - # Repurpose this as the "no bills" hint — hidden when bills exist. - _no_bills_label = bills_header - vbox.add_child(_no_bills_label) - - _bills_vbox = VBoxContainer.new() - _bills_vbox.name = "BillList" - _bills_vbox.add_theme_constant_override("separation", 8) - _bills_vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE - vbox.add_child(_bills_vbox) - - _add_separator(vbox) - - # ── Add-bill footer ─────────────────────────────────────────────────────── - var footer := HBoxContainer.new() - footer.name = "Footer" - footer.add_theme_constant_override("separation", 8) - vbox.add_child(footer) - - _add_btn = Button.new() - _add_btn.name = "AddBillBtn" - _add_btn.text = Strings.t(&"ui.bill.add_button") - _add_btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _add_btn.custom_minimum_size = Vector2(0, 48) - _add_btn.focus_mode = Control.FOCUS_NONE - _add_btn.pressed.connect(_on_add_bill_pressed) - footer.add_child(_add_btn) - - # PopupMenu for recipe selection — populated lazily in _on_add_bill_pressed. - _recipe_popup = PopupMenu.new() - _recipe_popup.name = "RecipePopup" - _recipe_popup.id_pressed.connect(_on_recipe_chosen) - _panel.add_child(_recipe_popup) - - -func _add_separator(parent: VBoxContainer) -> void: - var sep := HSeparator.new() - sep.mouse_filter = Control.MOUSE_FILTER_IGNORE - parent.add_child(sep) - - -func _clear_children(node: Node) -> void: - for child in node.get_children(): - child.queue_free() - - -# ── event handlers ──────────────────────────────────────────────────────────── - -func _on_workbench_selected(wb: Workbench) -> void: - current_workbench = wb - _tick_counter = 0 - _wb_name_label.text = wb.label_text - _refresh_status() - _populate_bills() - _set_visible(true) - Audit.log("workbench_panel", "opened for %s" % wb.label_text) - - -func _on_workbench_deselected() -> void: - current_workbench = null - _set_visible(false) - Audit.log("workbench_panel", "closed (deselected)") - - -func _on_pawn_selected(_pawn) -> void: - # Mutual exclusion: pawn panel and workbench panel are never both open. - if current_workbench == null: - return - current_workbench = null - _set_visible(false) - EventBus.workbench_deselected.emit() - Audit.log("workbench_panel", "closed (pawn selected)") - - -func _on_close_pressed() -> void: - current_workbench = null - _set_visible(false) - EventBus.workbench_deselected.emit() - Audit.log("workbench_panel", "closed (X button)") - - -func _on_sim_tick(_tick_number: int) -> void: - if current_workbench == null or not _panel.visible: - return - if not is_instance_valid(current_workbench): - current_workbench = null - _set_visible(false) - return - _tick_counter += 1 - if _tick_counter >= REFRESH_TICKS: - _tick_counter = 0 - _refresh_status() - - -# ── status-line refresh (called every REFRESH_TICKS, NOT rebuild) ───────────── - -func _refresh_status() -> void: - if current_workbench == null: - return - var cb = current_workbench.current_bill - if cb != null and cb.recipe != null: - var work_ticks: int = cb.recipe.work_ticks - _status_label.text = "%s: %s %d/%d" % [ - Strings.t(&"ui.workbench.current_bill"), - cb.recipe.display_name(), - current_workbench.current_work_progress, - work_ticks - ] - else: - _status_label.text = Strings.t(&"ui.workbench.idle") - - -# ── bill list population (called on open / add / remove only) ───────────────── - -func _populate_bills() -> void: - if current_workbench == null: - return - - _clear_children(_bills_vbox) - - var has_bills: bool = current_workbench.bills.size() > 0 - _no_bills_label.visible = not has_bills - - for bill in current_workbench.bills: - _bills_vbox.add_child(_make_bill_row(bill)) - - # Enable/disable the add button based on whether any filtered recipes exist. - var filtered: Array[Recipe] = _filtered_recipes() - _add_btn.disabled = filtered.is_empty() - - -## Build and return a VBoxContainer widget for a single bill. -## All controls mutate bill fields directly; remove triggers _populate_bills(). -func _make_bill_row(bill: Bill) -> VBoxContainer: - var row_vbox := VBoxContainer.new() - row_vbox.add_theme_constant_override("separation", 4) - row_vbox.mouse_filter = Control.MOUSE_FILTER_IGNORE - - # Row 1: recipe name (bold via theme; we use a plain Label — theme handles weight). - var name_lbl := Label.new() - name_lbl.text = bill.recipe.display_name() if bill.recipe != null else "???" - name_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE - row_vbox.add_child(name_lbl) - - # Row 2: mode OptionButton (Forever / Do X times / Do until X). - var mode_row := HBoxContainer.new() - mode_row.add_theme_constant_override("separation", 6) - row_vbox.add_child(mode_row) - - var mode_lbl := Label.new() - mode_lbl.text = "Mode:" - mode_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE - mode_lbl.custom_minimum_size = Vector2(40, 0) - mode_row.add_child(mode_lbl) - - var mode_btn := OptionButton.new() - mode_btn.add_item(Strings.t(&"ui.bill.mode_forever"), Bill.Mode.FOREVER) - mode_btn.add_item(Strings.t(&"ui.bill.mode_count"), Bill.Mode.COUNT) - mode_btn.add_item(Strings.t(&"ui.bill.mode_until_n"), Bill.Mode.UNTIL_N) - mode_btn.selected = bill.mode as int - mode_btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL - mode_btn.focus_mode = Control.FOCUS_NONE - mode_btn.custom_minimum_size = Vector2(0, 48) - # Capture bill reference in closure; repopulate on mode change so conditional - # rows (count spinner, done label) appear/disappear correctly. - mode_btn.item_selected.connect(func(idx: int) -> void: - bill.mode = idx as Bill.Mode - Audit.log("workbench_ui", "%s: bill mode → %d" % [current_workbench.label_text, idx]) - # Defer the rebuild — we must NOT free mode_btn while its item_selected - # signal is still emitting (instant crash). call_deferred runs the - # repopulate after the signal frame completes. - call_deferred("_populate_bills") - ) - mode_row.add_child(mode_btn) - - # Row 3 (conditional): SpinBox for target_count. Shown when mode != FOREVER. - if bill.mode != Bill.Mode.FOREVER: - var count_row := HBoxContainer.new() - count_row.add_theme_constant_override("separation", 6) - row_vbox.add_child(count_row) - - var count_lbl := Label.new() - count_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE - count_lbl.custom_minimum_size = Vector2(80, 0) - if bill.mode == Bill.Mode.COUNT: - count_lbl.text = Strings.t(&"ui.bill.target") - else: - count_lbl.text = Strings.t(&"ui.bill.until_count") - count_row.add_child(count_lbl) - - var spin := SpinBox.new() - spin.min_value = 1 - spin.max_value = 999 - spin.step = 1 - spin.value = max(1, bill.target_count) - spin.size_flags_horizontal = Control.SIZE_EXPAND_FILL - spin.focus_mode = Control.FOCUS_NONE - spin.value_changed.connect(func(v: float) -> void: - bill.target_count = int(v) - Audit.log("workbench_ui", "%s: bill target_count → %d" % [current_workbench.label_text, bill.target_count]) - ) - count_row.add_child(spin) - - # Row 4 (COUNT only): "Done: X/Y" progress label. - if bill.mode == Bill.Mode.COUNT: - var done_lbl := Label.new() - done_lbl.text = "%s: %d/%d" % [Strings.t(&"ui.bill.completed"), bill.completed_count, bill.target_count] - done_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE - row_vbox.add_child(done_lbl) - - # Row 5: pause CheckBox. - var pause_check := CheckBox.new() - pause_check.text = Strings.t(&"ui.bill.pause") - pause_check.button_pressed = bill.paused - pause_check.focus_mode = Control.FOCUS_NONE - pause_check.custom_minimum_size = Vector2(0, 40) - pause_check.toggled.connect(func(on: bool) -> void: - bill.paused = on - Audit.log("workbench_ui", "%s: bill paused → %s" % [current_workbench.label_text, str(on)]) - ) - row_vbox.add_child(pause_check) - - # Row 6: "Remove" button, right-aligned via HSpacer + HBox. - var remove_row := HBoxContainer.new() - row_vbox.add_child(remove_row) - - var spacer := Control.new() - spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL - spacer.mouse_filter = Control.MOUSE_FILTER_IGNORE - remove_row.add_child(spacer) - - var remove_btn := Button.new() - remove_btn.text = Strings.t(&"ui.bill.remove") - remove_btn.custom_minimum_size = Vector2(80, 40) - remove_btn.focus_mode = Control.FOCUS_NONE - remove_btn.pressed.connect(func() -> void: - if current_workbench == null: - return - current_workbench.remove_bill(bill) - Audit.log("workbench_ui", "%s: bill removed — recipe '%s'" % [ - current_workbench.label_text, - bill.recipe.id if bill.recipe != null else "null" - ]) - # Defer — same reason as mode_btn: don't free this button mid-emit. - call_deferred("_populate_bills") - ) - remove_row.add_child(remove_btn) - - # Thin separator below each bill row for visual grouping. - var sep := HSeparator.new() - sep.mouse_filter = Control.MOUSE_FILTER_IGNORE - row_vbox.add_child(sep) - - return row_vbox - - -# ── add-bill popup ──────────────────────────────────────────────────────────── - -func _on_add_bill_pressed() -> void: - if current_workbench == null: - return - - var recipes: Array[Recipe] = _filtered_recipes() - if recipes.is_empty(): - return - - _recipe_popup.clear() - for i in recipes.size(): - _recipe_popup.add_item(recipes[i].display_name(), i) - - # Position the popup just above the add button. - var btn_rect: Rect2 = _add_btn.get_global_rect() - _recipe_popup.position = Vector2i(int(btn_rect.position.x), int(btn_rect.position.y) - _recipe_popup.size.y - 4) - _recipe_popup.popup() - - -func _on_recipe_chosen(id: int) -> void: - if current_workbench == null: - return - var recipes: Array[Recipe] = _filtered_recipes() - if id < 0 or id >= recipes.size(): - return - var picked: Recipe = recipes[id] - var b := Bill.new() - b.recipe = picked - b.mode = Bill.Mode.FOREVER - current_workbench.add_bill(b) - Audit.log("workbench_ui", "%s: bill added — recipe '%s'" % [current_workbench.label_text, picked.id]) - _populate_bills() - - -## Returns all catalog recipes whose required_skill matches the workbench's -## accepted_skill. Returns an empty array when no workbench is set. -func _filtered_recipes() -> Array[Recipe]: - if current_workbench == null: - return [] - var result: Array[Recipe] = [] - for r in RecipeCatalog.all(): - if r.required_skill == current_workbench.accepted_skill: - result.append(r) - return result - - -# ── visibility ──────────────────────────────────────────────────────────────── - -func _set_visible(v: bool) -> void: - if _panel != null: - _panel.visible = v diff --git a/scenes/ui/workbench_panel.gd.uid b/scenes/ui/workbench_panel.gd.uid deleted file mode 100644 index 5c26183..0000000 --- a/scenes/ui/workbench_panel.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bsdc4o12x1v41 diff --git a/scenes/world/designation.gd b/scenes/world/designation.gd index b22ba5d..5958268 100644 --- a/scenes/world/designation.gd +++ b/scenes/world/designation.gd @@ -40,10 +40,6 @@ const TOOL_BUILD_WORKBENCH_HEARTH: StringName = &"build_workbench_hearth" const TOOL_BUILD_WORKBENCH_CREMATION_PYRE: StringName = &"build_workbench_cremation_pyre" # Phase 17 — Stockpile tab. const TOOL_PAINT_STOCKPILE: StringName = &"paint_stockpile" -# Tree planting — ghost sapling that ConstructionProvider will fulfil. -const TOOL_PLANT_TREE: StringName = &"plant_tree" -# Quarry — must paint on a BigRockNode tile; spawns a QuarryWorkbench ghost. -const TOOL_PAINT_QUARRY: StringName = &"paint_quarry" # ── tool → material override ───────────────────────────────────────────────── # For build_wall and build_floor the tool is shared but the material differs. @@ -77,8 +73,6 @@ const _ATLAS_BY_TOOL: Dictionary = { &"build_workbench_hearth": Vector2i(1, 0), &"build_workbench_cremation_pyre":Vector2i(3, 0), &"paint_stockpile": Vector2i(0, 0), - &"plant_tree": Vector2i(0, 0), # grass ghost — tinted green - &"paint_quarry": Vector2i(2, 0), # stone-grey ghost } # Placeholder source ID — mirrors World.PLACEHOLDER_SOURCE_ID. @@ -126,8 +120,6 @@ func set_active_tool(tool: StringName) -> void: TOOL_BUILD_WORKBENCH_MILLSTONE, TOOL_BUILD_WORKBENCH_HEARTH, TOOL_BUILD_WORKBENCH_CREMATION_PYRE, TOOL_PAINT_STOCKPILE, - TOOL_PLANT_TREE, - TOOL_PAINT_QUARRY, ], "Designation.set_active_tool: unknown tool '%s'" % tool ) diff --git a/scenes/world/selection.gd b/scenes/world/selection.gd index dbd0aca..ec04c64 100644 --- a/scenes/world/selection.gd +++ b/scenes/world/selection.gd @@ -14,9 +14,6 @@ const CLICK_MAX_DURATION_MS: int = 300 var _pathfinder: Pathfinder = null var _selected_pawn: Pawn = null -## Currently selected workbench, or null. Mutually exclusive with _selected_pawn — -## selecting one clears the other (see _select / _select_workbench). -var _selected_workbench: Workbench = null var _camera = null # Camera2D (CameraRig) — set via bind_camera(); duck-typed to avoid circular preload # When Designation paint mode is active this flag is raised by Designation so @@ -65,32 +62,23 @@ func _unhandled_input(event: InputEvent) -> void: # ── Keyboard: Escape → deselect (lowest-priority; consumed last) ───────────── # Designation._input handles Escape first; panels handle it in _unhandled_input # before reaching here. If we still see it and have a selection, consume it. - if event.is_action_pressed("cancel"): - if _selected_pawn != null: - _deselect() - get_viewport().set_input_as_handled() - Audit.log("selection", "escape: deselected pawn") - return - if _selected_workbench != null: - _deselect_workbench() - get_viewport().set_input_as_handled() - Audit.log("selection", "escape: deselected workbench") - return + if event.is_action_pressed("cancel") and _selected_pawn != null: + _deselect() + get_viewport().set_input_as_handled() + Audit.log("selection", "escape: deselected") + return # ── Mouse: only handle button events below ─────────────────────────────────── if not (event is InputEventMouseButton): return - # ── Right-click: cancel designation (if active) or deselect pawn / workbench ── + # ── Right-click: cancel designation (if active) or deselect pawn ───────────── if event.button_index == MOUSE_BUTTON_RIGHT and event.pressed: # Designation cancellation is handled by Designation._input; if we see - # this right-click, no designation was active. Deselect whatever is selected. + # this right-click, no designation was active. Deselect any selected pawn. if _selected_pawn != null: _deselect() get_viewport().set_input_as_handled() - elif _selected_workbench != null: - _deselect_workbench() - get_viewport().set_input_as_handled() return if event.button_index != MOUSE_BUTTON_LEFT: @@ -126,23 +114,14 @@ func _handle_click(screen_pos: Vector2) -> void: floori(world_pos.y / float(Pawn.TILE_SIZE_PX)), ) - # Click on a pawn → select. Pawn wins over workbench when they share a tile - # (a pawn working at a bench is selectable; tap empty bench tile to inspect bills). + # Click on a pawn → select. var hit_pawn: Pawn = World.pawn_at_tile(tile) if hit_pawn != null: _select(hit_pawn) return - # Click on a workbench → open the bill-editor panel. - var hit_workbench = World.workbench_at_tile(tile) - if hit_workbench != null: - _select_workbench(hit_workbench) - return - - # Empty tile with no current pawn selection → also clear any workbench selection. + # Empty tile with no current selection → no-op. if _selected_pawn == null: - if _selected_workbench != null: - _deselect_workbench() return # Empty walkable tile with a selection → queue a forced job. Decision picks @@ -161,9 +140,6 @@ func _handle_click(screen_pos: Vector2) -> void: func _select(pawn: Pawn) -> void: if _selected_pawn == pawn: return - # Mutual exclusion with workbench selection: clear it before promoting pawn. - if _selected_workbench != null: - _deselect_workbench() if _selected_pawn != null: _selected_pawn.set_selected(false) EventBus.pawn_deselected.emit() @@ -183,28 +159,6 @@ func _deselect() -> void: _selected_pawn = null -## Select a workbench → opens the bill-editor panel via EventBus. -## Mutually exclusive with pawn selection: clears _selected_pawn first. -func _select_workbench(wb) -> void: - if _selected_workbench == wb: - return - if _selected_pawn != null: - _deselect() - if _selected_workbench != null: - EventBus.workbench_deselected.emit() - _selected_workbench = wb - EventBus.workbench_selected.emit(wb) - Audit.log("selection", "selected workbench %s at %s" % [wb.label_text, wb.tile]) - - -func _deselect_workbench() -> void: - if _selected_workbench == null: - return - Audit.log("selection", "deselected workbench %s" % _selected_workbench.label_text) - _selected_workbench = null - EventBus.workbench_deselected.emit() - - ## Cycle the selection forward (dir=1) or backward (dir=-1) through World.pawns. ## Wraps around. If no pawn currently selected, picks World.pawns[0]. ## Pans the camera to the newly selected pawn's tile. diff --git a/scenes/world/world.gd b/scenes/world/world.gd index d36e2b0..734cc1c 100644 --- a/scenes/world/world.gd +++ b/scenes/world/world.gd @@ -1,9 +1,8 @@ extends Node2D ## Phase 4 world view. 80×80 TileMap, 6 layers, 3 pawns, full AI pipeline: ## RestProvider → ChopProvider → MineProvider → HaulingProvider → idle -## plus sample trees and rocks. No pre-made stockpiles — items sit where they -## are produced until the player paints storage (a stockpile zone or builds -## a crate). This matches Rimworld parity: storage is a player decision. +## plus sample trees, rocks, and two stockpile zones with different priorities +## for the haul-cascade demo. ## ## TileMap layer indices follow docs/architecture.md: ## 0 Terrain · 1 Floor · 2 Wall · 3 Designation · 4 Roof · 5 Fog @@ -32,8 +31,6 @@ const PAWN_SCENE: PackedScene = preload("res://scenes/pawn/pawn.tscn") const TREE_SCENE: PackedScene = preload("res://scenes/entities/tree.tscn") const ROCK_SCENE: PackedScene = preload("res://scenes/entities/rock.tscn") const BIG_ROCK_SCENE: PackedScene = preload("res://scenes/entities/big_rock.tscn") -const BIG_ROCK_NODE_SCENE: PackedScene = preload("res://scenes/entities/big_rock_node.tscn") -const QUARRY_WORKBENCH_SCENE: PackedScene = preload("res://scenes/entities/quarry_workbench.tscn") const STOCKPILE_SCENE: PackedScene = preload("res://scenes/world/stockpile_zone.tscn") const WALL_SCENE: PackedScene = preload("res://scenes/entities/wall.tscn") const FLOOR_SCENE: PackedScene = preload("res://scenes/entities/floor.tscn") @@ -62,15 +59,10 @@ const SAMPLE_PAWNS: Array[Dictionary] = [ ] # Phase 4 — sample harvestables. Trees clustered east, rocks south-east. -# Mix of 8 mature + 4 saplings so players see growth in action from day 1. const SAMPLE_TREES: Array[Vector2i] = [ Vector2i(58, 30), Vector2i(60, 31), Vector2i(62, 30), Vector2i(61, 33), Vector2i(63, 34), Vector2i(59, 35), - Vector2i(57, 28), Vector2i(64, 32), # 2 more mature - Vector2i(56, 36), Vector2i(65, 29), # 2 more mature ] -# The first 4 in SAMPLE_TREES_SAPLING are planted as saplings (stage 0). -const SAMPLE_TREES_SAPLING_COUNT: int = 4 const SAMPLE_ROCKS: Array[Vector2i] = [ Vector2i(60, 60), Vector2i(62, 60), Vector2i(63, 62), Vector2i(58, 62), ] @@ -82,28 +74,9 @@ const SAMPLE_BIG_ROCKS: Array[Vector2i] = [ Vector2i(56, 64), ] -# Permanent stone outcrops (BigRockNode). Scattered far from the cabin at -# (44, 22)..(51, 28) so the player has to scout / plan transport routes. -# Each is a 2×2 footprint that never depletes; player paints `paint_quarry` -# to build a QuarryWorkbench on it. -const SAMPLE_BIG_ROCK_NODES: Array[Vector2i] = [ - Vector2i(12, 30), # west, near map edge - Vector2i(68, 12), # north-east corner area - Vector2i(70, 60), # south-east corner -] - # HaulingProvider re-flow cadence — every 5 sim seconds at 1× (100 ticks). 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 = 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 - # Phase 11 — global darkness tint. Day = white, night = deep cool blue. # Driven by Clock.darkness_factor() (0..1) each sim tick. const NIGHT_TINT: Color = Color(0.20, 0.22, 0.40, 1.0) @@ -239,10 +212,7 @@ func _ready() -> void: _spawn_sample_pawns() _spawn_sample_harvestables() - # No pre-made stockpiles: the player must paint their own storage. Items - # from chop/mine/crafting sit where they're produced until a player-made - # stockpile or crate exists for hauling. (2026-05-15 — _spawn_sample_stockpiles - # removed; was leftover Phase 4 acceptance scaffolding south-west of the cabin.) + _spawn_sample_stockpiles() _seed_phase5_demo_buildings() # Phase 13 — pre-stamp the cabin walls + floors on the TileMap data layers # so RoomDetector can see a completed enclosure at boot without waiting for @@ -436,7 +406,7 @@ func _spawn_sample_pawns() -> void: World.register_pawn(p) -# ── Phase 4: harvestables (stockpile-zone seeding removed 2026-05-15) ─────── +# ── Phase 4: harvestables + stockpile zones ───────────────────────────────── func _spawn_sample_harvestables() -> void: # Untyped vars — Godot's class-name cache for class_name'd classes is @@ -445,15 +415,11 @@ func _spawn_sample_harvestables() -> void: # Boot seed auto-designates so the production-chain demo runs end-to-end # without requiring a player to paint chop/mine first. Real player-painted # trees / rocks still gate on chop_designated / mine_designated (Rimworld parity). - for i in SAMPLE_TREES.size(): + for t_tile in SAMPLE_TREES: var tree = TREE_SCENE.instantiate() add_child(tree) - # First SAMPLE_TREES_SAPLING_COUNT trees spawn as saplings (stage 0) - # so the player can observe growth from day 1. The rest are mature. - var stage: int = HarvestableTree.STAGE_SAPLING if i < SAMPLE_TREES_SAPLING_COUNT else HarvestableTree.STAGE_MATURE - tree.setup(SAMPLE_TREES[i], stage) - if stage == HarvestableTree.STAGE_MATURE: - tree.chop_designated = true + tree.setup(t_tile) + tree.chop_designated = true for r_tile in SAMPLE_ROCKS: var rock = ROCK_SCENE.instantiate() add_child(rock) @@ -464,13 +430,8 @@ func _spawn_sample_harvestables() -> void: add_child(big) big.setup(br_origin) big.mine_designated = true - # Permanent stone outcrops (never deplete; Quarry workbench built on them). - for node_origin in SAMPLE_BIG_ROCK_NODES: - var node = BIG_ROCK_NODE_SCENE.instantiate() - add_child(node) - node.setup(node_origin) - Audit.log("world", "spawned %d trees + %d rocks + %d big rocks + %d stone outcrops" % [ - SAMPLE_TREES.size(), SAMPLE_ROCKS.size(), SAMPLE_BIG_ROCKS.size(), SAMPLE_BIG_ROCK_NODES.size() + Audit.log("world", "spawned %d trees + %d rocks + %d big rocks" % [ + SAMPLE_TREES.size(), SAMPLE_ROCKS.size(), SAMPLE_BIG_ROCKS.size() ]) @@ -482,8 +443,8 @@ func _seed_phase5_demo_buildings() -> void: # • Perimeter walls (skipping the door slot) # • Door at (47, 28) — middle of the south wall # • Wood floor across the 6×5 interior (rows 23..27) - # • One pre-built crate inside (north-east corner of the interior — the - # cabin's starting amenity; player paints additional storage later) + # • One pre-built crate inside (north-east corner of the interior) + # • Two stockpile-target crates outside (Phase 4 hauling target) # Bumped from 8×6 → 8×7 so the north interior row (23) is free for the # bed sprites to extend into (beds are 1×2, anchored at the foot, head # extends one tile up). Previously the headboards clipped into the wall. @@ -525,13 +486,17 @@ func _seed_phase5_demo_buildings() -> void: while interior_crate.is_buildable(): interior_crate.on_build_tick() - # (2026-05-15) Two external SW crates removed alongside _spawn_sample_stockpiles: - # they were Phase 4 haul-destination scaffolding, no longer needed now that - # the player paints their own storage. The interior cabin crate above stays - # as a starting amenity. + # Two external stockpile-target crates south-west (Phase 4 haul destination). + var crate_tiles: Array[Vector2i] = [Vector2i(17, 60), Vector2i(18, 60)] + for t in crate_tiles: + var c: Crate = CRATE_SCENE.instantiate() + add_child(c) + c.setup(t) + while c.is_buildable(): + c.on_build_tick() - Audit.log("world", "phase 5 demo: %d walls + 1 door + %d floors queued; 1 interior crate pre-built" % [ - wall_count, floor_count + Audit.log("world", "phase 5 demo: %d walls + 1 door + %d floors queued; %d crates pre-built" % [ + wall_count, floor_count, 1 + crate_tiles.size() ]) # Phase 6 demo — two pre-built workbenches inside the cabin with bills. @@ -666,6 +631,31 @@ func _seed_phase5_demo_buildings() -> void: Audit.log("world", "phase 11 demo: %d torches pre-built inside cabin" % torch_tiles.size()) +func _spawn_sample_stockpiles() -> void: + # Two zones for the Phase 4 acceptance demo: + # - Zone A (north): wood-only filter, NORMAL priority (just a wood drop) + # - Zone B (south): wildcard, HIGH priority (the "watch wood flow upward" target) + # When the sweep runs, wood items in Zone A get re-marked for haul and + # eventually migrate to Zone B. + var zone_a: StockpileZone = STOCKPILE_SCENE.instantiate() + add_child(zone_a) + zone_a.region = Rect2i(15, 55, 4, 4) + zone_a.label = "Wood (Normal)" + zone_a.priority = StorageDestination.Priority.NORMAL + zone_a.accepted_types = [Item.TYPE_WOOD] as Array[StringName] + zone_a.queue_redraw() + + var zone_b: StockpileZone = STOCKPILE_SCENE.instantiate() + add_child(zone_b) + zone_b.region = Rect2i(15, 62, 4, 4) + zone_b.label = "Anything (High)" + zone_b.priority = StorageDestination.Priority.HIGH + zone_b.accepted_types = [] as Array[StringName] # wildcard + zone_b.queue_redraw() + + Audit.log("world", "spawned 2 stockpiles: %s + %s" % [zone_a.label, zone_b.label]) + + # ── Phase 5: designation → build-site spawn bridge ────────────────────────── # Track build sites keyed by tile so we can find + queue_free them on cancel. @@ -782,38 +772,6 @@ func _on_designation_added(cell: Vector2i, tool: StringName) -> void: sz.accepted_types = [] as Array[StringName] # wildcard sz.queue_redraw() entity = sz - # Quarry — must be placed on a BigRockNode tile. Spawns a - # QuarryWorkbench ghost (auto-FOREVER bill on completion). - &"paint_quarry": - var node = World.big_rock_node_at_tile(cell) - if node == null: - Audit.log("world", "paint_quarry: %s not on a stone outcrop — rejected" % cell) - return - # Refuse if this node already has a quarry built/queued. - for ws in World.workbenches: - if "label_text" in ws and ws.label_text == "Quarry" and node.is_at(ws.tile): - Audit.log("world", "paint_quarry: outcrop at %s already has a quarry" % cell) - return - var quarry = QUARRY_WORKBENCH_SCENE.instantiate() - add_child(quarry) - quarry.setup(cell) - entity = quarry - # Tree planting — spawn a ghost sapling with pending_plant=true so - # ConstructionProvider can queue a build job (1 wood, 30 ticks of work). - # The ghost renders as a translucent sprout until the pawn completes it. - &"plant_tree": - # Reject if tile is already occupied by a tree. - for existing_t in World.trees: - if is_instance_valid(existing_t) and existing_t.tile == cell: - Audit.log("world", "plant_tree: tile %s already has a tree — skipped" % cell) - return - var pt = TREE_SCENE.instantiate() - add_child(pt) - pt.setup(cell, HarvestableTree.STAGE_SAPLING) - pt.pending_plant = true - # Register as a build site so ConstructionProvider can assign a pawn. - World.register_build_site(pt) - entity = pt _: Audit.log("world", "unknown designation tool: %s" % tool) return @@ -906,88 +864,11 @@ func _on_designation_cleared(cell: Vector2i) -> void: func _on_sim_tick_world_sweep(tick_n: int) -> void: _update_dark_overlay() - - # Tree growth — tick every registered tree; mature + pending-plant trees are - # no-ops inside on_sim_tick(), so iterating all is safe. - for tree in World.trees: - if is_instance_valid(tree): - tree.on_sim_tick() - - # WildGrowth — attempt to plant a new sapling once per WILD_GROWTH_INTERVAL. - if tick_n % WILD_GROWTH_INTERVAL == 0: - _try_wild_growth() - if tick_n % HAUL_SWEEP_INTERVAL_TICKS != 0: return hauling_provider.sweep_for_better_destinations() -## Attempt to spawn one wild sapling on a random eligible grass tile. -## Eligibility: walkable + grass terrain + no entity overlap + < 2 tree neighbours. -## Gives up after WILD_GROWTH_MAX_ATTEMPTS rejected tries to avoid lag spikes. -func _try_wild_growth() -> void: - if World.trees.size() >= MAP_TREE_LIMIT: - return - if randf() >= WILD_GROWTH_SPAWN_PROBABILITY: - return - var rng := RandomNumberGenerator.new() - rng.seed = Sim.tick + 9973 # stable within a tick, different each call cycle - for _attempt in WILD_GROWTH_MAX_ATTEMPTS: - var candidate := Vector2i( - rng.randi_range(0, MAP_SIZE_TILES.x - 1), - rng.randi_range(0, MAP_SIZE_TILES.y - 1) - ) - if not _wild_growth_tile_eligible(candidate): - continue - var tree = TREE_SCENE.instantiate() - add_child(tree) - tree.setup(candidate, HarvestableTree.STAGE_SAPLING) - Audit.log("world", "wild growth: sapling at %s (total %d)" % [candidate, World.trees.size()]) - return - - -## Returns true if `tile` is a valid WildGrowth spawn location. -## Checks: walkable, grass terrain (source 0, atlas (0,0)), no entity at tile, -## and fewer than 2 trees among the 4 cardinal neighbours. -func _wild_growth_tile_eligible(tile: Vector2i) -> bool: - # Bounds check (pathfinder bounds == map bounds). - if pathfinder == null: - return false - if not pathfinder.is_walkable(tile): - return false - # Grass terrain only — atlas (0,0) on source 0 = TILE_GRASS. - if terrain_layer == null: - return false - var src_id: int = terrain_layer.get_cell_source_id(tile) - var atlas: Vector2i = terrain_layer.get_cell_atlas_coords(tile) - if src_id != PLACEHOLDER_SOURCE_ID or atlas != TILE_GRASS: - return false - # No existing tree, rock, or item at this tile. - for t in World.trees: - if is_instance_valid(t) and t.tile == tile: - return false - for r in World.rocks: - if is_instance_valid(r): - if r.has_method("footprint_tiles"): - if tile in r.footprint_tiles(): - return false - elif r.tile == tile: - return false - for it in World.items: - if is_instance_valid(it) and it.tile == tile: - return false - # No clumping: reject if 2+ cardinal neighbours already have a tree. - var neighbour_trees: int = 0 - var offsets: Array[Vector2i] = [Vector2i(1, 0), Vector2i(-1, 0), Vector2i(0, 1), Vector2i(0, -1)] - for offset in offsets: - var nb: Vector2i = tile + offset - for t in World.trees: - if is_instance_valid(t) and t.tile == nb: - neighbour_trees += 1 - break - return neighbour_trees < 2 - - # Phase 11 — interpolate CanvasModulate between DAY_TINT and NIGHT_TINT based # on Clock.darkness_factor() (0 = full day, 1 = full night). # Called every sim tick; Color.lerp is a handful of float ops — negligible cost.