diff --git a/assets/audio/OGG/SFX/Chopping and Mining/chop 1.ogg.import b/assets/audio/OGG/SFX/Chopping and Mining/chop 1.ogg.import deleted file mode 100644 index c64d022..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/chop 1.ogg.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="oggvorbisstr" -type="AudioStreamOggVorbis" -uid="uid://dk4uuqs15ke73" -path="res://.godot/imported/chop 1.ogg-b69af87df03a0c5fe070f85eb271d215.oggvorbisstr" - -[deps] - -source_file="res://assets/audio/OGG/SFX/Chopping and Mining/chop 1.ogg" -dest_files=["res://.godot/imported/chop 1.ogg-b69af87df03a0c5fe070f85eb271d215.oggvorbisstr"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/chop 2.ogg.import b/assets/audio/OGG/SFX/Chopping and Mining/chop 2.ogg.import deleted file mode 100644 index bed697e..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/chop 2.ogg.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="oggvorbisstr" -type="AudioStreamOggVorbis" -uid="uid://cu0or87uh4tnx" -path="res://.godot/imported/chop 2.ogg-3c1aa9e3d1bef67928f70428ba3baf67.oggvorbisstr" - -[deps] - -source_file="res://assets/audio/OGG/SFX/Chopping and Mining/chop 2.ogg" -dest_files=["res://.godot/imported/chop 2.ogg-3c1aa9e3d1bef67928f70428ba3baf67.oggvorbisstr"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/chop 3.ogg.import b/assets/audio/OGG/SFX/Chopping and Mining/chop 3.ogg.import deleted file mode 100644 index 88cc4fa..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/chop 3.ogg.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="oggvorbisstr" -type="AudioStreamOggVorbis" -uid="uid://dyeihn6rpcexy" -path="res://.godot/imported/chop 3.ogg-cbdf52dc00aee6e6fc66cc6cdf1d505c.oggvorbisstr" - -[deps] - -source_file="res://assets/audio/OGG/SFX/Chopping and Mining/chop 3.ogg" -dest_files=["res://.godot/imported/chop 3.ogg-cbdf52dc00aee6e6fc66cc6cdf1d505c.oggvorbisstr"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/chop 4.ogg.import b/assets/audio/OGG/SFX/Chopping and Mining/chop 4.ogg.import deleted file mode 100644 index 7b485b1..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/chop 4.ogg.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="oggvorbisstr" -type="AudioStreamOggVorbis" -uid="uid://dtv1te54cfra1" -path="res://.godot/imported/chop 4.ogg-3b75c4df4d131e29f8d9237341d5fdc8.oggvorbisstr" - -[deps] - -source_file="res://assets/audio/OGG/SFX/Chopping and Mining/chop 4.ogg" -dest_files=["res://.godot/imported/chop 4.ogg-3b75c4df4d131e29f8d9237341d5fdc8.oggvorbisstr"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/mine 1.ogg b/assets/audio/OGG/SFX/Chopping and Mining/mine 1.ogg deleted file mode 100644 index af0347c..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/mine 1.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7a482bc0dd0336b678e7bb2ee0485df6dc2cdd649ea6136f015984ee686b69b9 -size 33072 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/mine 1.ogg.import b/assets/audio/OGG/SFX/Chopping and Mining/mine 1.ogg.import deleted file mode 100644 index 5c7afd5..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/mine 1.ogg.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="oggvorbisstr" -type="AudioStreamOggVorbis" -uid="uid://ciigu6xv4een2" -path="res://.godot/imported/mine 1.ogg-7d18662074fe361b38d431c8b05adfef.oggvorbisstr" - -[deps] - -source_file="res://assets/audio/OGG/SFX/Chopping and Mining/mine 1.ogg" -dest_files=["res://.godot/imported/mine 1.ogg-7d18662074fe361b38d431c8b05adfef.oggvorbisstr"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/mine 2.ogg b/assets/audio/OGG/SFX/Chopping and Mining/mine 2.ogg deleted file mode 100644 index 8c7d489..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/mine 2.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:efb291c33b90172234cb2c96b9cb0733d712f3663998a394ce653017ece6a0b2 -size 30937 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/mine 2.ogg.import b/assets/audio/OGG/SFX/Chopping and Mining/mine 2.ogg.import deleted file mode 100644 index 2b591f0..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/mine 2.ogg.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="oggvorbisstr" -type="AudioStreamOggVorbis" -uid="uid://dvymfro1mgu26" -path="res://.godot/imported/mine 2.ogg-ddfea9d88f0eaa481725560e89c016d4.oggvorbisstr" - -[deps] - -source_file="res://assets/audio/OGG/SFX/Chopping and Mining/mine 2.ogg" -dest_files=["res://.godot/imported/mine 2.ogg-ddfea9d88f0eaa481725560e89c016d4.oggvorbisstr"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/mine 3.ogg b/assets/audio/OGG/SFX/Chopping and Mining/mine 3.ogg deleted file mode 100644 index c73fb16..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/mine 3.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4d0181feb17087d62da7cb2eba216cc1362567c48e086ef7e8757561b20350fd -size 26080 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/mine 3.ogg.import b/assets/audio/OGG/SFX/Chopping and Mining/mine 3.ogg.import deleted file mode 100644 index 8cbcf32..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/mine 3.ogg.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="oggvorbisstr" -type="AudioStreamOggVorbis" -uid="uid://df271xfcx0a7b" -path="res://.godot/imported/mine 3.ogg-217d1add25379185f600351d973c4748.oggvorbisstr" - -[deps] - -source_file="res://assets/audio/OGG/SFX/Chopping and Mining/mine 3.ogg" -dest_files=["res://.godot/imported/mine 3.ogg-217d1add25379185f600351d973c4748.oggvorbisstr"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/mine 4.ogg b/assets/audio/OGG/SFX/Chopping and Mining/mine 4.ogg deleted file mode 100644 index 44a511e..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/mine 4.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e9f7d0b76e8fc9d0ff70e819208763698fca099a657f147ad33cbc48abfb15cb -size 32489 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/mine 4.ogg.import b/assets/audio/OGG/SFX/Chopping and Mining/mine 4.ogg.import deleted file mode 100644 index 27d769d..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/mine 4.ogg.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="oggvorbisstr" -type="AudioStreamOggVorbis" -uid="uid://bqdbaorkkkg8o" -path="res://.godot/imported/mine 4.ogg-9f72de7d0c734b4128ba92bef9b7b50f.oggvorbisstr" - -[deps] - -source_file="res://assets/audio/OGG/SFX/Chopping and Mining/mine 4.ogg" -dest_files=["res://.godot/imported/mine 4.ogg-9f72de7d0c734b4128ba92bef9b7b50f.oggvorbisstr"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/mine 5.ogg b/assets/audio/OGG/SFX/Chopping and Mining/mine 5.ogg deleted file mode 100644 index f652787..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/mine 5.ogg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1a0f02a72aafb81b01eb971391bb3d348d0a8676334b8926f477f2b8873425a2 -size 31572 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/mine 5.ogg.import b/assets/audio/OGG/SFX/Chopping and Mining/mine 5.ogg.import deleted file mode 100644 index 6e155fc..0000000 --- a/assets/audio/OGG/SFX/Chopping and Mining/mine 5.ogg.import +++ /dev/null @@ -1,19 +0,0 @@ -[remap] - -importer="oggvorbisstr" -type="AudioStreamOggVorbis" -uid="uid://dpbdn7vgrkbvg" -path="res://.godot/imported/mine 5.ogg-fefc870733e0b64c3ccf27b6e4d44e3f.oggvorbisstr" - -[deps] - -source_file="res://assets/audio/OGG/SFX/Chopping and Mining/mine 5.ogg" -dest_files=["res://.godot/imported/mine 5.ogg-fefc870733e0b64c3ccf27b6e4d44e3f.oggvorbisstr"] - -[params] - -loop=false -loop_offset=0 -bpm=0 -beat_count=0 -bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/.DS_Store b/assets/audio/OGG/SFX/chopping/.DS_Store similarity index 100% rename from assets/audio/OGG/SFX/Chopping and Mining/.DS_Store rename to assets/audio/OGG/SFX/chopping/.DS_Store diff --git a/assets/audio/OGG/SFX/Chopping and Mining/chop 1.ogg b/assets/audio/OGG/SFX/chopping/chop1.ogg similarity index 100% rename from assets/audio/OGG/SFX/Chopping and Mining/chop 1.ogg rename to assets/audio/OGG/SFX/chopping/chop1.ogg diff --git a/assets/audio/OGG/SFX/chopping/chop1.ogg.import b/assets/audio/OGG/SFX/chopping/chop1.ogg.import new file mode 100644 index 0000000..841d951 --- /dev/null +++ b/assets/audio/OGG/SFX/chopping/chop1.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://dk4uuqs15ke73" +path="res://.godot/imported/chop1.ogg-f4598c93b23f97c2d4f238891e3ff73f.oggvorbisstr" + +[deps] + +source_file="res://assets/audio/OGG/SFX/chopping/chop1.ogg" +dest_files=["res://.godot/imported/chop1.ogg-f4598c93b23f97c2d4f238891e3ff73f.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/chop 2.ogg b/assets/audio/OGG/SFX/chopping/chop2.ogg similarity index 100% rename from assets/audio/OGG/SFX/Chopping and Mining/chop 2.ogg rename to assets/audio/OGG/SFX/chopping/chop2.ogg diff --git a/assets/audio/OGG/SFX/chopping/chop2.ogg.import b/assets/audio/OGG/SFX/chopping/chop2.ogg.import new file mode 100644 index 0000000..ebd1268 --- /dev/null +++ b/assets/audio/OGG/SFX/chopping/chop2.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://cu0or87uh4tnx" +path="res://.godot/imported/chop2.ogg-c9974775002e292c3a0954ff6ec0cf49.oggvorbisstr" + +[deps] + +source_file="res://assets/audio/OGG/SFX/chopping/chop2.ogg" +dest_files=["res://.godot/imported/chop2.ogg-c9974775002e292c3a0954ff6ec0cf49.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/chop 3.ogg b/assets/audio/OGG/SFX/chopping/chop3.ogg similarity index 100% rename from assets/audio/OGG/SFX/Chopping and Mining/chop 3.ogg rename to assets/audio/OGG/SFX/chopping/chop3.ogg diff --git a/assets/audio/OGG/SFX/chopping/chop3.ogg.import b/assets/audio/OGG/SFX/chopping/chop3.ogg.import new file mode 100644 index 0000000..33e0c50 --- /dev/null +++ b/assets/audio/OGG/SFX/chopping/chop3.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://dyeihn6rpcexy" +path="res://.godot/imported/chop3.ogg-d2a5ffcab1d1558f23fdf93ca7a3b6da.oggvorbisstr" + +[deps] + +source_file="res://assets/audio/OGG/SFX/chopping/chop3.ogg" +dest_files=["res://.godot/imported/chop3.ogg-d2a5ffcab1d1558f23fdf93ca7a3b6da.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/assets/audio/OGG/SFX/Chopping and Mining/chop 4.ogg b/assets/audio/OGG/SFX/chopping/chop4.ogg similarity index 100% rename from assets/audio/OGG/SFX/Chopping and Mining/chop 4.ogg rename to assets/audio/OGG/SFX/chopping/chop4.ogg diff --git a/assets/audio/OGG/SFX/chopping/chop4.ogg.import b/assets/audio/OGG/SFX/chopping/chop4.ogg.import new file mode 100644 index 0000000..78a6867 --- /dev/null +++ b/assets/audio/OGG/SFX/chopping/chop4.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://dtv1te54cfra1" +path="res://.godot/imported/chop4.ogg-930a76e627570bb0a5bcd1e12c512a03.oggvorbisstr" + +[deps] + +source_file="res://assets/audio/OGG/SFX/chopping/chop4.ogg" +dest_files=["res://.godot/imported/chop4.ogg-930a76e627570bb0a5bcd1e12c512a03.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/assets/audio/coin.mp3 b/assets/audio/coin.mp3 new file mode 100644 index 0000000..58c6841 --- /dev/null +++ b/assets/audio/coin.mp3 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:05439ef240ed7bedd12e462854e309ec02c8de04a3787b1ac5f312b63b631194 +size 12260 diff --git a/assets/audio/coin.mp3.import b/assets/audio/coin.mp3.import new file mode 100644 index 0000000..cda7a7a --- /dev/null +++ b/assets/audio/coin.mp3.import @@ -0,0 +1,19 @@ +[remap] + +importer="mp3" +type="AudioStreamMP3" +uid="uid://c08bib4emn512" +path="res://.godot/imported/coin.mp3-5bd517a0009591b634e39f6a9642e1fc.mp3str" + +[deps] + +source_file="res://assets/audio/coin.mp3" +dest_files=["res://.godot/imported/coin.mp3-5bd517a0009591b634e39f6a9642e1fc.mp3str"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/assets/characters/animals/Clucking Chicken/CluckingChicken.aseprite b/assets/characters/animals/Clucking Chicken/CluckingChicken.aseprite new file mode 100644 index 0000000..b205bf4 Binary files /dev/null and b/assets/characters/animals/Clucking Chicken/CluckingChicken.aseprite differ diff --git a/assets/characters/animals/Clucking Chicken/CluckingChicken.gif b/assets/characters/animals/Clucking Chicken/CluckingChicken.gif new file mode 100644 index 0000000..8522b1b Binary files /dev/null and b/assets/characters/animals/Clucking Chicken/CluckingChicken.gif differ diff --git a/assets/characters/animals/Clucking Chicken/CluckingChicken.png b/assets/characters/animals/Clucking Chicken/CluckingChicken.png new file mode 100644 index 0000000..527ed7e Binary files /dev/null and b/assets/characters/animals/Clucking Chicken/CluckingChicken.png differ diff --git a/icon.svg.import b/assets/characters/animals/Clucking Chicken/CluckingChicken.png.import similarity index 69% rename from icon.svg.import rename to assets/characters/animals/Clucking Chicken/CluckingChicken.png.import index e725647..fd98dc1 100644 --- a/icon.svg.import +++ b/assets/characters/animals/Clucking Chicken/CluckingChicken.png.import @@ -2,16 +2,16 @@ importer="texture" type="CompressedTexture2D" -uid="uid://dytbdi8k6f5oj" -path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +uid="uid://brfch42cll8qs" +path="res://.godot/imported/CluckingChicken.png-9269d537f9696347fe906016c6c5b621.ctex" metadata={ "vram_texture": false } [deps] -source_file="res://icon.svg" -dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] +source_file="res://assets/characters/animals/Clucking Chicken/CluckingChicken.png" +dest_files=["res://.godot/imported/CluckingChicken.png-9269d537f9696347fe906016c6c5b621.ctex"] [params] @@ -38,6 +38,3 @@ process/hdr_as_srgb=false process/hdr_clamp_exposure=false process/size_limit=0 detect_3d/compress_to=1 -svg/scale=1.0 -editor/scale_with_editor_scale=false -editor/convert_colors_with_editor_theme=false diff --git a/assets/characters/animals/Coral Crab/CoralCrab.aseprite b/assets/characters/animals/Coral Crab/CoralCrab.aseprite new file mode 100644 index 0000000..8ef42c4 Binary files /dev/null and b/assets/characters/animals/Coral Crab/CoralCrab.aseprite differ diff --git a/assets/characters/animals/Coral Crab/CoralCrab.gif b/assets/characters/animals/Coral Crab/CoralCrab.gif new file mode 100644 index 0000000..cc2f82d Binary files /dev/null and b/assets/characters/animals/Coral Crab/CoralCrab.gif differ diff --git a/assets/characters/animals/Coral Crab/CoralCrab.png b/assets/characters/animals/Coral Crab/CoralCrab.png new file mode 100644 index 0000000..e89b532 Binary files /dev/null and b/assets/characters/animals/Coral Crab/CoralCrab.png differ diff --git a/assets/characters/animals/Coral Crab/CoralCrab.png.import b/assets/characters/animals/Coral Crab/CoralCrab.png.import new file mode 100644 index 0000000..b32da11 --- /dev/null +++ b/assets/characters/animals/Coral Crab/CoralCrab.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ro8a7khvhpbi" +path="res://.godot/imported/CoralCrab.png-d94278909b6d7f3fd928af0b32655ef0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Coral Crab/CoralCrab.png" +dest_files=["res://.godot/imported/CoralCrab.png-d94278909b6d7f3fd928af0b32655ef0.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/assets/characters/animals/Croaking Toad/CroakingToad.aseprite b/assets/characters/animals/Croaking Toad/CroakingToad.aseprite new file mode 100644 index 0000000..ace11e3 Binary files /dev/null and b/assets/characters/animals/Croaking Toad/CroakingToad.aseprite differ diff --git a/assets/characters/animals/Croaking Toad/CroakingToad.gif b/assets/characters/animals/Croaking Toad/CroakingToad.gif new file mode 100644 index 0000000..29bb779 Binary files /dev/null and b/assets/characters/animals/Croaking Toad/CroakingToad.gif differ diff --git a/assets/characters/animals/Croaking Toad/CroakingToad.png b/assets/characters/animals/Croaking Toad/CroakingToad.png new file mode 100644 index 0000000..4a6c3fc Binary files /dev/null and b/assets/characters/animals/Croaking Toad/CroakingToad.png differ diff --git a/assets/characters/animals/Croaking Toad/CroakingToad.png.import b/assets/characters/animals/Croaking Toad/CroakingToad.png.import new file mode 100644 index 0000000..e29d3a0 --- /dev/null +++ b/assets/characters/animals/Croaking Toad/CroakingToad.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://rvdwc66bww3" +path="res://.godot/imported/CroakingToad.png-fb812ca808291c048fac7fdaa36878d0.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Croaking Toad/CroakingToad.png" +dest_files=["res://.godot/imported/CroakingToad.png-fb812ca808291c048fac7fdaa36878d0.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/assets/characters/animals/Dainty Pig/DaintyPig.aseprite b/assets/characters/animals/Dainty Pig/DaintyPig.aseprite new file mode 100644 index 0000000..d8a6064 Binary files /dev/null and b/assets/characters/animals/Dainty Pig/DaintyPig.aseprite differ diff --git a/assets/characters/animals/Dainty Pig/DaintyPig.gif b/assets/characters/animals/Dainty Pig/DaintyPig.gif new file mode 100644 index 0000000..ea8268f Binary files /dev/null and b/assets/characters/animals/Dainty Pig/DaintyPig.gif differ diff --git a/assets/characters/animals/Dainty Pig/DaintyPig.png b/assets/characters/animals/Dainty Pig/DaintyPig.png new file mode 100644 index 0000000..9968817 Binary files /dev/null and b/assets/characters/animals/Dainty Pig/DaintyPig.png differ diff --git a/assets/characters/animals/Dainty Pig/DaintyPig.png.import b/assets/characters/animals/Dainty Pig/DaintyPig.png.import new file mode 100644 index 0000000..85c3490 --- /dev/null +++ b/assets/characters/animals/Dainty Pig/DaintyPig.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://d2dpt56hf53vv" +path="res://.godot/imported/DaintyPig.png-1fa1d8560b1693c9ba5edaa5ddd52345.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Dainty Pig/DaintyPig.png" +dest_files=["res://.godot/imported/DaintyPig.png-1fa1d8560b1693c9ba5edaa5ddd52345.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/assets/characters/animals/Honking Goose/HonkingGoose.aseprite b/assets/characters/animals/Honking Goose/HonkingGoose.aseprite new file mode 100644 index 0000000..6b051b5 Binary files /dev/null and b/assets/characters/animals/Honking Goose/HonkingGoose.aseprite differ diff --git a/assets/characters/animals/Honking Goose/HonkingGoose.gif b/assets/characters/animals/Honking Goose/HonkingGoose.gif new file mode 100644 index 0000000..3badda7 Binary files /dev/null and b/assets/characters/animals/Honking Goose/HonkingGoose.gif differ diff --git a/assets/characters/animals/Honking Goose/HonkingGoose.png b/assets/characters/animals/Honking Goose/HonkingGoose.png new file mode 100644 index 0000000..eeb674e Binary files /dev/null and b/assets/characters/animals/Honking Goose/HonkingGoose.png differ diff --git a/assets/characters/animals/Honking Goose/HonkingGoose.png.import b/assets/characters/animals/Honking Goose/HonkingGoose.png.import new file mode 100644 index 0000000..086867e --- /dev/null +++ b/assets/characters/animals/Honking Goose/HonkingGoose.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://drghlunsulio" +path="res://.godot/imported/HonkingGoose.png-a80027fda17c6e2accb6a5c9a61c188f.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Honking Goose/HonkingGoose.png" +dest_files=["res://.godot/imported/HonkingGoose.png-a80027fda17c6e2accb6a5c9a61c188f.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/assets/characters/animals/Leaping Frog/LeapingFrog.aseprite b/assets/characters/animals/Leaping Frog/LeapingFrog.aseprite new file mode 100644 index 0000000..7a9588a Binary files /dev/null and b/assets/characters/animals/Leaping Frog/LeapingFrog.aseprite differ diff --git a/assets/characters/animals/Leaping Frog/LeapingFrog.gif b/assets/characters/animals/Leaping Frog/LeapingFrog.gif new file mode 100644 index 0000000..614c670 Binary files /dev/null and b/assets/characters/animals/Leaping Frog/LeapingFrog.gif differ diff --git a/assets/characters/animals/Leaping Frog/LeapingFrog.png b/assets/characters/animals/Leaping Frog/LeapingFrog.png new file mode 100644 index 0000000..d035d2e Binary files /dev/null and b/assets/characters/animals/Leaping Frog/LeapingFrog.png differ diff --git a/assets/characters/animals/Leaping Frog/LeapingFrog.png.import b/assets/characters/animals/Leaping Frog/LeapingFrog.png.import new file mode 100644 index 0000000..55da402 --- /dev/null +++ b/assets/characters/animals/Leaping Frog/LeapingFrog.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://mprebvbv6gms" +path="res://.godot/imported/LeapingFrog.png-057f3b71b6b5e09bb7754ccf5be7f652.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Leaping Frog/LeapingFrog.png" +dest_files=["res://.godot/imported/LeapingFrog.png-057f3b71b6b5e09bb7754ccf5be7f652.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/assets/characters/animals/Mad Boar/MadBoar.aseprite b/assets/characters/animals/Mad Boar/MadBoar.aseprite new file mode 100644 index 0000000..d8d5509 Binary files /dev/null and b/assets/characters/animals/Mad Boar/MadBoar.aseprite differ diff --git a/assets/characters/animals/Mad Boar/MadBoar.gif b/assets/characters/animals/Mad Boar/MadBoar.gif new file mode 100644 index 0000000..3fbf248 Binary files /dev/null and b/assets/characters/animals/Mad Boar/MadBoar.gif differ diff --git a/assets/characters/animals/Mad Boar/MadBoar.png b/assets/characters/animals/Mad Boar/MadBoar.png new file mode 100644 index 0000000..ba2208b Binary files /dev/null and b/assets/characters/animals/Mad Boar/MadBoar.png differ diff --git a/assets/characters/animals/Mad Boar/MadBoar.png.import b/assets/characters/animals/Mad Boar/MadBoar.png.import new file mode 100644 index 0000000..032e018 --- /dev/null +++ b/assets/characters/animals/Mad Boar/MadBoar.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://lvaakhex8snn" +path="res://.godot/imported/MadBoar.png-b446fcf6ed52b7d39df2c522d9fe58ec.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Mad Boar/MadBoar.png" +dest_files=["res://.godot/imported/MadBoar.png-b446fcf6ed52b7d39df2c522d9fe58ec.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/assets/characters/animals/Meowing Cat/MeowingCat.aseprite b/assets/characters/animals/Meowing Cat/MeowingCat.aseprite new file mode 100644 index 0000000..75771a4 Binary files /dev/null and b/assets/characters/animals/Meowing Cat/MeowingCat.aseprite differ diff --git a/assets/characters/animals/Meowing Cat/MeowingCat.gif b/assets/characters/animals/Meowing Cat/MeowingCat.gif new file mode 100644 index 0000000..21ff081 Binary files /dev/null and b/assets/characters/animals/Meowing Cat/MeowingCat.gif differ diff --git a/assets/characters/animals/Meowing Cat/MeowingCat.png b/assets/characters/animals/Meowing Cat/MeowingCat.png new file mode 100644 index 0000000..a32ff03 Binary files /dev/null and b/assets/characters/animals/Meowing Cat/MeowingCat.png differ diff --git a/assets/characters/animals/Meowing Cat/MeowingCat.png.import b/assets/characters/animals/Meowing Cat/MeowingCat.png.import new file mode 100644 index 0000000..078577c --- /dev/null +++ b/assets/characters/animals/Meowing Cat/MeowingCat.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dccs841gqvsnr" +path="res://.godot/imported/MeowingCat.png-46c7da26115980fa28ba085ab6f5d447.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Meowing Cat/MeowingCat.png" +dest_files=["res://.godot/imported/MeowingCat.png-46c7da26115980fa28ba085ab6f5d447.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/assets/characters/animals/Pasturing Sheep/PasturingSheep.aseprite b/assets/characters/animals/Pasturing Sheep/PasturingSheep.aseprite new file mode 100644 index 0000000..b4031e6 Binary files /dev/null and b/assets/characters/animals/Pasturing Sheep/PasturingSheep.aseprite differ diff --git a/assets/characters/animals/Pasturing Sheep/PasturingSheep.gif b/assets/characters/animals/Pasturing Sheep/PasturingSheep.gif new file mode 100644 index 0000000..68cb642 Binary files /dev/null and b/assets/characters/animals/Pasturing Sheep/PasturingSheep.gif differ diff --git a/assets/characters/animals/Pasturing Sheep/PasturingSheep.png b/assets/characters/animals/Pasturing Sheep/PasturingSheep.png new file mode 100644 index 0000000..6e3d9c1 Binary files /dev/null and b/assets/characters/animals/Pasturing Sheep/PasturingSheep.png differ diff --git a/assets/characters/animals/Pasturing Sheep/PasturingSheep.png.import b/assets/characters/animals/Pasturing Sheep/PasturingSheep.png.import new file mode 100644 index 0000000..7856e23 --- /dev/null +++ b/assets/characters/animals/Pasturing Sheep/PasturingSheep.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cvg3idufs050w" +path="res://.godot/imported/PasturingSheep.png-aed02be6fa372b65f45c92ee8c6681b5.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Pasturing Sheep/PasturingSheep.png" +dest_files=["res://.godot/imported/PasturingSheep.png-aed02be6fa372b65f45c92ee8c6681b5.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/assets/characters/animals/SleepDog.png b/assets/characters/animals/SleepDog.png new file mode 100644 index 0000000..2f269d3 Binary files /dev/null and b/assets/characters/animals/SleepDog.png differ diff --git a/assets/characters/animals/SleepDog.png.import b/assets/characters/animals/SleepDog.png.import new file mode 100644 index 0000000..355b1ad --- /dev/null +++ b/assets/characters/animals/SleepDog.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b7hgiys1h18ov" +path="res://.godot/imported/SleepDog.png-5a732d01d494bef231adf10678a32db9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/SleepDog.png" +dest_files=["res://.godot/imported/SleepDog.png-5a732d01d494bef231adf10678a32db9.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/assets/characters/animals/Slow Turtle/SlowTurtle.aseprite b/assets/characters/animals/Slow Turtle/SlowTurtle.aseprite new file mode 100644 index 0000000..ff7c480 Binary files /dev/null and b/assets/characters/animals/Slow Turtle/SlowTurtle.aseprite differ diff --git a/assets/characters/animals/Slow Turtle/SlowTurtle.gif b/assets/characters/animals/Slow Turtle/SlowTurtle.gif new file mode 100644 index 0000000..8587ac2 Binary files /dev/null and b/assets/characters/animals/Slow Turtle/SlowTurtle.gif differ diff --git a/assets/characters/animals/Slow Turtle/SlowTurtle.png b/assets/characters/animals/Slow Turtle/SlowTurtle.png new file mode 100644 index 0000000..1f4643a Binary files /dev/null and b/assets/characters/animals/Slow Turtle/SlowTurtle.png differ diff --git a/assets/characters/animals/Slow Turtle/SlowTurtle.png.import b/assets/characters/animals/Slow Turtle/SlowTurtle.png.import new file mode 100644 index 0000000..2119a39 --- /dev/null +++ b/assets/characters/animals/Slow Turtle/SlowTurtle.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ddpod17b0clk4" +path="res://.godot/imported/SlowTurtle.png-23aae53fe1e7eaed37e13b3e52381e08.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Slow Turtle/SlowTurtle.png" +dest_files=["res://.godot/imported/SlowTurtle.png-23aae53fe1e7eaed37e13b3e52381e08.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/assets/characters/animals/Snow Fox/SnowFox.aseprite b/assets/characters/animals/Snow Fox/SnowFox.aseprite new file mode 100644 index 0000000..358e6e1 Binary files /dev/null and b/assets/characters/animals/Snow Fox/SnowFox.aseprite differ diff --git a/assets/characters/animals/Snow Fox/SnowFox.gif b/assets/characters/animals/Snow Fox/SnowFox.gif new file mode 100644 index 0000000..db05fa5 Binary files /dev/null and b/assets/characters/animals/Snow Fox/SnowFox.gif differ diff --git a/assets/characters/animals/Snow Fox/SnowFox.png b/assets/characters/animals/Snow Fox/SnowFox.png new file mode 100644 index 0000000..43f1636 Binary files /dev/null and b/assets/characters/animals/Snow Fox/SnowFox.png differ diff --git a/assets/characters/animals/Snow Fox/SnowFox.png.import b/assets/characters/animals/Snow Fox/SnowFox.png.import new file mode 100644 index 0000000..f78e0cb --- /dev/null +++ b/assets/characters/animals/Snow Fox/SnowFox.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cig2j8vh061c2" +path="res://.godot/imported/SnowFox.png-5da2eb6b56f0d3049f1451c7aed0b878.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Snow Fox/SnowFox.png" +dest_files=["res://.godot/imported/SnowFox.png-5da2eb6b56f0d3049f1451c7aed0b878.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/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.aseprite b/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.aseprite new file mode 100644 index 0000000..f747378 Binary files /dev/null and b/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.aseprite differ diff --git a/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.gif b/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.gif new file mode 100644 index 0000000..f43a16f Binary files /dev/null and b/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.gif differ diff --git a/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.png b/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.png new file mode 100644 index 0000000..ea9f161 Binary files /dev/null and b/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.png differ diff --git a/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.png.import b/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.png.import new file mode 100644 index 0000000..b08992b --- /dev/null +++ b/assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://1uh6meq2g7pw" +path="res://.godot/imported/SpikeyPorcupine.png-3588d2059214390b3bd6e5d5653cf965.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.png" +dest_files=["res://.godot/imported/SpikeyPorcupine.png-3588d2059214390b3bd6e5d5653cf965.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/assets/characters/animals/Stinky Skunk/StinkySkunk.aseprite b/assets/characters/animals/Stinky Skunk/StinkySkunk.aseprite new file mode 100644 index 0000000..ea8d09d Binary files /dev/null and b/assets/characters/animals/Stinky Skunk/StinkySkunk.aseprite differ diff --git a/assets/characters/animals/Stinky Skunk/StinkySkunk.gif b/assets/characters/animals/Stinky Skunk/StinkySkunk.gif new file mode 100644 index 0000000..f34db7a Binary files /dev/null and b/assets/characters/animals/Stinky Skunk/StinkySkunk.gif differ diff --git a/assets/characters/animals/Stinky Skunk/StinkySkunk.png b/assets/characters/animals/Stinky Skunk/StinkySkunk.png new file mode 100644 index 0000000..953b5ea Binary files /dev/null and b/assets/characters/animals/Stinky Skunk/StinkySkunk.png differ diff --git a/assets/characters/animals/Stinky Skunk/StinkySkunk.png.import b/assets/characters/animals/Stinky Skunk/StinkySkunk.png.import new file mode 100644 index 0000000..0e3d8b5 --- /dev/null +++ b/assets/characters/animals/Stinky Skunk/StinkySkunk.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dx323aeidjler" +path="res://.godot/imported/StinkySkunk.png-4c08d373fe1dc8fbb5695a9ace20973e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Stinky Skunk/StinkySkunk.png" +dest_files=["res://.godot/imported/StinkySkunk.png-4c08d373fe1dc8fbb5695a9ace20973e.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/assets/characters/animals/Timber Wolf/TimberWolf.aseprite b/assets/characters/animals/Timber Wolf/TimberWolf.aseprite new file mode 100644 index 0000000..f00ee35 Binary files /dev/null and b/assets/characters/animals/Timber Wolf/TimberWolf.aseprite differ diff --git a/assets/characters/animals/Timber Wolf/TimberWolf.gif b/assets/characters/animals/Timber Wolf/TimberWolf.gif new file mode 100644 index 0000000..4ae2739 Binary files /dev/null and b/assets/characters/animals/Timber Wolf/TimberWolf.gif differ diff --git a/assets/characters/animals/Timber Wolf/TimberWolf.png b/assets/characters/animals/Timber Wolf/TimberWolf.png new file mode 100644 index 0000000..22f8af0 Binary files /dev/null and b/assets/characters/animals/Timber Wolf/TimberWolf.png differ diff --git a/assets/characters/animals/Timber Wolf/TimberWolf.png.import b/assets/characters/animals/Timber Wolf/TimberWolf.png.import new file mode 100644 index 0000000..d33b962 --- /dev/null +++ b/assets/characters/animals/Timber Wolf/TimberWolf.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ctg64vc7kxwcq" +path="res://.godot/imported/TimberWolf.png-6517eed8f8f91d741f29eab507d7592a.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Timber Wolf/TimberWolf.png" +dest_files=["res://.godot/imported/TimberWolf.png-6517eed8f8f91d741f29eab507d7592a.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/assets/characters/animals/Tiny Chick/TinyChick.aseprite b/assets/characters/animals/Tiny Chick/TinyChick.aseprite new file mode 100644 index 0000000..7010441 Binary files /dev/null and b/assets/characters/animals/Tiny Chick/TinyChick.aseprite differ diff --git a/assets/characters/animals/Tiny Chick/TinyChick.gif b/assets/characters/animals/Tiny Chick/TinyChick.gif new file mode 100644 index 0000000..902efc9 Binary files /dev/null and b/assets/characters/animals/Tiny Chick/TinyChick.gif differ diff --git a/assets/characters/animals/Tiny Chick/TinyChick.png b/assets/characters/animals/Tiny Chick/TinyChick.png new file mode 100644 index 0000000..d8ac7e2 Binary files /dev/null and b/assets/characters/animals/Tiny Chick/TinyChick.png differ diff --git a/assets/characters/animals/Tiny Chick/TinyChick.png.import b/assets/characters/animals/Tiny Chick/TinyChick.png.import new file mode 100644 index 0000000..4b90264 --- /dev/null +++ b/assets/characters/animals/Tiny Chick/TinyChick.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://elhjscadwqu0" +path="res://.godot/imported/TinyChick.png-d2a991e7fc7dfdafcae1d64e25a723a9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/characters/animals/Tiny Chick/TinyChick.png" +dest_files=["res://.godot/imported/TinyChick.png-d2a991e7fc7dfdafcae1d64e25a723a9.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/assets/font/NotoColorEmoji-Regular.ttf b/assets/font/NotoColorEmoji-Regular.ttf new file mode 100644 index 0000000..24be958 --- /dev/null +++ b/assets/font/NotoColorEmoji-Regular.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52215b679b7e1b2d69740af8ad7549bf9697084307ac157c404ffae7ccdcee5d +size 25097144 diff --git a/assets/font/NotoColorEmoji-Regular.ttf.import b/assets/font/NotoColorEmoji-Regular.ttf.import new file mode 100644 index 0000000..9390485 --- /dev/null +++ b/assets/font/NotoColorEmoji-Regular.ttf.import @@ -0,0 +1,36 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://d2vuqc82hbh5b" +path="res://.godot/imported/NotoColorEmoji-Regular.ttf-3577461e3a993fe0291874403bf6cecc.fontdata" + +[deps] + +source_file="res://assets/font/NotoColorEmoji-Regular.ttf" +dest_files=["res://.godot/imported/NotoColorEmoji-Regular.ttf-3577461e3a993fe0291874403bf6cecc.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=true +disable_embedded_bitmaps=true +multichannel_signed_distance_field=false +msdf_pixel_range=1 +msdf_size=48 +allow_system_fallback=false +force_autohinter=false +modulate_color_glyphs=false +hinting=0 +subpixel_positioning=0 +keep_rounding_remainders=false +oversampling=5.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/assets/images/logo.png b/assets/images/logo.png new file mode 100644 index 0000000..fe12632 Binary files /dev/null and b/assets/images/logo.png differ diff --git a/assets/images/logo.png.import b/assets/images/logo.png.import new file mode 100644 index 0000000..0c383a7 --- /dev/null +++ b/assets/images/logo.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bndq3avrtfck0" +path="res://.godot/imported/logo.png-9164f87f81119ac7320e78ed71593bc1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/images/logo.png" +dest_files=["res://.godot/imported/logo.png-9164f87f81119ac7320e78ed71593bc1.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/export_presets.cfg b/export_presets.cfg index 93f5cbb..dc55008 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -31,9 +31,9 @@ variant/thread_support=false vram_texture_compression/for_desktop=true vram_texture_compression/for_mobile=false html/export_icon=true -html/custom_html_shell="" +html/custom_html_shell="res://export_template.html" html/head_include="" -html/canvas_resize_policy=2 +html/canvas_resize_policy=1 html/focus_canvas_on_start=true html/experimental_virtual_keyboard=false progressive_web_app/enabled=true @@ -44,6 +44,6 @@ progressive_web_app/orientation=0 progressive_web_app/icon_144x144="" progressive_web_app/icon_180x180="" progressive_web_app/icon_512x512="" -progressive_web_app/background_color=Color(0, 0, 0, 1) +progressive_web_app/background_color=Color(0.81960785, 0.7176471, 0.47843137, 1) threads/emscripten_pool_size=8 threads/godot_pool_size=4 diff --git a/export_template.html b/export_template.html new file mode 100644 index 0000000..871aae2 --- /dev/null +++ b/export_template.html @@ -0,0 +1,340 @@ + + + + + + $GODOT_PROJECT_NAME + + $GODOT_HEAD_INCLUDE + + + Your browser does not support the canvas tag. + + + +
+ + +
+
+ + + + + diff --git a/icon.svg b/icon.svg deleted file mode 100644 index c6bbb7d..0000000 --- a/icon.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/project.godot b/project.godot index 581b109..80cacf3 100644 --- a/project.godot +++ b/project.godot @@ -17,7 +17,8 @@ compatibility/default_parent_skeleton_in_mesh_instance_3d=true config/name="Clicker" run/main_scene="uid://bqtexca6cdr54" config/features=PackedStringArray("4.6", "Forward Plus") -config/icon="res://icon.svg" +boot_splash/bg_color=Color(0.5921569, 0.44313726, 0.2901961, 1) +boot_splash/image="uid://bndq3avrtfck0" [autoload] @@ -28,6 +29,7 @@ DebugMenu="*uid://cggqb75a8w8r" Inventory="*res://scripts/inventory.gd" Unlocks="*res://scripts/unlocks.gd" Audio="*res://scripts/audio.gd" +AnimalFriendsManager="*res://scripts/animal_friends_manager.gd" [display] diff --git a/resources/UnlockData.tres b/resources/UnlockData.tres index 87baba5..b0ffd5a 100644 --- a/resources/UnlockData.tres +++ b/resources/UnlockData.tres @@ -1,4 +1,4 @@ -[gd_resource type="Resource" script_class="UnlockDataCollection" load_steps=12 format=3 uid="uid://b4c01yrmp1wf2"] +[gd_resource type="Resource" script_class="UnlockDataCollection" format=3 uid="uid://b4c01yrmp1wf2"] [ext_resource type="Script" uid="uid://bg1ymgbdcwc0j" path="res://resources/UnlockDataCollection.gd" id="1_gdehu"] [ext_resource type="Script" uid="uid://biqqffne7dd8r" path="res://resources/UnlockDataResource.gd" id="2_1js7i"] @@ -6,13 +6,13 @@ [sub_resource type="Resource" id="Resource_gdehu"] script = ExtResource("2_1js7i") unlock_id = 1 -unlock_name = "Marketing" +unlock_name = "Market Charm" unlock_description = "Affects the amount people are willing to pay for your whittling" base_cost = 100 is_scaling = true max_rank = 8 -cost_ladder = [100, 350, 1000, 3000, 9000, 28000, 80000, 220000] -effect_ladder = [1.08, 1.18, 1.3, 1.45, 1.65, 1.9, 2.3, 3.0] +cost_ladder = Array[int]([100, 350, 1000, 3000, 9000, 28000, 80000, 220000]) +effect_ladder = Array[float]([1.08, 1.18, 1.3, 1.45, 1.65, 1.9, 2.3, 3.0]) base_modifiers = { "sale_price_modifier": 1.08 } @@ -21,13 +21,13 @@ metadata/_custom_type_script = "uid://biqqffne7dd8r" [sub_resource type="Resource" id="Resource_1js7i"] script = ExtResource("2_1js7i") unlock_id = 2 -unlock_name = "Wood" +unlock_name = "Sharper Blades" unlock_description = "Increases the amount of wood produced per click" base_cost = 30 is_scaling = true max_rank = 5 -cost_ladder = [30, 100, 300, 900, 2500] -effect_ladder = [2.0, 3.0, 5.0, 7.0, 10.0] +cost_ladder = Array[int]([30, 100, 300, 900, 2500]) +effect_ladder = Array[float]([2.0, 3.0, 5.0, 7.0, 10.0]) base_modifiers = { "wood_per_click_modifier": 2.0 } @@ -36,13 +36,13 @@ metadata/_custom_type_script = "uid://biqqffne7dd8r" [sub_resource type="Resource" id="Resource_xbpe0"] script = ExtResource("2_1js7i") unlock_id = 3 -unlock_name = "Demand" -unlock_description = "How many whittled products can be purchased per tick" +unlock_name = "Trade Winds" +unlock_description = "How many whittled products can be sold per tick" base_cost = 120 is_scaling = true max_rank = 7 -cost_ladder = [120, 400, 1200, 4000, 12000, 38000, 110000] -effect_ladder = [2.0, 3.0, 5.0, 7.0, 10.0, 14.0, 18.0] +cost_ladder = Array[int]([120, 400, 1200, 4000, 12000, 38000, 110000]) +effect_ladder = Array[float]([2.0, 3.0, 5.0, 7.0, 10.0, 14.0, 18.0]) base_modifiers = { "purchase_rate_modifier": 2.0 } @@ -51,13 +51,13 @@ metadata/_custom_type_script = "uid://biqqffne7dd8r" [sub_resource type="Resource" id="Resource_nbe0w"] script = ExtResource("2_1js7i") unlock_id = 4 -unlock_name = "Efficiency" +unlock_name = "Whittler's Focus" unlock_description = "How many things you can produce per whittle" base_cost = 60 is_scaling = true max_rank = 6 -cost_ladder = [60, 400, 1800, 8000, 35000, 140000] -effect_ladder = [2.0, 3.0, 4.0, 5.0, 6.0, 8.0] +cost_ladder = Array[int]([60, 400, 1800, 8000, 35000, 140000]) +effect_ladder = Array[float]([2.0, 3.0, 4.0, 5.0, 6.0, 8.0]) base_modifiers = { "efficiency_modifier": 2.0 } @@ -66,7 +66,7 @@ metadata/_custom_type_script = "uid://biqqffne7dd8r" [sub_resource type="Resource" id="Resource_ppuju"] script = ExtResource("2_1js7i") unlock_id = 5 -unlock_name = "Wholesale" +unlock_name = "Guild Contract" unlock_description = "Sell multiples of 100 at 20% less income" base_cost = 35000 base_modifiers = { @@ -77,13 +77,13 @@ metadata/_custom_type_script = "uid://biqqffne7dd8r" [sub_resource type="Resource" id="Resource_chx6j"] script = ExtResource("2_1js7i") unlock_id = 6 -unlock_name = "Multicraft" -unlock_description = "Just craft more stuff" +unlock_name = "Forest Friends" +unlock_description = "Your furry friends start crafting for some reason..." base_cost = 6000 is_scaling = true max_rank = 5 -cost_ladder = [6000, 20000, 55000, 140000, 320000] -effect_ladder = [1.0, 2.0, 3.0, 4.0, 5.0] +cost_ladder = Array[int]([6000, 20000, 55000, 140000, 320000]) +effect_ladder = Array[float]([1.0, 2.0, 3.0, 4.0, 5.0]) base_modifiers = { "multicraft_increase_modifier": 1.0 } @@ -92,13 +92,13 @@ metadata/_custom_type_script = "uid://biqqffne7dd8r" [sub_resource type="Resource" id="Resource_f82ch"] script = ExtResource("2_1js7i") unlock_id = 7 -unlock_name = "Autowood" -unlock_description = "Automatically gather a percent of a clicks wood per tick" +unlock_name = "Beaver Brigade" +unlock_description = "Automatically gain wood" base_cost = 150 is_scaling = true max_rank = 5 -cost_ladder = [150, 600, 2000, 6500, 20000] -effect_ladder = [0.1, 0.2, 0.35, 0.55, 0.8] +cost_ladder = Array[int]([150, 600, 2000, 6500, 20000]) +effect_ladder = Array[float]([0.1, 0.2, 0.35, 0.55, 0.8]) base_modifiers = { "autowood_modifier": 0.1 } @@ -107,13 +107,13 @@ metadata/_custom_type_script = "uid://biqqffne7dd8r" [sub_resource type="Resource" id="Resource_premium"] script = ExtResource("2_1js7i") unlock_id = 8 -unlock_name = "Premium Crafts" +unlock_name = "Artisan's Touch" unlock_description = "Your reputation for quality allows higher prices" base_cost = 8000 is_scaling = true max_rank = 5 -cost_ladder = [8000, 25000, 70000, 160000, 350000] -effect_ladder = [1.08, 1.18, 1.3, 1.45, 1.65] +cost_ladder = Array[int]([8000, 25000, 70000, 160000, 350000]) +effect_ladder = Array[float]([1.15, 1.3, 1.45, 1.65, 1.8]) base_modifiers = { "premium_price_modifier": 1.08 } @@ -122,13 +122,13 @@ metadata/_custom_type_script = "uid://biqqffne7dd8r" [sub_resource type="Resource" id="Resource_reputation"] script = ExtResource("2_1js7i") unlock_id = 9 -unlock_name = "Reputation" +unlock_name = "Legendary Crafter" unlock_description = "Loyal customers provide steady passive income" base_cost = 4000 is_scaling = true max_rank = 5 -cost_ladder = [4000, 12000, 35000, 90000, 200000] -effect_ladder = [4.0, 10.0, 22.0, 42.0, 75.0] +cost_ladder = Array[int]([4000, 12000, 35000, 90000, 200000]) +effect_ladder = Array[float]([4.0, 10.0, 22.0, 42.0, 75.0]) base_modifiers = { "reputation_income": 4.0 } diff --git a/scenes/animal.tscn b/scenes/animal.tscn new file mode 100644 index 0000000..fe9ccc7 --- /dev/null +++ b/scenes/animal.tscn @@ -0,0 +1,473 @@ +[gd_scene format=3 uid="uid://bfkpy8wqqktca"] + +[ext_resource type="Script" uid="uid://uhlsvqaaemre" path="res://scenes/scripts/animal.gd" id="1_hb82n"] +[ext_resource type="Texture2D" uid="uid://cig2j8vh061c2" path="res://assets/characters/animals/Snow Fox/SnowFox.png" id="1_ufccv"] +[ext_resource type="Texture2D" uid="uid://ckvge3k08px5c" path="res://assets/tiles/sun.png" id="2_35nom"] +[ext_resource type="Texture2D" uid="uid://1uh6meq2g7pw" path="res://assets/characters/animals/Spikey Porcupine/SpikeyPorcupine.png" id="2_236rq"] +[ext_resource type="Texture2D" uid="uid://ctg64vc7kxwcq" path="res://assets/characters/animals/Timber Wolf/TimberWolf.png" id="3_3c5tq"] +[ext_resource type="Texture2D" uid="uid://dccs841gqvsnr" path="res://assets/characters/animals/Meowing Cat/MeowingCat.png" id="4_ieass"] +[ext_resource type="Texture2D" uid="uid://drghlunsulio" path="res://assets/characters/animals/Honking Goose/HonkingGoose.png" id="5_ieass"] +[ext_resource type="Texture2D" uid="uid://rvdwc66bww3" path="res://assets/characters/animals/Croaking Toad/CroakingToad.png" id="6_d7epe"] +[ext_resource type="Texture2D" uid="uid://elhjscadwqu0" path="res://assets/characters/animals/Tiny Chick/TinyChick.png" id="7_brjsm"] +[ext_resource type="Texture2D" uid="uid://b7hgiys1h18ov" path="res://assets/characters/animals/SleepDog.png" id="8_ybi86"] + +[sub_resource type="AtlasTexture" id="AtlasTexture_35nom"] +atlas = ExtResource("1_ufccv") +region = Rect2(0, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_3u0el"] +atlas = ExtResource("1_ufccv") +region = Rect2(16, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_brrv0"] +atlas = ExtResource("1_ufccv") +region = Rect2(32, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_2g4xt"] +atlas = ExtResource("1_ufccv") +region = Rect2(48, 0, 16, 16) + +[sub_resource type="SpriteFrames" id="SpriteFrames_m5q2l"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_35nom") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_3u0el") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_brrv0") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_2g4xt") +}], +"loop": true, +"name": &"default", +"speed": 4.0 +}] + +[sub_resource type="AtlasTexture" id="AtlasTexture_3v7ex"] +atlas = ExtResource("2_236rq") +region = Rect2(0, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_3c5tq"] +atlas = ExtResource("2_236rq") +region = Rect2(16, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ieass"] +atlas = ExtResource("2_236rq") +region = Rect2(32, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_d7epe"] +atlas = ExtResource("2_236rq") +region = Rect2(48, 0, 16, 16) + +[sub_resource type="SpriteFrames" id="SpriteFrames_brjsm"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_3v7ex") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_3c5tq") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ieass") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_d7epe") +}], +"loop": true, +"name": &"default", +"speed": 4.0 +}] + +[sub_resource type="AtlasTexture" id="AtlasTexture_brjsm"] +atlas = ExtResource("3_3c5tq") +region = Rect2(0, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_ybi86"] +atlas = ExtResource("3_3c5tq") +region = Rect2(16, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_hb82n"] +atlas = ExtResource("3_3c5tq") +region = Rect2(32, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_fiknr"] +atlas = ExtResource("3_3c5tq") +region = Rect2(48, 0, 16, 16) + +[sub_resource type="SpriteFrames" id="SpriteFrames_d06xb"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_brjsm") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_ybi86") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_hb82n") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_fiknr") +}], +"loop": true, +"name": &"default", +"speed": 2.0 +}] + +[sub_resource type="AtlasTexture" id="AtlasTexture_gceah"] +atlas = ExtResource("4_ieass") +region = Rect2(0, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_s21jh"] +atlas = ExtResource("4_ieass") +region = Rect2(16, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_xjlt7"] +atlas = ExtResource("4_ieass") +region = Rect2(32, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_jp041"] +atlas = ExtResource("4_ieass") +region = Rect2(48, 0, 16, 16) + +[sub_resource type="SpriteFrames" id="SpriteFrames_7do3a"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_gceah") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_s21jh") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_xjlt7") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_jp041") +}], +"loop": true, +"name": &"default", +"speed": 4.0 +}] + +[sub_resource type="AtlasTexture" id="AtlasTexture_d06xb"] +atlas = ExtResource("5_ieass") +region = Rect2(0, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_7do3a"] +atlas = ExtResource("5_ieass") +region = Rect2(16, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_qag4x"] +atlas = ExtResource("5_ieass") +region = Rect2(32, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_10oeq"] +atlas = ExtResource("5_ieass") +region = Rect2(48, 0, 16, 16) + +[sub_resource type="SpriteFrames" id="SpriteFrames_lt1bt"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_d06xb") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_7do3a") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_qag4x") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_10oeq") +}], +"loop": true, +"name": &"default", +"speed": 4.0 +}] + +[sub_resource type="AtlasTexture" id="AtlasTexture_lt1bt"] +atlas = ExtResource("6_d7epe") +region = Rect2(0, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_748p7"] +atlas = ExtResource("6_d7epe") +region = Rect2(16, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_h4cte"] +atlas = ExtResource("6_d7epe") +region = Rect2(32, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_rori6"] +atlas = ExtResource("6_d7epe") +region = Rect2(48, 0, 16, 16) + +[sub_resource type="SpriteFrames" id="SpriteFrames_gbowl"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_lt1bt") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_748p7") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_h4cte") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_rori6") +}], +"loop": true, +"name": &"default", +"speed": 5.0 +}] + +[sub_resource type="AtlasTexture" id="AtlasTexture_gbowl"] +atlas = ExtResource("7_brjsm") +region = Rect2(0, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_em8nj"] +atlas = ExtResource("7_brjsm") +region = Rect2(16, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_j1qxf"] +atlas = ExtResource("7_brjsm") +region = Rect2(32, 0, 16, 16) + +[sub_resource type="AtlasTexture" id="AtlasTexture_lowru"] +atlas = ExtResource("7_brjsm") +region = Rect2(48, 0, 16, 16) + +[sub_resource type="SpriteFrames" id="SpriteFrames_20wpp"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_gbowl") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_em8nj") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_j1qxf") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_lowru") +}], +"loop": true, +"name": &"default", +"speed": 5.0 +}] + +[sub_resource type="AtlasTexture" id="AtlasTexture_20wpp"] +atlas = ExtResource("8_ybi86") +region = Rect2(0, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_eaemt"] +atlas = ExtResource("8_ybi86") +region = Rect2(64, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_xa74n"] +atlas = ExtResource("8_ybi86") +region = Rect2(128, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_v8kal"] +atlas = ExtResource("8_ybi86") +region = Rect2(192, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_r1mgj"] +atlas = ExtResource("8_ybi86") +region = Rect2(256, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_wxa41"] +atlas = ExtResource("8_ybi86") +region = Rect2(320, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_33tgj"] +atlas = ExtResource("8_ybi86") +region = Rect2(384, 0, 64, 64) + +[sub_resource type="AtlasTexture" id="AtlasTexture_l8a8u"] +atlas = ExtResource("8_ybi86") +region = Rect2(448, 0, 64, 64) + +[sub_resource type="SpriteFrames" id="SpriteFrames_d57ma"] +animations = [{ +"frames": [{ +"duration": 1.0, +"texture": SubResource("AtlasTexture_20wpp") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_eaemt") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_xa74n") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_v8kal") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_r1mgj") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_wxa41") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_33tgj") +}, { +"duration": 1.0, +"texture": SubResource("AtlasTexture_l8a8u") +}], +"loop": true, +"name": &"default", +"speed": 3.0 +}] + +[sub_resource type="Curve" id="Curve_lrhn5"] +_data = [Vector2(0, 1), 0.0, 0.0, 0, 0, Vector2(1, 0), 0.0, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="CurveTexture" id="CurveTexture_okhi1"] +curve = SubResource("Curve_lrhn5") + +[sub_resource type="Gradient" id="Gradient_my7n2"] +colors = PackedColorArray(0.6267965, 0.35946804, 0.115510084, 0.3764706, 0.5640522, 0.46861154, 0.16076079, 0.627451) + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_rkhd4"] +gradient = SubResource("Gradient_my7n2") + +[sub_resource type="Curve" id="Curve_dp8ca"] +_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(1, 1), 0.0, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="CurveTexture" id="CurveTexture_deeqb"] +curve = SubResource("Curve_dp8ca") + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_0lw5n"] +lifetime_randomness = 0.73 +particle_flag_disable_z = true +emission_shape = 1 +emission_sphere_radius = 1.0 +angle_min = 1.0728835e-05 +angle_max = 115.70001 +inherit_velocity_ratio = 0.154 +direction = Vector3(0, 0, 0) +spread = 98.933 +initial_velocity_max = 2.0 +gravity = Vector3(0, 0, 0) +linear_accel_min = 0.99999774 +linear_accel_max = 4.9999976 +scale_min = 0.19999999 +scale_max = 0.7 +scale_curve = SubResource("CurveTexture_deeqb") +color_ramp = SubResource("GradientTexture1D_rkhd4") +alpha_curve = SubResource("CurveTexture_okhi1") +hue_variation_min = -0.11000002 +hue_variation_max = 0.089999974 +turbulence_enabled = true +turbulence_noise_strength = 0.56 +turbulence_noise_scale = 4.861 + +[sub_resource type="Curve" id="Curve_rkhd4"] +_data = [Vector2(0, 0.007272601), 0.0, 0.0, 0, 0, Vector2(0.46874994, 0.758909), 0.0, 0.0, 0, 0, Vector2(0.73632807, 0.8865454), 0.0, 0.0, 0, 0] +point_count = 3 + +[sub_resource type="CurveTexture" id="CurveTexture_lrhn5"] +curve = SubResource("Curve_rkhd4") + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_agfs1"] +particle_flag_disable_z = true +gravity = Vector3(0, 0, 0) +linear_accel_min = -2.2351742e-06 +linear_accel_max = 6.4299974 +radial_accel_min = -2.2351742e-06 +radial_accel_max = 23.289997 +tangential_accel_min = -24.100002 +tangential_accel_max = 10.439998 +color = Color(0.84313726, 0.56078434, 0, 0.7921569) +alpha_curve = SubResource("CurveTexture_lrhn5") + +[node name="Character" type="Node2D" unique_id=1357587844] +script = ExtResource("1_hb82n") +show_shavings = null + +[node name="Fox" type="AnimatedSprite2D" parent="." unique_id=565500848] +unique_name_in_owner = true +visible = false +sprite_frames = SubResource("SpriteFrames_m5q2l") +autoplay = "default" +frame_progress = 0.44348216 + +[node name="Porcupine" type="AnimatedSprite2D" parent="." unique_id=1272266059] +unique_name_in_owner = true +visible = false +sprite_frames = SubResource("SpriteFrames_brjsm") +autoplay = "default" +frame = 3 +frame_progress = 0.8018118 + +[node name="Wolf" type="AnimatedSprite2D" parent="." unique_id=1759146555] +unique_name_in_owner = true +visible = false +sprite_frames = SubResource("SpriteFrames_d06xb") +autoplay = "default" +frame = 3 +frame_progress = 0.19724818 + +[node name="Cat" type="AnimatedSprite2D" parent="." unique_id=1332976928] +unique_name_in_owner = true +visible = false +sprite_frames = SubResource("SpriteFrames_7do3a") +autoplay = "default" +frame_progress = 0.031060848 + +[node name="Goose" type="AnimatedSprite2D" parent="." unique_id=222745417] +unique_name_in_owner = true +visible = false +sprite_frames = SubResource("SpriteFrames_lt1bt") +autoplay = "default" +frame_progress = 0.4729133 + +[node name="Frog" type="AnimatedSprite2D" parent="." unique_id=679106242] +unique_name_in_owner = true +visible = false +sprite_frames = SubResource("SpriteFrames_gbowl") +autoplay = "default" +frame = 1 +frame_progress = 0.27542892 + +[node name="Chick" type="AnimatedSprite2D" parent="." unique_id=1754919209] +unique_name_in_owner = true +visible = false +sprite_frames = SubResource("SpriteFrames_20wpp") +autoplay = "default" +frame = 1 +frame_progress = 0.11994407 + +[node name="Dog" type="AnimatedSprite2D" parent="." unique_id=197188039] +unique_name_in_owner = true +visible = false +position = Vector2(0, -32) +sprite_frames = SubResource("SpriteFrames_d57ma") +autoplay = "default" + +[node name="Dust" type="GPUParticles2D" parent="." unique_id=785652497] +visible = false +position = Vector2(0, 1) +amount = 100 +texture = ExtResource("2_35nom") +lifetime = 10.0 +preprocess = 2.0 +explosiveness = 0.06 +randomness = 0.39 +process_material = SubResource("ParticleProcessMaterial_0lw5n") + +[node name="Shavings" type="GPUParticles2D" parent="." unique_id=1628407210] +unique_name_in_owner = true +visible = false +preprocess = 2.0 +process_material = SubResource("ParticleProcessMaterial_agfs1") diff --git a/scenes/button.tscn b/scenes/button.tscn index 2ad2a86..664af4a 100644 --- a/scenes/button.tscn +++ b/scenes/button.tscn @@ -1,33 +1,43 @@ -[gd_scene load_steps=7 format=3 uid="uid://b0bmsqlrg77le"] +[gd_scene format=3 uid="uid://b0bmsqlrg77le"] [ext_resource type="Texture2D" uid="uid://dx134esqj3kg3" path="res://assets/ui/buttonLong_brown.png" id="1_1bdt2"] [ext_resource type="Texture2D" uid="uid://bmdc4875jf16r" path="res://assets/ui/buttonLong_brown_pressed.png" id="2_8m7bo"] -[ext_resource type="Texture2D" uid="uid://ddghl4cooepr1" path="res://assets/ui/buttonLong_blue.png" id="3_t81cg"] -[ext_resource type="Texture2D" uid="uid://f0tde4s55m2o" path="res://assets/ui/buttonLong_grey.png" id="4_is61r"] +[ext_resource type="Texture2D" uid="uid://c3gmw2rffktva" path="res://assets/ui/buttonLong_beige_pressed.png" id="3_8m7bo"] +[ext_resource type="Texture2D" uid="uid://bk377v70i8bsw" path="res://assets/ui/buttonLong_blue_pressed.png" id="4_8m7bo"] [ext_resource type="Script" uid="uid://dj7uoaxxat5n4" path="res://scenes/scripts/button.gd" id="5_8m7bo"] [ext_resource type="Theme" uid="uid://bnbtwoxxd6cg5" path="res://assets/theme/clicker.theme" id="5_iw4ej"] -[node name="TextureButton" type="TextureButton"] +[node name="TextureButton" type="TextureButton" unique_id=186080222] custom_minimum_size = Vector2(100, 25) -offset_right = 100.0 -offset_bottom = 25.0 +offset_right = 103.0 +offset_bottom = 32.0 tooltip_text = "I need a tooltip bro" texture_normal = ExtResource("1_1bdt2") texture_pressed = ExtResource("2_8m7bo") -texture_hover = ExtResource("3_t81cg") -texture_disabled = ExtResource("4_is61r") +texture_hover = ExtResource("3_8m7bo") +texture_disabled = ExtResource("4_8m7bo") ignore_texture_size = true stretch_mode = 0 script = ExtResource("5_8m7bo") -[node name="CenterContainer" type="CenterContainer" parent="."] +[node name="CenterContainer" type="CenterContainer" parent="." unique_id=313903696] clip_contents = true custom_minimum_size = Vector2(100, 25) layout_mode = 0 offset_right = 100.0 offset_bottom = 25.0 -[node name="Label" type="Label" parent="CenterContainer"] +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer" unique_id=1263225362] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="NameLabel" type="Label" parent="CenterContainer/VBoxContainer" unique_id=1225878363] +layout_mode = 2 +theme = ExtResource("5_iw4ej") +text = "-" +horizontal_alignment = 1 + +[node name="PriceLabel" type="Label" parent="CenterContainer/VBoxContainer" unique_id=1012864835] layout_mode = 2 theme = ExtResource("5_iw4ej") text = "-" diff --git a/scenes/config_panel.tscn b/scenes/config_panel.tscn new file mode 100644 index 0000000..b545a0b --- /dev/null +++ b/scenes/config_panel.tscn @@ -0,0 +1,91 @@ +[gd_scene format=3 uid="uid://bqy5j8xm7o8ra"] + +[ext_resource type="Script" path="res://scenes/scripts/config_panel.gd" id="1_config"] + +[node name="ConfigPanel" type="Panel"] +unique_name_in_owner = true +light_mask = 2 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -200.0 +offset_top = -150.0 +offset_right = 200.0 +offset_bottom = 150.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_config") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 20 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 20 +theme_override_constants/margin_bottom = 20 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 +theme_override_constants/separation = 15 + +[node name="TitleLabel" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "Sound Settings" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="MusicContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/MusicContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Background Music" +vertical_alignment = 1 + +[node name="MusicToggle" type="CheckButton" parent="MarginContainer/VBoxContainer/MusicContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="ChopContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/ChopContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Wood Chop Sound" +vertical_alignment = 1 + +[node name="ChopToggle" type="CheckButton" parent="MarginContainer/VBoxContainer/ChopContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="MoneyContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/MoneyContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Money Sound" +vertical_alignment = 1 + +[node name="MoneyToggle" type="CheckButton" parent="MarginContainer/VBoxContainer/MoneyContainer"] +unique_name_in_owner = true +layout_mode = 2 + +[node name="HSeparator2" type="HSeparator" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="CloseButton" type="Button" parent="MarginContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Close" diff --git a/scenes/game.tscn b/scenes/game.tscn deleted file mode 100644 index 170b5f8..0000000 --- a/scenes/game.tscn +++ /dev/null @@ -1,3 +0,0 @@ -[gd_scene format=3 uid="uid://duhwm7m5hc506"] - -[node name="Game" type="Node3D"] diff --git a/scenes/game2.tscn b/scenes/game2.tscn index 22808b9..c5efb4e 100644 --- a/scenes/game2.tscn +++ b/scenes/game2.tscn @@ -18,11 +18,16 @@ [ext_resource type="Script" uid="uid://cpimo8q5dcjxf" path="res://scenes/scripts/fire_light.gd" id="16_5kdtj"] [ext_resource type="PackedScene" uid="uid://cbrkq6jd5a4ho" path="res://scenes/character.tscn" id="17_1hpkv"] [ext_resource type="PackedScene" uid="uid://cnyxwsj6i27ja" path="res://scenes/stock_pile.tscn" id="17_deeqb"] +[ext_resource type="PackedScene" uid="uid://bfkpy8wqqktca" path="res://scenes/animal.tscn" id="17_k8jaa"] [ext_resource type="PackedScene" uid="uid://bubjxrs8qmr4y" path="res://scenes/wood_pile.tscn" id="17_oibj5"] [ext_resource type="Script" uid="uid://cm84m3olmcc8o" path="res://scenes/scripts/ui_control.gd" id="17_q7h7c"] [ext_resource type="PackedScene" uid="uid://b0bmsqlrg77le" path="res://scenes/button.tscn" id="19_v4v8k"] +[ext_resource type="PackedScene" path="res://scenes/config_panel.tscn" id="20_config"] +[ext_resource type="Texture2D" uid="uid://dylc6y7ajsln3" path="res://assets/ui/panel_brown.png" id="22_hcndq"] +[ext_resource type="FontFile" uid="uid://bfq6d6y56gr4s" path="res://assets/font/m5x7.ttf" id="22_k8i0y"] [ext_resource type="Theme" uid="uid://bnbtwoxxd6cg5" path="res://assets/theme/clicker.theme" id="22_q7h7c"] [ext_resource type="Texture2D" uid="uid://dx134esqj3kg3" path="res://assets/ui/buttonLong_brown.png" id="23_lv1cq"] +[ext_resource type="FontFile" uid="uid://d2vuqc82hbh5b" path="res://assets/font/NotoColorEmoji-Regular.ttf" id="24_chdjo"] [ext_resource type="Texture2D" uid="uid://bmdc4875jf16r" path="res://assets/ui/buttonLong_brown_pressed.png" id="24_k8i0y"] [ext_resource type="Texture2D" uid="uid://ddghl4cooepr1" path="res://assets/ui/buttonLong_blue.png" id="25_hcndq"] [ext_resource type="Texture2D" uid="uid://f0tde4s55m2o" path="res://assets/ui/buttonLong_grey.png" id="26_chdjo"] @@ -1946,9 +1951,26 @@ gradient = SubResource("Gradient_5kdtj") fill = 1 fill_from = Vector2(0.5, 0.5) +[sub_resource type="FontVariation" id="FontVariation_hcndq"] +base_font = ExtResource("22_k8i0y") +variation_embolden = 0.2 + [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_q7h7c"] bg_color = Color(0, 0, 0, 0.7921569) +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_mjadu"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_k8jaa"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_hp34j"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_lxcs1"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_hxr5a"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_mjadu"] +bg_color = Color(0.5921569, 0.44313726, 0.2901961, 1) + [node name="GameScene" type="Node2D" unique_id=1879752572] [node name="Scene" type="Node2D" parent="." unique_id=1170541436] @@ -1960,42 +1982,57 @@ metadata/_edit_lock_ = true [node name="Sprite2D" type="Sprite2D" parent="Scene/Background" unique_id=1234031660] position = Vector2(43, -45) texture = ExtResource("1_xrrf0") +metadata/_edit_lock_ = true [node name="Sprite2D2" type="Sprite2D" parent="Scene/Background" unique_id=531673597] position = Vector2(43, -45) texture = ExtResource("2_kh4a2") +metadata/_edit_lock_ = true [node name="Sprite2D3" type="Sprite2D" parent="Scene/Background" unique_id=1328011980] position = Vector2(43, -45) texture = ExtResource("3_8j387") +metadata/_edit_lock_ = true [node name="Sprite2D4" type="Sprite2D" parent="Scene/Background" unique_id=236948197] position = Vector2(43, -45) texture = ExtResource("4_5dhap") +metadata/_edit_lock_ = true [node name="Sprite2D5" type="Sprite2D" parent="Scene/Background" unique_id=1992550436] position = Vector2(44, -45) texture = ExtResource("5_s427y") +metadata/_edit_lock_ = true [node name="BGTrees2" type="TileMapLayer" parent="Scene" unique_id=1214041213] position = Vector2(-49, 59) tile_map_data = PackedByteArray("AAACAPr/BgAAAAEAAAACAPv/BgAAAAIAAAACAPz/BgAAAAMAAAACAP3/BgAAAAQAAAADAPn/BgABAAAAAAADAPr/BgABAAEAAAADAPv/BgABAAIAAAADAPz/BgABAAMAAAADAP3/BgABAAQAAAADAP7/BgABAAUAAAAEAPn/BgACAAAAAAAEAPr/BgACAAEAAAAEAPv/BgACAAIAAAAEAPz/BgACAAMAAAAEAP3/BgACAAQAAAAEAP7/BgACAAUAAAAEAP//BgACAAYAAAAFAPn/BgADAAAAAAAFAPr/BgADAAEAAAAFAPv/BgADAAIAAAAFAPz/BgADAAMAAAAFAP3/BgADAAQAAAAGAPr/BgAEAAEAAAAGAPv/BgAEAAIAAAAGAPz/BgAEAAMAAAAGAP3/BgAEAAQAAAAOAPr/BgAAAAEAAAAOAPv/BgAAAAIAAAAOAPz/BgAAAAMAAAAOAP3/BgAAAAQAAAAPAPn/BgABAAAAAAAPAPr/BgABAAEAAAAPAPv/BgABAAIAAAAPAPz/BgABAAMAAAAPAP3/BgABAAQAAAAPAP7/BgABAAUAAAAQAPn/BgACAAAAAAAQAPr/BgACAAEAAAAQAPv/BgACAAIAAAAQAPz/BgACAAMAAAAQAP3/BgACAAQAAAAQAP7/BgACAAUAAAAQAP//BgACAAYAAAARAPn/BgADAAAAAAARAPr/BgADAAEAAAARAPv/BgADAAIAAAARAPz/BgADAAMAAAARAP3/BgADAAQAAAASAPr/BgAEAAEAAAASAPv/BgAEAAIAAAASAPz/BgAEAAMAAAASAP3/BgAEAAQAAAD5//z/BwAAAAcAAAD5//3/BwAAAAgAAAD5//7/BwAAAAkAAAD5////BwAAAAoAAAD6//v/BwABAAYAAAD6//z/BwABAAcAAAD6//3/BwABAAgAAAD6//7/BwABAAkAAAD6////BwABAAoAAAD7//n/BwACAAQAAAD7//r/BwACAAUAAAD7//v/BwACAAYAAAD7//z/BwACAAcAAAD7//3/BwACAAgAAAD7//7/BwACAAkAAAD7////BwACAAoAAAD8//f/BwADAAIAAAD8//j/BwADAAMAAAD8//n/BwADAAQAAAD8//r/BwADAAUAAAD8//v/BwADAAYAAAD8//z/BwADAAcAAAD8//3/BwADAAgAAAD8//7/BwADAAkAAAD9//b/BwAEAAEAAAD9//f/BwAEAAIAAAD9//j/BwAEAAMAAAD9//n/BwAEAAQAAAD9//r/BwAEAAUAAAD9//v/BwAEAAYAAAD9//z/BwAEAAcAAAD9//3/BwAEAAgAAAD9//7/BwAEAAkAAAD+//b/BwAFAAEAAAD+//f/BwAFAAIAAAD+//j/BwAFAAMAAAD+//n/BwAFAAQAAAD+//r/BwAFAAUAAAD+//v/BwAFAAYAAAD+//z/BwAFAAcAAAD+//3/BwAFAAgAAAD+//7/BwAFAAkAAAD///X/BwAGAAAAAAD///b/BwAGAAEAAAD///f/BwAGAAIAAAD///j/BwAGAAMAAAD///n/BwAGAAQAAAD///r/BwAGAAUAAAD///v/BwAGAAYAAAD///z/BwAGAAcAAAD///3/BwAGAAgAAAAAAPj/BwAHAAMAAAAAAPn/BwAHAAQAAAAAAPr/BwAHAAUAAAAMAP7/AQAMABQAAAAMAP//AQAMABUAAAANAP7/AQANABQAAAANAP//AQANABUAAAAOAP7/AQAOABQAAAAOAP//AQAOABUAAAAPAP//AQAPABUAAAAKAP//AQAKABUAAAALAP//AQALABUAAAAAAP7/AQAAABwAAAAAAP//AQAAAB0AAAABAP7/AQABABwAAAABAP//AQABAB0AAAACAP7/AQACABwAAAACAP//AQACAB0AAAADAP//AQADAB0AAAAVAP//AQAGAB0AAAAWAP//AQAHAB0AAAA=") tile_set = SubResource("TileSet_btr28") +metadata/_edit_lock_ = true [node name="BGTrees1" type="TileMapLayer" parent="Scene" unique_id=232331490] position = Vector2(-49, 59) tile_map_data = PackedByteArray("AAD9//7/BAAKAAMAAAD9////BAAKAAQAAAD+////BAALAAQAAAAHAP7/AgAGAAsAAAAHAP//AgAGAAwAAAAIAP3/AgAHAAoAAAAIAP7/AgAHAAsAAAAIAP//AgAHAAwAAAAJAP7/AgAIAAsAAAAJAP//AgAIAAwAAAABAPj/AgAAAAUAAAABAPn/AgAAAAYAAAACAPf/AgABAAQAAAACAPj/AgABAAUAAAACAPn/AgABAAYAAAACAPr/AgABAAcAAAADAPf/AgACAAQAAAADAPj/AgACAAUAAAADAPn/AgACAAYAAAADAPr/AgACAAcAAAAEAPX/AgADAAIAAAAEAPb/AgADAAMAAAAEAPf/AgADAAQAAAAEAPj/AgADAAUAAAAEAPn/AgADAAYAAAAEAPr/AgADAAcAAAAEAPv/AgADAAgAAAAFAPT/AgAEAAEAAAAFAPX/AgAEAAIAAAAFAPb/AgAEAAMAAAAFAPf/AgAEAAQAAAAFAPj/AgAEAAUAAAAFAPn/AgAEAAYAAAAFAPr/AgAEAAcAAAAFAPv/AgAEAAgAAAAGAPT/AgAFAAEAAAAGAPX/AgAFAAIAAAAGAPb/AgAFAAMAAAAGAPf/AgAFAAQAAAAGAPj/AgAFAAUAAAAGAPn/AgAFAAYAAAAGAPr/AgAFAAcAAAAGAPv/AgAFAAgAAAAHAPP/AgAGAAAAAAAHAPT/AgAGAAEAAAAHAPX/AgAGAAIAAAAHAPb/AgAGAAMAAAAHAPf/AgAGAAQAAAAHAPj/AgAGAAUAAAAHAPn/AgAGAAYAAAAHAPr/AgAGAAcAAAAHAPv/AgAGAAgAAAAHAPz/AgAGAAkAAAAIAPP/AgAHAAAAAAAIAPT/AgAHAAEAAAAIAPX/AgAHAAIAAAAIAPb/AgAHAAMAAAAIAPf/AgAHAAQAAAAIAPj/AgAHAAUAAAAIAPn/AgAHAAYAAAAIAPr/AgAHAAcAAAAIAPv/AgAHAAgAAAAIAPz/AgAHAAkAAAAJAPP/AgAIAAAAAAAJAPT/AgAIAAEAAAAJAPX/AgAIAAIAAAAJAPb/AgAIAAMAAAAJAPf/AgAIAAQAAAAJAPj/AgAIAAUAAAAJAPn/AgAIAAYAAAAJAPr/AgAIAAcAAAAJAPv/AgAIAAgAAAAJAPz/AgAIAAkAAAAJAP3/AgAIAAoAAAAKAPP/AgAJAAAAAAAKAPT/AgAJAAEAAAAKAPX/AgAJAAIAAAAKAPb/AgAJAAMAAAAKAPf/AgAJAAQAAAAKAPj/AgAJAAUAAAAKAPn/AgAJAAYAAAAKAPr/AgAJAAcAAAAKAPv/AgAJAAgAAAAKAP//AgAJAAwAAAALAPP/AgAKAAAAAAALAPT/AgAKAAEAAAALAPX/AgAKAAIAAAALAPb/AgAKAAMAAAALAPf/AgAKAAQAAAALAPj/AgAKAAUAAAALAPn/AgAKAAYAAAALAPr/AgAKAAcAAAALAPv/AgAKAAgAAAAMAPT/AgALAAEAAAAMAPX/AgALAAIAAAAMAPb/AgALAAMAAAAMAPf/AgALAAQAAAAMAPj/AgALAAUAAAAMAPn/AgALAAYAAAAMAPr/AgALAAcAAAAMAPv/AgALAAgAAAAMAPz/AgALAAkAAAANAPT/AgAMAAEAAAANAPX/AgAMAAIAAAANAPb/AgAMAAMAAAANAPf/AgAMAAQAAAANAPj/AgAMAAUAAAANAPn/AgAMAAYAAAANAPr/AgAMAAcAAAANAPv/AgAMAAgAAAANAPz/AgAMAAkAAAAOAPf/AgANAAQAAAAOAPj/AgANAAUAAAAOAPn/AgANAAYAAAAOAPr/AgANAAcAAAAOAPv/AgANAAgAAAAPAPj/AgAOAAUAAAAPAPn/AgAOAAYAAAAPAPr/AgAOAAcAAAAPAPv/AgAOAAgAAAAQAPj/AgAPAAUAAAAQAPn/AgAPAAYAAAAQAPr/AgAPAAcAAAAQAPv/AgAPAAgAAAD5//v/BAAGAAAAAAD5//z/BAAGAAEAAAD5//3/BAAGAAIAAAD5//7/BAAGAAMAAAD6//v/BAAHAAAAAAD6//z/BAAHAAEAAAD6//3/BAAHAAIAAAD6//7/BAAHAAMAAAD7//v/BAAIAAAAAAD7//z/BAAIAAEAAAD7//3/BAAIAAIAAAD7//7/BAAIAAMAAAD7////BAAIAAQAAAD7/wAABAAIAAUAAAD8//z/BAAJAAEAAAD8//3/BAAJAAIAAAD8//7/BAAJAAMAAAD8////BAAJAAQAAAD8/wAABAAJAAUAAAD9/wAABAAKAAUAAAD+/wAABAALAAUAAAD5////BAAGAAMAAAD5/wAABAAGAAMAAAD6/wEABAAGAAMAAAD6/wIABAAGAAMAAAD6/wMABAAGAAMAAAD5/wMABAAGAAMAAAD5/wQABAAGAAMAAAD6/wQABAAGAAMAAAD7/wQABAAGAAMAAAD8/wQABAAGAAMAAAD9/wQABAAGAAMAAAD9/wMABAAGAAMAAAD8/wMABAAGAAMAAAD7/wMABAAGAAMAAAD4/wIABAAGAAMAAAD4/wEABAAGAAMAAAD5/wEABAAGAAMAAAD6/wAABAAGAAMAAAD6////BAAGAAMAAAD5/wIABAAGAAMAAAD7/wEABAAGAAMAAAD8/wEABAAGAAMAAAD9/wEABAAGAAMAAAD+/wEABAAGAAMAAAD+/wIABAAGAAMAAAD9/wIABAAGAAMAAAD8/wIABAAGAAMAAAD7/wIABAAGAAMAAAD+/wMABAAGAAMAAAD+/wQABAAGAAMAAAD4/wMABAAGAAMAAAD4/wQABAAGAAMAAAD4////BAAGAAMAAAD4//7/AAAEAA8AAAD4//3/AAAEAA4AAADx////AAACAA8AAADy////AAACAA8AAADz//7/AAADAA8AAADz////AAACAA8AAAD0//7/AAAEAA8AAAD0////AAACAA8AAAD1//7/AAABAA8AAAD1////AAACAA8AAAD2////AAACAA8AAAD1//v/AAABAAwAAAD1//z/AAABAA0AAAD1//3/AAABAA4AAAD2//v/AAACAAwAAAD2//z/AAACAA0AAAD2//3/AAACAA4AAAD2//7/AAACAA8AAAD3//v/AAADAAwAAAD3//z/AAADAA0AAAD3//3/AAADAA4AAAD3//7/AAADAA8AAAD4//v/AAAEAAwAAAD4//z/AAAEAA0AAADx//v/AAADAAwAAADx//z/AAADAA0AAADx//3/AAADAA4AAADx//7/AAADAA8AAADy//v/AAAEAAwAAADy//z/AAAEAA0AAADy//3/AAAEAA4AAADy//7/AAAEAA8AAADz//v/AAADAAwAAADz//z/AAADAA0AAADz//3/AAADAA4AAAD0//v/AAAEAAwAAAD0//z/AAAEAA0AAAD0//3/AAAEAA4AAADv//v/AAABAAwAAADv//z/AAABAA0AAADv//3/AAABAA4AAADv//7/AAABAA8AAADw//v/AAACAAwAAADw//z/AAACAA0AAADw//3/AAACAA4AAADw//7/AAACAA8AAAD3////AAACAA8AAADw////AAACAA8AAADv////AAACAA8AAAA=") tile_set = SubResource("TileSet_btr28") +metadata/_edit_lock_ = true [node name="BGTrees0" type="TileMapLayer" parent="Scene" unique_id=192241590] position = Vector2(-49, 59) tile_map_data = PackedByteArray("AAARAPn/BgAAAAEAAAARAPr/BgAAAAIAAAARAPv/BgAAAAMAAAARAPz/BgAAAAQAAAASAPj/BgABAAAAAAASAPn/BgABAAEAAAASAPr/BgABAAIAAAASAPv/BgABAAMAAAASAPz/BgABAAQAAAASAP3/BgABAAUAAAATAPj/BgACAAAAAAATAPn/BgACAAEAAAATAPr/BgACAAIAAAATAPv/BgACAAMAAAATAPz/BgACAAQAAAATAP3/BgACAAUAAAATAP7/BgACAAYAAAAUAPj/BgADAAAAAAAUAPn/BgADAAEAAAAUAPr/BgADAAIAAAAUAPv/BgADAAMAAAAUAPz/BgADAAQAAAAVAPn/BgAEAAEAAAAVAPr/BgAEAAIAAAAVAPv/BgAEAAMAAAAVAPz/BgAEAAQAAADx//n/BgAAAAEAAADx//r/BgAAAAIAAADx//v/BgAAAAMAAADx//z/BgAAAAQAAADy//j/BgABAAAAAADy//n/BgABAAEAAADy//r/BgABAAIAAADy//v/BgABAAMAAADy//z/BgABAAQAAADy//3/BgABAAUAAADz//j/BgACAAAAAADz//n/BgACAAEAAADz//r/BgACAAIAAADz//v/BgACAAMAAADz//z/BgACAAQAAADz//3/BgACAAUAAADz//7/BgACAAYAAAD0//j/BgADAAAAAAD0//n/BgADAAEAAAD0//r/BgADAAIAAAD0//v/BgADAAMAAAD0//z/BgADAAQAAAD1//n/BgAEAAEAAAD1//r/BgAEAAIAAAD1//v/BgAEAAMAAAD1//z/BgAEAAQAAAD1//7/AQAAABwAAAD1////AQAAAB0AAAD2//7/AQABABwAAAD2////AQABAB0AAAD3//7/AQACABwAAAD3////AQACAB0AAAD4////AQADAB0AAADv//7/AQAAABwAAADv////AQAAAB0AAADw//7/AQABABwAAADw////AQABAB0AAADx//7/AQACABwAAADx////AQACAB0AAADy////AQADAB0AAAD5//v/CAAWAAAAAAD5//z/CAAWAAEAAAD5//3/CAAWAAIAAAD5//7/CAAWAAMAAAD6//v/CAAXAAAAAAD6//z/CAAXAAEAAAD6//3/CAAXAAIAAAD6//7/CAAXAAMAAAD5////CAAUAAMAAAD6////CAAVAAMAAAD3//n/CAAaAAIAAAD3//r/CAAaAAMAAAD4//n/CAAbAAIAAAD4//r/CAAbAAMAAAA=") tile_set = SubResource("TileSet_btr28") +metadata/_edit_lock_ = true [node name="BackgroundDecor" type="TileMapLayer" parent="Scene" unique_id=1466162064] position = Vector2(-49, 59) tile_map_data = PackedByteArray("AAD9//7/AQAAAAQAAAD9////AQAAAAUAAAD+//3/AQABAAMAAAD+//7/AQABAAQAAAD+////AQABAAUAAAD///3/AQACAAMAAAD///7/AQACAAQAAAD/////AQACAAUAAAAAAP3/AQADAAMAAAAAAP7/AQADAAQAAAAAAP//AQADAAUAAAABAP3/AQAEAAMAAAABAP7/AQAEAAQAAAABAP//AQAEAAUAAAACAP7/AQAFAAQAAAACAP//AQAFAAUAAAAEAP7/AQAGAAQAAAAEAP//AQAGAAUAAAAFAP3/AQAHAAMAAAAFAP7/AQAHAAQAAAAFAP//AQAHAAUAAAAGAP3/AQAIAAMAAAAGAP7/AQAIAAQAAAAGAP//AQAIAAUAAAAHAP3/AQAJAAMAAAAHAP7/AQAJAAQAAAAHAP//AQAJAAUAAAAIAP3/AQAKAAMAAAAIAP7/AQAKAAQAAAAIAP//AQAKAAUAAAAJAP7/AQALAAQAAAAJAP//AQALAAUAAADx////AQAKABUAAADy////AQALABUAAADz//7/AQAMABQAAADz////AQAMABUAAAD0//7/AQANABQAAAD0////AQANABUAAAD1//7/AQAOABQAAAD1////AQAOABUAAAD2////AQAPABUAAAARAP//AQAKABUAAAASAP//AQALABUAAAATAP7/AQAMABQAAAATAP//AQAMABUAAAAUAP7/AQANABQAAAAUAP//AQANABUAAAAVAP7/AQAOABQAAAAVAP//AQAOABUAAAAWAP//AQAPABUAAAA=") tile_set = SubResource("TileSet_btr28") +metadata/_edit_lock_ = true + +[node name="Cat" parent="Scene" unique_id=1357587844 groups=["animal_friends"] instance=ExtResource("17_k8jaa")] +position = Vector2(127, 35) +animal_type = "Cat" +flip_horizontal = true +show_shavings = true [node name="LeafParticles" type="GPUParticles2D" parent="Scene" unique_id=420003071] material = SubResource("CanvasItemMaterial_w330p") @@ -2034,11 +2071,41 @@ script = ExtResource("16_5kdtj") position = Vector2(-49, 59) tile_map_data = PackedByteArray("AAD9/wAAAAABAAwAAAD+/wAAAAACAAwAAAD//wAAAAADAAwAAAAAAAAAAAABAAwAAAABAAAAAAACAAwAAAACAAAAAAADAAwAAAADAAAAAAABAAwAAAAEAAAAAAACAAwAAAAFAAAAAAADAAwAAAAGAAAAAAABAAwAAAAHAAAAAAACAAwAAAAIAAAAAAADAAwAAAAJAAAAAAABAAwAAAAKAAAAAAACAAwAAAAKAAEAAAACAA0AAAAKAAIAAAACAA4AAAAKAAMAAAADAA4AAAAKAAQAAAADAA8AAAAHAAEAAAACAA0AAAAHAAIAAAACAA4AAAAHAAMAAAACAA4AAAAIAAEAAAADAA0AAAAIAAIAAAADAA4AAAAIAAMAAAADAA4AAAAJAAEAAAABAA0AAAAJAAIAAAABAA4AAAAJAAMAAAACAA4AAAAEAAEAAAACAA0AAAAEAAIAAAACAA4AAAAEAAMAAAADAA4AAAAFAAEAAAADAA0AAAAFAAIAAAADAA4AAAAFAAMAAAACAA4AAAAGAAEAAAABAA0AAAAGAAIAAAABAA4AAAAGAAMAAAADAA4AAAABAAEAAAACAA0AAAABAAIAAAACAA4AAAABAAMAAAACAA4AAAACAAEAAAADAA0AAAACAAIAAAADAA4AAAACAAMAAAADAA4AAAADAAEAAAABAA0AAAADAAIAAAABAA4AAAADAAMAAAACAA4AAAD+/wEAAAACAA0AAAD+/wIAAAACAA4AAAD+/wMAAAACAA8AAAD//wEAAAADAA0AAAD//wIAAAADAA4AAAD//wMAAAADAA8AAAAAAAEAAAABAA0AAAAAAAIAAAABAA4AAAAAAAMAAAABAA8AAAD9/wEAAAABAA0AAAD9/wIAAAABAA4AAAD9/wMAAAABAA8AAAAHAAQAAAACAA8AAAAIAAQAAAADAA8AAAAJAAQAAAACAA8AAAAEAAQAAAADAA8AAAAFAAQAAAACAA8AAAAGAAQAAAADAA8AAAABAAQAAAACAA8AAAACAAQAAAADAA8AAAADAAQAAAACAA8AAAD+/wQAAAACABAAAAD//wQAAAADABAAAAAAAAQAAAABABAAAAD9/wQAAAABABAAAADw/wAAAAAAAAwAAADw/wEAAAACAA4AAADw/wIAAAACAA8AAADw/wMAAAACAA4AAADw/wQAAAACAA8AAADx/wAAAAABAAwAAADx/wEAAAADAA4AAADx/wIAAAADAA8AAADx/wMAAAADAA4AAADx/wQAAAADAA8AAADy/wAAAAACAAwAAADy/wEAAAACAA0AAADy/wIAAAACAA4AAADy/wMAAAACAA8AAADy/wQAAAACABAAAADz/wAAAAADAAwAAADz/wEAAAADAA0AAADz/wIAAAADAA4AAADz/wMAAAADAA8AAADz/wQAAAADABAAAAD0/wAAAAABAAwAAAD0/wEAAAABAA0AAAD0/wIAAAABAA4AAAD0/wMAAAABAA8AAAD0/wQAAAABABAAAAD1/wAAAAACAAwAAAD1/wEAAAACAA0AAAD1/wIAAAACAA4AAAD1/wMAAAACAA8AAAD1/wQAAAACABAAAAD2/wAAAAADAAwAAAD2/wEAAAADAA0AAAD2/wIAAAADAA4AAAD2/wMAAAADAA8AAAD2/wQAAAADABAAAAD3/wAAAAABAAwAAAD3/wEAAAABAA0AAAD3/wIAAAABAA4AAAD3/wMAAAABAA8AAAD3/wQAAAABABAAAAD4/wAAAAACAAwAAAD4/wEAAAACAA0AAAD4/wIAAAACAA4AAAD4/wMAAAACAA8AAAD4/wQAAAACABAAAAD5/wAAAAADAAwAAAD5/wEAAAADAA0AAAD5/wIAAAADAA4AAAD5/wMAAAADAA8AAAD5/wQAAAADABAAAAD6/wAAAAABAAwAAAD6/wEAAAABAA0AAAD6/wIAAAABAA4AAAD6/wMAAAABAA8AAAD6/wQAAAABABAAAAD7/wAAAAACAAwAAAD7/wEAAAACAA0AAAD7/wIAAAACAA4AAAD7/wMAAAACAA8AAAD7/wQAAAACABAAAAD8/wAAAAADAAwAAAD8/wEAAAADAA0AAAD8/wIAAAADAA4AAAD8/wMAAAADAA8AAAD8/wQAAAADABAAAAALAAAAAAADAAwAAAALAAEAAAADAA0AAAALAAIAAAADAA4AAAALAAMAAAADAA8AAAALAAQAAAADABAAAAAMAAAAAAABAAwAAAAMAAEAAAABAA0AAAAMAAIAAAABAA4AAAAMAAMAAAABAA8AAAAMAAQAAAABABAAAAANAAAAAAACAAwAAAANAAEAAAACAA0AAAANAAIAAAACAA4AAAANAAMAAAACAA8AAAANAAQAAAACABAAAAAOAAAAAAADAAwAAAAOAAEAAAADAA0AAAAOAAIAAAADAA4AAAAOAAMAAAADAA8AAAAOAAQAAAADABAAAAAPAAAAAAABAAwAAAAPAAEAAAABAA0AAAAPAAIAAAABAA4AAAAPAAMAAAABAA8AAAAPAAQAAAABABAAAAAQAAAAAAACAAwAAAAQAAEAAAACAA0AAAAQAAIAAAACAA4AAAAQAAMAAAACAA8AAAAQAAQAAAACABAAAAARAAAAAAABAAwAAAARAAEAAAABAA0AAAARAAIAAAABAA4AAAARAAMAAAABAA8AAAARAAQAAAABABAAAAASAAAAAAACAAwAAAASAAEAAAACAA0AAAASAAIAAAACAA4AAAASAAMAAAACAA8AAAASAAQAAAACABAAAAATAAAAAAADAAwAAAATAAEAAAADAA0AAAATAAIAAAADAA4AAAATAAMAAAADAA8AAAATAAQAAAADABAAAAAUAAAAAAABAAwAAAAUAAEAAAABAA0AAAAUAAIAAAABAA4AAAAUAAMAAAABAA8AAAAUAAQAAAABABAAAAAVAAAAAAACAAwAAAAVAAEAAAACAA0AAAAVAAIAAAACAA4AAAAVAAMAAAACAA8AAAAVAAQAAAACABAAAAAWAAAAAAADAAwAAAAWAAEAAAADAA0AAAAWAAIAAAADAA4AAAAWAAMAAAADAA8AAAAWAAQAAAADABAAAADx/wUAAAADAA4AAADx/wYAAAADAA8AAADy/wUAAAADAA4AAADy/wYAAAADAA8AAADz/wUAAAACAA4AAADz/wYAAAACAA8AAAD0/wUAAAADAA4AAAD0/wYAAAADAA8AAAD1/wUAAAACAA4AAAD1/wYAAAACAA8AAAD2/wUAAAADAA4AAAD2/wYAAAADAA8AAAD3/wUAAAACAA4AAAD3/wYAAAACAA8AAAD4/wUAAAADAA4AAAD4/wYAAAADAA8AAAD5/wUAAAACAA4AAAD5/wYAAAACAA8AAAD6/wUAAAADAA4AAAD6/wYAAAADAA8AAAD7/wUAAAACAA4AAAD7/wYAAAACAA8AAAD8/wUAAAADAA4AAAD8/wYAAAADAA8AAAD9/wUAAAACAA4AAAD9/wYAAAACAA8AAAD+/wUAAAADAA4AAAD+/wYAAAADAA8AAAD//wUAAAACAA4AAAD//wYAAAACAA8AAAAAAAUAAAADAA4AAAAAAAYAAAADAA8AAAABAAUAAAACAA4AAAABAAYAAAACAA8AAAACAAUAAAADAA4AAAACAAYAAAADAA8AAAADAAUAAAACAA4AAAADAAYAAAACAA8AAAAEAAUAAAADAA4AAAAEAAYAAAADAA8AAAAFAAUAAAACAA4AAAAFAAYAAAACAA8AAAAGAAUAAAADAA4AAAAGAAYAAAADAA8AAAAHAAUAAAACAA4AAAAHAAYAAAACAA8AAAAIAAUAAAADAA4AAAAIAAYAAAADAA8AAAAJAAUAAAACAA4AAAAJAAYAAAACAA8AAAAKAAUAAAADAA4AAAAKAAYAAAADAA8AAAALAAUAAAACAA4AAAALAAYAAAACAA8AAAAMAAUAAAADAA4AAAAMAAYAAAADAA8AAAANAAUAAAACAA4AAAANAAYAAAACAA8AAAAOAAUAAAADAA4AAAAOAAYAAAADAA8AAAAPAAUAAAACAA4AAAAPAAYAAAACAA8AAAAQAAUAAAADAA4AAAAQAAYAAAADAA8AAAARAAUAAAACAA4AAAARAAYAAAACAA8AAAASAAUAAAADAA4AAAASAAYAAAADAA8AAAATAAUAAAACAA4AAAATAAYAAAACAA8AAAAUAAUAAAADAA4AAAAUAAYAAAADAA8AAAAVAAUAAAACAA4AAAAVAAYAAAACAA8AAAAWAAUAAAADAA4AAAAWAAYAAAADAA8AAADw/wUAAAACAA4AAADw/wYAAAACAA8AAAAKAP7/AQAOAAAAAAAKAP//AQAOAAEAAAALAP7/AQAPAAAAAAALAP//AQAPAAEAAAA=") tile_set = SubResource("TileSet_btr28") +metadata/_edit_lock_ = true [node name="InteractionLayer" type="Node2D" parent="Scene" unique_id=955061762] +[node name="Goose" parent="Scene/InteractionLayer" unique_id=495867136 groups=["animal_friends"] instance=ExtResource("17_k8jaa")] +position = Vector2(-187, 19) +animal_type = "Goose" +flip_horizontal = true +show_shavings = true + +[node name="Fox" parent="Scene/InteractionLayer" unique_id=1333255729 groups=["animal_friends"] instance=ExtResource("17_k8jaa")] +position = Vector2(-52, 51) +animal_type = "Fox" +show_shavings = true + +[node name="Frog" parent="Scene/InteractionLayer" unique_id=1883056059 groups=["animal_friends"] instance=ExtResource("17_k8jaa")] +position = Vector2(-127, 51) +animal_type = "Frog" +show_shavings = true + +[node name="Wolf" parent="Scene/InteractionLayer" unique_id=526034363 groups=["animal_friends"] instance=ExtResource("17_k8jaa")] +position = Vector2(-149, -29) +animal_type = "Wolf" +flip_horizontal = true +show_shavings = true + +[node name="Dog" parent="Scene/InteractionLayer" unique_id=165373951 instance=ExtResource("17_k8jaa")] +position = Vector2(28, 59) +animal_type = "Dog" +show_shavings = false +metadata/_edit_lock_ = true + [node name="Character" parent="Scene/InteractionLayer" unique_id=916171989 instance=ExtResource("17_1hpkv")] -position = Vector2(-62, 29) +position = Vector2(-70, 29) +metadata/_edit_lock_ = true [node name="StockPile" parent="Scene/InteractionLayer" unique_id=1961584119 instance=ExtResource("17_deeqb")] position = Vector2(-215, 51) @@ -2051,17 +2118,21 @@ position = Vector2(164, 48) position = Vector2(-49, 59) tile_map_data = PackedByteArray("AAACAP7/AQACAAYAAAACAP//AQACAAcAAAADAP7/AQADAAYAAAADAP//AQADAAcAAAAHAP//AQAYAAcAAAAIAP//AQAZAAcAAADz////AQAIAAkAAAD0////AQAJAAkAAAD3////AQAKAAkAAAD4////AQALAAkAAADw////AQAWAAcAAADx////AQAXAAcAAAD8////AQAGAA0AAAD9////AQAHAA0AAAD5////AQAGAA8AAAD6////AQAHAA8AAAAOAP//AQAIAA8AAAAPAP7/AQAJAA4AAAAPAP//AQAJAA8AAAAQAP7/AQAKAA4AAAAQAP//AQAKAA8AAAARAP//AQALAA8AAAATAP//AQAGAA0AAAAUAP//AQAHAA0AAAAJAP//AQAGAA0AAAAKAP//AQAHAA0AAAALAP//AQAKAAkAAAAMAP//AQALAAkAAAA=") tile_set = SubResource("TileSet_btr28") +metadata/_edit_lock_ = true [node name="Camera2D" type="Camera2D" parent="." unique_id=1719721614] zoom = Vector2(2, 2) +metadata/_edit_lock_ = true [node name="UI" type="Control" parent="." unique_id=1827364964] +light_mask = 2 layout_mode = 3 anchors_preset = 0 offset_right = 40.0 offset_bottom = 40.0 theme = ExtResource("22_q7h7c") script = ExtResource("17_q7h7c") +metadata/_edit_lock_ = true [node name="Panel" type="Panel" parent="UI" unique_id=35561461] visible = false @@ -2080,31 +2151,77 @@ offset_right = 45.0 offset_bottom = -5.0 theme_override_font_sizes/font_size = 12 +[node name="CurrencyLabel" type="Label" parent="UI" unique_id=508512755] +unique_name_in_owner = true +layout_mode = 0 +offset_left = -41.0 +offset_top = -137.0 +offset_right = 43.0 +offset_bottom = -120.375 +theme = ExtResource("22_q7h7c") +theme_override_fonts/font = SubResource("FontVariation_hcndq") +theme_override_font_sizes/font_size = 21 +text = "Currency" +horizontal_alignment = 1 + +[node name="CurrenciesBG" type="Sprite2D" parent="UI" unique_id=400039529] +position = Vector2(-246.24998, -132) +scale = Vector2(0.7450001, 0.44000018) +texture = ExtResource("22_hcndq") + [node name="Currencies" type="VBoxContainer" parent="UI" unique_id=1952407607] layout_mode = 0 -offset_left = -284.0 -offset_top = -159.0 -offset_right = -239.0 -offset_bottom = -103.0 +offset_left = -278.0 +offset_top = -151.0 +offset_right = -182.0 +offset_bottom = -114.0 theme_override_constants/separation = 5 alignment = 1 -[node name="CurrencyLabel" type="Label" parent="UI/Currencies" unique_id=508512755] -unique_name_in_owner = true +[node name="HBoxContainer" type="HBoxContainer" parent="UI/Currencies" unique_id=1028765341] layout_mode = 2 -theme = ExtResource("22_q7h7c") -text = "Currency" -[node name="WoodLabel" type="Label" parent="UI/Currencies" unique_id=173301448] +[node name="RichTextLabel2" type="RichTextLabel" parent="UI/Currencies/HBoxContainer" unique_id=622882040] +clip_contents = false +custom_minimum_size = Vector2(10, 14) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +theme_override_fonts/normal_font = ExtResource("24_chdjo") +theme_override_font_sizes/normal_font_size = 9 +text = "🪵" +scroll_active = false +autowrap_mode = 0 +autowrap_trim_flags = 0 + +[node name="WoodLabel" type="Label" parent="UI/Currencies/HBoxContainer" unique_id=173301448] unique_name_in_owner = true layout_mode = 2 theme = ExtResource("22_q7h7c") +theme_override_font_sizes/font_size = 18 text = "0" -[node name="StockLabel" type="Label" parent="UI/Currencies" unique_id=1137115173] +[node name="HBoxContainer2" type="HBoxContainer" parent="UI/Currencies" unique_id=231882591] +layout_mode = 2 + +[node name="RichTextLabel" type="RichTextLabel" parent="UI/Currencies/HBoxContainer2" unique_id=248143892] +clip_contents = false +custom_minimum_size = Vector2(10, 14) +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +theme_override_fonts/normal_font = ExtResource("24_chdjo") +theme_override_font_sizes/normal_font_size = 10 +text = "📦" +scroll_active = false +autowrap_mode = 0 +autowrap_trim_flags = 0 + +[node name="StockLabel" type="Label" parent="UI/Currencies/HBoxContainer2" unique_id=1137115173] unique_name_in_owner = true layout_mode = 2 theme = ExtResource("22_q7h7c") +theme_override_font_sizes/font_size = 18 text = "0" [node name="UnlockContainer" type="GridContainer" parent="UI" unique_id=1701514762] @@ -2112,9 +2229,9 @@ layout_mode = 1 anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 -offset_left = -282.0 -offset_top = 79.0 -offset_right = 242.0 +offset_left = -257.0 +offset_top = 87.0 +offset_right = 217.0 offset_bottom = 115.0 grow_horizontal = 2 grow_vertical = 2 @@ -2128,10 +2245,11 @@ size_flags_vertical = 0 [node name="Timer" type="Label" parent="UI" unique_id=405529286] unique_name_in_owner = true layout_mode = 0 -offset_left = -20.0 +offset_left = -29.0 offset_top = -157.0 -offset_right = 20.0 -offset_bottom = -144.0 +offset_right = 29.0 +offset_bottom = -138.79167 +theme_override_font_sizes/font_size = 23 text = "00:00:00" [node name="GameCompleted" type="Panel" parent="UI" unique_id=1824061781] @@ -2156,6 +2274,7 @@ text = "YOU WIN!" horizontal_alignment = 1 [node name="CompletionTimeLabel" type="Label" parent="UI/GameCompleted" unique_id=1845505160] +unique_name_in_owner = true layout_mode = 0 offset_left = 221.0 offset_top = 211.0 @@ -2183,6 +2302,7 @@ Completion Time" horizontal_alignment = 1 [node name="TextEdit" type="TextEdit" parent="UI/GameCompleted" unique_id=637671853] +unique_name_in_owner = true layout_mode = 0 offset_left = 35.0 offset_top = 277.0 @@ -2192,6 +2312,7 @@ theme = ExtResource("22_q7h7c") placeholder_text = "Your Name (Optional)" [node name="SubmitScoreButton" type="TextureButton" parent="UI/GameCompleted" unique_id=111534642] +unique_name_in_owner = true custom_minimum_size = Vector2(100, 25) layout_mode = 0 offset_left = 171.0 @@ -2221,6 +2342,17 @@ theme = ExtResource("22_q7h7c") text = "Submit Score" horizontal_alignment = 1 +[node name="SubmissionStatusLabel" type="Label" parent="UI/GameCompleted" unique_id=976543210] +unique_name_in_owner = true +layout_mode = 0 +offset_left = 281.0 +offset_top = 277.0 +offset_right = 450.0 +offset_bottom = 304.0 +theme_override_font_sizes/font_size = 12 +horizontal_alignment = 1 +vertical_alignment = 1 + [node name="ContinueButton" type="TextureButton" parent="UI/GameCompleted" unique_id=1202982034] unique_name_in_owner = true custom_minimum_size = Vector2(100, 25) @@ -2252,4 +2384,33 @@ theme = ExtResource("22_q7h7c") text = "Continue Whittling" horizontal_alignment = 1 +[node name="ConfigButton" type="Button" parent="UI" unique_id=2147483647] +unique_name_in_owner = true +layout_mode = 0 +offset_left = 255.0 +offset_top = 134.0 +offset_right = 275.0 +offset_bottom = 153.0 +theme = ExtResource("22_q7h7c") +theme_override_colors/font_color = Color(0.3764706, 0.31764707, 0.16078432, 1) +theme_override_colors/font_pressed_color = Color(0.7882353, 0.36078432, 0.19215687, 1) +theme_override_colors/font_hover_color = Color(0.49803922, 0.4627451, 0.2901961, 1) +theme_override_fonts/font = ExtResource("24_chdjo") +theme_override_font_sizes/font_size = 13 +theme_override_styles/normal = SubResource("StyleBoxEmpty_mjadu") +theme_override_styles/pressed = SubResource("StyleBoxEmpty_k8jaa") +theme_override_styles/hover = SubResource("StyleBoxEmpty_hp34j") +theme_override_styles/disabled = SubResource("StyleBoxEmpty_lxcs1") +theme_override_styles/focus = SubResource("StyleBoxEmpty_hxr5a") +text = "🎵" + +[node name="ConfigPanel" parent="UI" unique_id=281194599 instance=ExtResource("20_config")] +visible = false +layout_mode = 1 +offset_left = -129.0 +offset_top = -132.0 +offset_right = 90.0 +offset_bottom = 81.0 +theme_override_styles/panel = SubResource("StyleBoxFlat_mjadu") + [connection signal="pressed" from="UI/GameCompleted/ContinueButton" to="UI" method="_on_continue_button_pressed"] diff --git a/scenes/scripts/animal.gd b/scenes/scripts/animal.gd new file mode 100644 index 0000000..e739839 --- /dev/null +++ b/scenes/scripts/animal.gd @@ -0,0 +1,79 @@ +@tool +extends Node2D + +@export var animal_type : String: + set(value): + animal_type = value + if is_node_ready(): + _update_sprite() + +@export var flip_horizontal : bool = false: + set(value): + flip_horizontal = value + if is_node_ready(): + _update_sprite() + +@export var show_shavings : bool = false: + set(value): + show_shavings = value + if is_node_ready(): + _update_shavings() + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + _update_sprite() + _update_shavings() + + +func _update_sprite() -> void: + + # Hide all animal sprites first + %Fox.visible = false + %Porcupine.visible = false + %Wolf.visible = false + %Cat.visible = false + %Goose.visible = false + %Frog.visible = false + %Chick.visible = false + %Dog.visible = false + + # Show the appropriate animal based on animal_type + var active_sprite: AnimatedSprite2D + match animal_type: + "Fox": + %Fox.visible = true + active_sprite = %Fox + "Porcupine": + %Porcupine.visible = true + active_sprite = %Porcupine + "Wolf": + %Wolf.visible = true + active_sprite = %Wolf + "Cat": + %Cat.visible = true + active_sprite = %Cat + "Goose": + %Goose.visible = true + active_sprite = %Goose + "Frog": + %Frog.visible = true + active_sprite = %Frog + "Chick": + %Chick.visible = true + active_sprite = %Chick + "Dog": + %Dog.visible = true + active_sprite = %Dog + + # Apply horizontal flip if enabled + if active_sprite: + active_sprite.flip_h = flip_horizontal + + +func _update_shavings() -> void: + %Shavings.visible = show_shavings + + +# Called every frame. 'delta' is the elapsed time since the previous frame. +func _process(delta: float) -> void: + pass diff --git a/scenes/scripts/animal.gd.uid b/scenes/scripts/animal.gd.uid new file mode 100644 index 0000000..20884b1 --- /dev/null +++ b/scenes/scripts/animal.gd.uid @@ -0,0 +1 @@ +uid://uhlsvqaaemre diff --git a/scenes/scripts/button.gd b/scenes/scripts/button.gd index 13baf3f..30da171 100644 --- a/scenes/scripts/button.gd +++ b/scenes/scripts/button.gd @@ -1,58 +1,136 @@ extends TextureButton -@onready var label: Label = $CenterContainer/Label # Adjust path to your Label node +@onready var name_label: Label = $CenterContainer/VBoxContainer/NameLabel +@onready var price_label: Label = $CenterContainer/VBoxContainer/PriceLabel -var unlock_id = "" # Store the unlock ID +var unlock_id = -1 # Store the unlock ID +var unlock_description_text = "" # Store the description for custom tooltip func _ready(): - label.visible = false # Hide label initially + name_label.visible = false # Hide label initially + price_label.visible = false adjust_label_font_size() # Connect the pressed signal pressed.connect(_on_button_pressed) + # Connect to currency changes to update button state + Inventory.currency_changed.connect(_on_currency_changed) + # Connect to unlock events to update button state when items are unlocked + Unlocks.item_unlocked.connect(_on_item_unlocked) func setup(unlock_data): # Log.pr("Setting up button for unlock:", unlock_data.unlock_name) unlock_id = unlock_data.unlock_id # Store the ID - if label: - label.visible = false - label.text = unlock_data.unlock_name + " " + str(unlock_data.get_next_rank()) - label.text = label.text + " - " + Global.currency_symbol + str(unlock_data.get_next_cost()) - label.text = label.text + "\n" + unlock_data.get_next_modifiers_string() - #self.disabled = unlock_data.is_unlocked + if name_label and price_label: + name_label.visible = false + price_label.visible = false + update_button_state(unlock_data) adjust_label_font_size() else: - Log.pr("Warning: Label node not found in button.") + Log.pr("Warning: Label nodes not found in button.") + +func update_button_state(unlock_data): + # Store unlock description for custom tooltip + if unlock_data.unlock_description: + unlock_description_text = unlock_data.unlock_description + tooltip_text = unlock_data.unlock_description + + # Check if at max rank + if not unlock_data.can_rank_up(): + self.disabled = true + name_label.text = unlock_data.unlock_name + " (MAX)" + price_label.text = "" + return + + # Build name text - only show rank if it's a scaling unlock + if unlock_data.is_scaling: + name_label.text = unlock_data.unlock_name + " " + str(unlock_data.get_next_rank()) + else: + name_label.text = unlock_data.unlock_name + + # Build price text + price_label.text = Global.currency_symbol + Global.format_number(unlock_data.get_next_cost()) + + # Check if player has enough currency + var cost = unlock_data.get_next_cost() + var current_currency = Inventory.get_currency() + self.disabled = current_currency < cost + +func _on_currency_changed(new_amount: float): + # Update button state when currency changes + if unlock_id >= 0: + var unlock_data = Unlocks.get_unlock_by_id(unlock_id) + if unlock_data: + update_button_state(unlock_data) + +func _on_item_unlocked(): + # Update button state when any item is unlocked (in case this button reached max rank) + if unlock_id >= 0: + var unlock_data = Unlocks.get_unlock_by_id(unlock_id) + if unlock_data: + update_button_state(unlock_data) + adjust_label_font_size() func _on_button_pressed(): # Log.pr("Button pressed, unlocking item:", unlock_id) Unlocks.unlock_item(unlock_id) func adjust_label_font_size(): - if not label: + if not name_label or not price_label: return var available_width = size.x - 10 var available_height = size.y - 10 - # Start with a reasonable font size - var font_size = 32 + + # Calculate font sizes for both labels + # Name label gets 60% of the height, price label gets 40% + var name_height = available_height * 0.6 + var price_height = available_height * 0.4 + + # Start with reasonable font sizes + var name_font_size = 32 + var price_font_size = 24 var min_font_size = 8 - # Get or create a font - var font = label.get_theme_font("font") - # Binary search for the optimal font size - while font_size > min_font_size: - label.add_theme_font_size_override("font_size", font_size) - # Force update and get the actual text size - await get_tree().process_frame - var text_size = font.get_string_size(label.text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size) - # Check if it fits - if text_size.x <= available_width and text_size.y <= available_height: + + # Get fonts + var name_font = name_label.get_theme_font("font") + var price_font = price_label.get_theme_font("font") + + # Calculate name label font size without applying it + while name_font_size > min_font_size: + var text_size = name_font.get_string_size(name_label.text, HORIZONTAL_ALIGNMENT_LEFT, -1, name_font_size) + if text_size.x <= available_width and text_size.y <= name_height: break - # Reduce font size and try again - font_size -= 1 - label.add_theme_font_size_override("font_size", font_size) - label.visible = true # Show label after resizing is complete + name_font_size -= 1 + + # Calculate price label font size without applying it + while price_font_size > min_font_size: + var text_size = price_font.get_string_size(price_label.text, HORIZONTAL_ALIGNMENT_LEFT, -1, price_font_size) + if text_size.x <= available_width and text_size.y <= price_height: + break + price_font_size -= 1 + + # Apply both font sizes at once + name_label.add_theme_font_size_override("font_size", name_font_size) + price_label.add_theme_font_size_override("font_size", price_font_size) + + name_label.visible = true + price_label.visible = true # Call this function whenever you change the label text func set_label_text(new_text: String): - if label: - label.visible = false # Hide while resizing - label.text = new_text + if name_label: + name_label.visible = false # Hide while resizing + price_label.visible = false + name_label.text = new_text + price_label.text = "" adjust_label_font_size() + +# Override to create custom tooltip with larger font +func _make_custom_tooltip(for_text: String) -> Object: + var tooltip_label = Label.new() + tooltip_label.text = for_text + tooltip_label.add_theme_font_size_override("font_size", 26) + + # Create a panel container for background + var panel = PanelContainer.new() + panel.add_child(tooltip_label) + + return panel diff --git a/scenes/scripts/config_panel.gd b/scenes/scripts/config_panel.gd new file mode 100644 index 0000000..80ee62e --- /dev/null +++ b/scenes/scripts/config_panel.gd @@ -0,0 +1,41 @@ +extends Panel + +@onready var music_toggle: CheckButton = %MusicToggle +@onready var chop_toggle: CheckButton = %ChopToggle +@onready var money_toggle: CheckButton = %MoneyToggle +@onready var close_button: Button = %CloseButton + +func _ready(): + # Initialize toggle states from Global settings + music_toggle.button_pressed = Global.play_background_music + chop_toggle.button_pressed = Global.play_chop_sound + money_toggle.button_pressed = Global.play_money_sound + + # Connect signals + music_toggle.toggled.connect(_on_music_toggled) + chop_toggle.toggled.connect(_on_chop_toggled) + money_toggle.toggled.connect(_on_money_toggled) + close_button.pressed.connect(_on_close_pressed) + + # Start hidden + visible = false + +func _on_music_toggled(enabled: bool): + Global.play_background_music = enabled + var audio_manager = get_node("/root/Audio") + if enabled: + audio_manager.play_background_music() + else: + audio_manager.stop_background_music() + +func _on_chop_toggled(enabled: bool): + Global.play_chop_sound = enabled + +func _on_money_toggled(enabled: bool): + Global.play_money_sound = enabled + +func _on_close_pressed(): + visible = false + +func toggle_visibility(): + visible = !visible diff --git a/scenes/scripts/config_panel.gd.uid b/scenes/scripts/config_panel.gd.uid new file mode 100644 index 0000000..a8d36ed --- /dev/null +++ b/scenes/scripts/config_panel.gd.uid @@ -0,0 +1 @@ +uid://2jm25u1ehlac diff --git a/scenes/scripts/ui_control.gd b/scenes/scripts/ui_control.gd index f94d094..c163777 100644 --- a/scenes/scripts/ui_control.gd +++ b/scenes/scripts/ui_control.gd @@ -8,10 +8,19 @@ extends Control @onready var timer_label : Label = %Timer @onready var game_complete_screen : Panel = %GameCompleted +@onready var completion_time_label : Label = %CompletionTimeLabel @onready var continue_button : TextureButton = %ContinueButton +@onready var config_button : Button = %ConfigButton +@onready var config_panel : Panel = %ConfigPanel +@onready var player_name_input : TextEdit = %TextEdit +@onready var submit_score_button : TextureButton = %SubmitScoreButton +@onready var submission_status_label : Label = %SubmissionStatusLabel var game_timer : Timer var elapsed_time := 0.0 +var current_nonce : String = "" +var bridge = null +var score_submitted := false func _ready(): populate_modifiers_display() @@ -34,6 +43,7 @@ func _ready(): Inventory.currency_changed.connect(_on_currency_changed) Inventory.currency_added.connect(spawn_currency_increase) + Inventory.currency_added.connect(_on_currency_added) Inventory.wood_changed.connect(_on_currency_changed) Inventory.wood_added.connect(spawn_wood_increase) Inventory.stock_added.connect(spawn_stock_increase) @@ -41,17 +51,27 @@ func _ready(): Unlocks.item_unlocked.connect(populate_unlock_buttons) GameManager.currency_goal_met.connect(_on_currency_goal_met) + if config_button: + config_button.pressed.connect(_on_config_button_pressed) + + if submit_score_button: + submit_score_button.pressed.connect(_on_submit_score_button_pressed) + + # Initialize JavaScript bridge for web builds + if OS.has_feature("web"): + bridge = JavaScriptBridge.get_interface("godotBridge") + # get_tree().paused = true func update_currency_label(): - currency_label.text = Global.currency_symbol + " " + str(int(Inventory.get_currency())) + currency_label.text = Global.currency_symbol + Global.format_number(Inventory.get_currency()) func update_wood_label(): - wood_label.text = "W: " + str(int(Inventory.get_wood())) + wood_label.text = Global.format_number(Inventory.get_wood()) func update_stock_label(): - stock_label.text = "S: " + str(int(Inventory.get_stock())) + stock_label.text = Global.format_number(Inventory.get_stock()) func spawn_currency_increase(value, _total): spawn_inventory_change_value(value, _total, "+", Global.currency_symbol, Global.money_color) @@ -64,7 +84,7 @@ func spawn_stock_increase(value, _total): func spawn_inventory_change_value(value, _total, display_sign: String = "+", symbol: String = "", label_color: Color = Color.WHITE): var float_label = Label.new() - float_label.text = display_sign + symbol + str(int(abs(value))) + float_label.text = display_sign + symbol + Global.format_number(abs(value)) float_label.add_theme_font_size_override("font_size", 16) float_label.modulate = label_color @@ -99,10 +119,10 @@ func populate_unlock_buttons(): func populate_modifiers_display(): var modifiers_text = "" - modifiers_text = modifiers_text + "Sale Price: " + Global.currency_symbol + str(Unlocks.get_sale_price_per_item()) + "\n" - modifiers_text = modifiers_text + "Items Produced Per Tick: " + str(Unlocks.get_items_produced_per_tick()) + "\n" - modifiers_text = modifiers_text + "Wood per Click: " + str(Unlocks.get_wood_per_click()) + "\n\n" - modifiers_text = modifiers_text + "Demand: " + str(int(Unlocks.get_sale_demand())) + "\n\n" + modifiers_text = modifiers_text + "Sale Price: " + Global.currency_symbol + Global.format_number(Unlocks.get_sale_price_per_item()) + "\n" + modifiers_text = modifiers_text + "Items Produced Per Tick: " + Global.format_number(Unlocks.get_items_produced_per_tick()) + "\n" + modifiers_text = modifiers_text + "Wood per Click: " + Global.format_number(Unlocks.get_wood_per_click()) + "\n\n" + modifiers_text = modifiers_text + "Demand: " + Global.format_number(Unlocks.get_sale_demand()) + "\n\n" modifiers_text = modifiers_text + "Current Modifiers:\n" for key in Unlocks.current_modifiers.keys(): @@ -129,7 +149,20 @@ func _on_timer_tick(): func _on_currency_goal_met(): Log.pr("Currency goal met!") get_tree().paused = true + completion_time_label.text = format_time(elapsed_time) game_complete_screen.visible = true + score_submitted = false + + # Request nonce from API when game is completed + if bridge != null: + # Disable submit button until nonce is received + submit_score_button.disabled = true + submission_status_label.visible = false + _request_nonce() + else: + # No web bridge available, hide the submit button entirely + submit_score_button.visible = false + submission_status_label.visible = false func format_time(seconds: float) -> String: var total_seconds := int(seconds) @@ -145,3 +178,228 @@ func _on_continue_button_pressed() -> void: Global.game_continue_pressed = true game_complete_screen.visible = false get_tree().paused = false + +func _on_config_button_pressed() -> void: + if config_panel: + config_panel.toggle_visibility() + +func _on_currency_added(_value, _total): + var audio_manager = get_node("/root/Audio") + if audio_manager: + audio_manager.play_money_sound() + +# HIGH SCORE SUBMISSION FUNCTIONS + +func _request_nonce(): + if bridge == null: + return + + # Call JavaScript bridge to request nonce + var result = bridge.requestNonce() + + # Set up polling to check if nonce was received + # (since JS callbacks are async) + var nonce_check_timer = Timer.new() + nonce_check_timer.wait_time = 0.5 + nonce_check_timer.one_shot = false + nonce_check_timer.process_mode = Node.PROCESS_MODE_ALWAYS # Run even when paused + add_child(nonce_check_timer) + + var attempts = 0 + var max_attempts = 20 # 10 seconds total + + nonce_check_timer.timeout.connect(func(): + attempts += 1 + var nonce = _get_nonce_from_js() + + if nonce != null and nonce != "" and nonce != "null": + current_nonce = nonce + # Enable submit button when nonce is ready + submit_score_button.disabled = false + nonce_check_timer.stop() + nonce_check_timer.queue_free() + elif attempts >= max_attempts: + # Failed to get nonce - show error and keep button disabled + submission_status_label.text = "Failed to connect to server" + submission_status_label.modulate = Color(1.0, 0.3, 0.3) # Red + submission_status_label.visible = true + nonce_check_timer.stop() + nonce_check_timer.queue_free() + ) + + nonce_check_timer.start() + +func _get_nonce_from_js() -> String: + if bridge == null: + return "" + + # Try to get the nonce that JavaScript stored + var result = JavaScriptBridge.eval(""" + (function() { + if (window.godotNonce) { + return window.godotNonce; + } + return ''; + })(); + """, true) + + return str(result) if result != null else "" + +func _on_submit_score_button_pressed(): + if score_submitted: + submission_status_label.text = "Score already submitted!" + submission_status_label.modulate = Color(1.0, 0.8, 0.3) # Yellow + submission_status_label.visible = true + return + + if bridge == null or current_nonce == "": + # This shouldn't happen as button should be disabled + return + + # Disable button during submission and show status + submit_score_button.disabled = true + submission_status_label.text = "Submitting..." + submission_status_label.modulate = Color(1.0, 1.0, 1.0) # White + submission_status_label.visible = true + + # Get player name from input and sanitize + var player_name = _sanitize_player_name(player_name_input.text) + if player_name == "": + player_name = "Anonymous" + + # Get completion time in seconds + var completion_time_seconds = int(elapsed_time) + + # Create and encode payload on GDScript side + var encoded_payload = _create_encoded_payload(current_nonce, player_name, completion_time_seconds) + + # Call JavaScript to submit score with pre-encoded payload and nonce + bridge.submitScore(encoded_payload, current_nonce) + + # Poll for submission result + var submit_check_timer = Timer.new() + submit_check_timer.wait_time = 0.5 + submit_check_timer.one_shot = false + submit_check_timer.process_mode = Node.PROCESS_MODE_ALWAYS # Run even when paused + add_child(submit_check_timer) + + var attempts = 0 + var max_attempts = 30 # 15 seconds total + + submit_check_timer.timeout.connect(func(): + attempts += 1 + var result = _get_submission_result_from_js() + + if result.has("completed") and result["completed"]: + submission_status_label.visible = true + if result["success"]: + submission_status_label.text = result.get("message", "Score submitted!") + submission_status_label.modulate = Color(0.5, 1.0, 0.5) # Green + score_submitted = true + + # Show rank if available + if result.has("rank") and result["rank"] > 0: + submission_status_label.text += " (Rank #%d)" % result["rank"] + else: + submission_status_label.text = result.get("message", "Failed to submit") + submission_status_label.modulate = Color(1.0, 0.3, 0.3) # Red + submit_score_button.disabled = false + + submit_check_timer.stop() + submit_check_timer.queue_free() + elif attempts >= max_attempts: + submission_status_label.text = "Submission timeout" + submission_status_label.modulate = Color(1.0, 0.3, 0.3) # Red + submission_status_label.visible = true + submit_score_button.disabled = false + submit_check_timer.stop() + submit_check_timer.queue_free() + ) + + submit_check_timer.start() + +func _get_submission_result_from_js() -> Dictionary: + if bridge == null: + return {} + + var result = JavaScriptBridge.eval(""" + (function() { + if (window.godotSubmissionResult) { + var result = window.godotSubmissionResult; + window.godotSubmissionResult = null; // Clear after reading + return JSON.stringify(result); + } + return '{}'; + })(); + """, true) + + if result != null and result != "": + var json = JSON.new() + var error = json.parse(str(result)) + if error == OK: + return json.data + + return {} + +func _create_encoded_payload(nonce: String, player_name: String, completion_time: int) -> String: + # Create the payload as JSON + var payload = { + "nonce": nonce, + "gameId": "whittling-clicker", # Unique identifier for this game + "playerName": player_name, + "completionTime": completion_time, + "timestamp": Time.get_unix_time_from_system() * 1000 # Convert to milliseconds + } + + var json_string = JSON.stringify(payload) + + # XOR encode with key derived from nonce + var key = "WHITTLING_KEY_" + nonce.substr(0, 8) + var encoded_bytes = _xor_encode(json_string, key) + + # Base64 encode (using raw bytes) + var base64_encoded = Marshalls.raw_to_base64(encoded_bytes) + + return base64_encoded + +func _sanitize_player_name(name: String) -> String: + # Strip leading/trailing whitespace + name = name.strip_edges() + + # Remove control characters and most special characters, keep letters, numbers, spaces, and safe punctuation + var safe_name = "" + for i in range(name.length()): + var c = name[i] + var code = name.unicode_at(i) + + # Allow: letters, numbers, spaces, hyphens, underscores, periods + # Block: control chars, path separators, quotes, angle brackets, etc + if (code >= 48 and code <= 57) or \ + (code >= 65 and code <= 90) or \ + (code >= 97 and code <= 122) or \ + c == " " or c == "-" or c == "_" or c == ".": + safe_name += c + + # Limit length to 50 characters + safe_name = safe_name.substr(0, 50) + + # Remove multiple consecutive spaces + while safe_name.find(" ") != -1: + safe_name = safe_name.replace(" ", " ") + + # Strip again after processing + safe_name = safe_name.strip_edges() + + return safe_name + +func _xor_encode(text: String, key: String) -> PackedByteArray: + var text_bytes = text.to_utf8_buffer() + var key_bytes = key.to_utf8_buffer() + var key_length = key_bytes.size() + var result = PackedByteArray() + + for i in range(text_bytes.size()): + var xor_byte = text_bytes[i] ^ key_bytes[i % key_length] + result.append(xor_byte) + + return result diff --git a/scenes/simulator.tscn b/scenes/simulator.tscn deleted file mode 100644 index d5b2274..0000000 --- a/scenes/simulator.tscn +++ /dev/null @@ -1,124 +0,0 @@ -[gd_scene format=3 uid="uid://br6hgvb4buyji"] - -[ext_resource type="Script" uid="uid://citjokiv6skqi" path="res://scripts/sim_direct.gd" id="1_sim"] - -[node name="Simulator" type="Control" unique_id=1833845714] -layout_mode = 3 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -script = ExtResource("1_sim") - -[node name="MarginContainer" type="MarginContainer" parent="." unique_id=689981813] -layout_mode = 1 -anchors_preset = 15 -anchor_right = 1.0 -anchor_bottom = 1.0 -grow_horizontal = 2 -grow_vertical = 2 -theme_override_constants/margin_left = 20 -theme_override_constants/margin_top = 20 -theme_override_constants/margin_right = 20 -theme_override_constants/margin_bottom = 20 - -[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer" unique_id=1425713346] -layout_mode = 2 -theme_override_constants/separation = 15 - -[node name="Title" type="Label" parent="MarginContainer/VBoxContainer" unique_id=1101365395] -layout_mode = 2 -theme_override_font_sizes/font_size = 32 -text = "Unlock Simulator" -horizontal_alignment = 1 - -[node name="StatusPanel" type="PanelContainer" parent="MarginContainer/VBoxContainer" unique_id=21717961] -layout_mode = 2 - -[node name="VBox" type="VBoxContainer" parent="MarginContainer/VBoxContainer/StatusPanel" unique_id=846585479] -layout_mode = 2 -theme_override_constants/separation = 8 - -[node name="StatusLabel" type="Label" parent="MarginContainer/VBoxContainer/StatusPanel/VBox" unique_id=234737032] -layout_mode = 2 -theme_override_font_sizes/font_size = 20 -text = "Status: Initializing..." - -[node name="ProgressLabel" type="Label" parent="MarginContainer/VBoxContainer/StatusPanel/VBox" unique_id=2064314389] -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "Progress: 0.0% (0/0)" - -[node name="ProgressBar" type="ProgressBar" parent="MarginContainer/VBoxContainer/StatusPanel/VBox" unique_id=1093115744] -custom_minimum_size = Vector2(0, 30) -layout_mode = 2 -max_value = 1.0 -step = 0.001 -show_percentage = false - -[node name="RateLabel" type="Label" parent="MarginContainer/VBoxContainer/StatusPanel/VBox" unique_id=2006247697] -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "Speed: 0.0 combos/sec" - -[node name="ETALabel" type="Label" parent="MarginContainer/VBoxContainer/StatusPanel/VBox" unique_id=300266005] -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "ETA: Calculating..." - -[node name="CachePanel" type="PanelContainer" parent="MarginContainer/VBoxContainer" unique_id=242908015] -layout_mode = 2 - -[node name="VBox" type="VBoxContainer" parent="MarginContainer/VBoxContainer/CachePanel" unique_id=1164261399] -layout_mode = 2 -theme_override_constants/separation = 8 - -[node name="CacheTitle" type="Label" parent="MarginContainer/VBoxContainer/CachePanel/VBox" unique_id=615421163] -layout_mode = 2 -theme_override_font_sizes/font_size = 20 -text = "Cache Statistics" - -[node name="CacheHitsLabel" type="Label" parent="MarginContainer/VBoxContainer/CachePanel/VBox" unique_id=1993375767] -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "Cache Hits: 0" - -[node name="CacheMissesLabel" type="Label" parent="MarginContainer/VBoxContainer/CachePanel/VBox" unique_id=159876992] -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "Cache Misses: 0" - -[node name="CacheRateLabel" type="Label" parent="MarginContainer/VBoxContainer/CachePanel/VBox" unique_id=51938306] -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "Hit Rate: 0.0%" - -[node name="CacheSizeLabel" type="Label" parent="MarginContainer/VBoxContainer/CachePanel/VBox" unique_id=2001183562] -layout_mode = 2 -theme_override_font_sizes/font_size = 16 -text = "Cache Entries: 0" - -[node name="ResultsPanel" type="PanelContainer" parent="MarginContainer/VBoxContainer" unique_id=1825974467] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="VBox" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ResultsPanel" unique_id=339315001] -layout_mode = 2 - -[node name="ResultsTitle" type="Label" parent="MarginContainer/VBoxContainer/ResultsPanel/VBox" unique_id=975903988] -layout_mode = 2 -theme_override_font_sizes/font_size = 20 -text = "Results" - -[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer/ResultsPanel/VBox" unique_id=1950199865] -layout_mode = 2 -size_flags_vertical = 3 - -[node name="ResultsLabel" type="RichTextLabel" parent="MarginContainer/VBoxContainer/ResultsPanel/VBox/ScrollContainer" unique_id=107644773] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -bbcode_enabled = true -text = "Waiting for results..." -fit_content = true diff --git a/scripts/animal_friends_manager.gd b/scripts/animal_friends_manager.gd new file mode 100644 index 0000000..a342d0a --- /dev/null +++ b/scripts/animal_friends_manager.gd @@ -0,0 +1,101 @@ +extends Node + +## Manages the visibility of animal friends in the animal_friends group +## Animals start invisible and are revealed one at a time randomly as ranks are unlocked + +const ANIMAL_FRIENDS_UNLOCK_ID: int = 6 # Forest Friends unlock + +var all_animals: Array[Node] = [] +var hidden_animals: Array[Node] = [] +var revealed_animals: Array[Node] = [] +var last_known_rank: int = 0 + +func _ready() -> void: + # Wait a frame to ensure all nodes are ready + call_deferred("_initialize") + +func _initialize() -> void: + # Get all nodes in the animal_friends group + all_animals = get_tree().get_nodes_in_group("animal_friends") + + if all_animals.is_empty(): + Log.pr("Warning: No animal friends found in the animal_friends group") + return + + # Shuffle the animals to randomize the order they'll be revealed + all_animals.shuffle() + + # Start with all animals hidden + for animal in all_animals: + animal.visible = false + hidden_animals.append(animal) + + Log.pr("Animal Friends Manager initialized with %d animals" % all_animals.size()) + + # Get the Forest Friends unlock to track its rank + var forest_friends = Unlocks.get_unlock_by_id(ANIMAL_FRIENDS_UNLOCK_ID) + if forest_friends: + last_known_rank = forest_friends.current_rank + # Reveal animals based on current rank (for game loads) + _reveal_animals_for_current_rank() + else: + Log.pr("Warning: Forest Friends unlock (ID %d) not found" % ANIMAL_FRIENDS_UNLOCK_ID) + + # Connect to unlock signal to detect when new ranks are purchased + Unlocks.item_unlocked.connect(_on_item_unlocked) + +func _reveal_animals_for_current_rank() -> void: + # Reveal one animal per rank in the Forest Friends unlock + var forest_friends = Unlocks.get_unlock_by_id(ANIMAL_FRIENDS_UNLOCK_ID) + if not forest_friends: + return + + var ranks_to_reveal = forest_friends.current_rank + for i in range(ranks_to_reveal): + if hidden_animals.is_empty(): + break + _reveal_next_animal() + +func _on_item_unlocked() -> void: + # Check if the Forest Friends unlock was ranked up + var forest_friends = Unlocks.get_unlock_by_id(ANIMAL_FRIENDS_UNLOCK_ID) + if not forest_friends: + return + + var current_rank = forest_friends.current_rank + var new_ranks = current_rank - last_known_rank + + if new_ranks > 0: + Log.pr("Forest Friends ranked up! Revealing %d animal friend(s)" % new_ranks) + + # Reveal one animal for each new rank + for i in range(new_ranks): + if hidden_animals.is_empty(): + Log.pr("All animal friends have been revealed!") + break + _reveal_next_animal() + + last_known_rank = current_rank + +func _reveal_next_animal() -> void: + if hidden_animals.is_empty(): + return + + # Take the first animal from the shuffled hidden list (random order) + var animal = hidden_animals.pop_front() + animal.visible = true + revealed_animals.append(animal) + + Log.pr("Revealed animal friend: %s (%d/%d visible)" % [animal.name, revealed_animals.size(), all_animals.size()]) + +## Debug function to manually reveal the next animal +func debug_reveal_next() -> void: + _reveal_next_animal() + +## Debug function to hide all animals +func debug_reset_all() -> void: + for animal in all_animals: + animal.visible = false + hidden_animals = all_animals.duplicate() + revealed_animals.clear() + Log.pr("All animals hidden") diff --git a/scripts/animal_friends_manager.gd.uid b/scripts/animal_friends_manager.gd.uid new file mode 100644 index 0000000..a67e2c5 --- /dev/null +++ b/scripts/animal_friends_manager.gd.uid @@ -0,0 +1 @@ +uid://ewmlbda2adc7 diff --git a/scripts/audio.gd b/scripts/audio.gd index ea268d5..334fe05 100644 --- a/scripts/audio.gd +++ b/scripts/audio.gd @@ -7,16 +7,24 @@ func _ready(): play_background_music() func play_chop_sound(): + if not Global.play_chop_sound: + return ## Pick one of the chopping sounds randomly var chop_sounds = [ - "res://assets/audio/ogg/SFX/Chopping and Mining/chop 1.ogg", - "res://assets/audio/ogg/SFX/Chopping and Mining/chop 2.ogg", - "res://assets/audio/ogg/SFX/Chopping and Mining/chop 3.ogg", - "res://assets/audio/ogg/SFX/Chopping and Mining/chop 4.ogg" + "res://assets/audio/OGG/SFX/chopping/chop1.ogg", + "res://assets/audio/OGG/SFX/chopping/chop2.ogg", + "res://assets/audio/OGG/SFX/chopping/chop3.ogg", + "res://assets/audio/OGG/SFX/chopping/chop4.ogg" ] var random_index = randi() % chop_sounds.size() play_sound_effect(chop_sounds[random_index]) +func play_money_sound(): + if not Global.play_money_sound: + return + # Using a simple coin sound - you can replace with your own sound file + play_sound_effect("res://assets/audio/coin.mp3") + func play_sound_effect(sound_path: String): var sfx_player = AudioStreamPlayer.new() @@ -26,10 +34,22 @@ func play_sound_effect(sound_path: String): sfx_player.play() sfx_player.connect("finished", sfx_player.queue_free) +var music_player: AudioStreamPlayer = null + func play_background_music(): - var music_player = AudioStreamPlayer.new() + if not Global.play_background_music: + return + if music_player: + return # Already playing + music_player = AudioStreamPlayer.new() music_player.stream = load("res://assets/audio/background_music.ogg") music_player.volume_db = -10 # Set volume to a comfortable level music_player.autoplay = true add_child(music_player) music_player.play() + +func stop_background_music(): + if music_player: + music_player.stop() + music_player.queue_free() + music_player = null diff --git a/scripts/globals.gd b/scripts/globals.gd index b58e330..9d08dac 100644 --- a/scripts/globals.gd +++ b/scripts/globals.gd @@ -2,6 +2,8 @@ extends Node # SETTINGS var play_background_music: bool = true +var play_chop_sound: bool = true +var play_money_sound: bool = false var game_continue_pressed : bool = false # STRINGS @@ -13,7 +15,7 @@ var wood_color: Color = Color(0.95, 0.6, 0.35) # Light pumpkin orange (autumn le var stock_color: Color = Color(0.6, 0.75, 0.95) # Light periwinkle blue (clear autumn sky) # GAMEPLAY VALUES -var target_currency: float = 1000000 +var target_currency: float = 100 var base_sale_price: float = 30 var base_wood_respawn: float = 5 # seconds var wood_per_click: float = 5 @@ -30,3 +32,17 @@ var autowood_unlock_id: int = 7 var premium_crafts_unlock_id: int = 8 var reputation_unlock_id: int = 9 + +# FORMAT NUMBERS WITH COMMAS FOR DISPLAY +func format_number(value: float) -> String: + var num_str = str(int(value)) + var result = "" + var length = num_str.length() + + for i in range(length): + result += num_str[i] + var remaining = length - i - 1 + if remaining > 0 and remaining % 3 == 0: + result += "," + + return result diff --git a/scripts/sim_cached.gd b/scripts/sim_cached.gd deleted file mode 100644 index a719385..0000000 --- a/scripts/sim_cached.gd +++ /dev/null @@ -1,931 +0,0 @@ -class_name UnlockSimulatorCached -extends Control - -# CACHED VERSION - Uses aggressive chunk-based caching for 10-100x speedup - -# Load the actual game resources -var unlock_collection: UnlockDataCollection = load("res://resources/UnlockData.tres") -var inventory_resource: InventoryResource = load("res://resources/InventoryData.tres") - -# Results tracking -var all_results: Array[Dictionary] = [] -var results_mutex: Mutex = Mutex.new() - -# Chunk-based caching for intermediate simulation states -# Key: "unlock_id:rank,..." sorted string -# Value: {cache_key, ticks, currency, wood, stock, current_ranks, modifiers} -var simulation_cache: Dictionary = {} -var cache_mutex: Mutex = Mutex.new() - -# Global unlock struct cache (build once, clone per simulation) -var global_unlock_structs: Array[Dictionary] = [] -var global_unlock_structs_mutex: Mutex = Mutex.new() -var unlock_structs_initialized: bool = false - -# Manual thread pool -var num_threads: int = 14 # Increase this for more CPU usage -var threads: Array[Thread] = [] -var task_queue: Array[Dictionary] = [] -var queue_mutex: Mutex = Mutex.new() -var completed_count: int = 0 -var completed_mutex: Mutex = Mutex.new() -var active_threads: int = 0 -var threads_done: bool = false - -# Pre-calculated cost arrays for faster lookup - OPTIMIZATION -var cost_cache: Dictionary = {} - -var start_time: int = 0 -var total_combinations: int = 0 -var last_progress_time: int = 0 -var monitoring_active: bool = false -var cache_hits: int = 0 -var cache_misses: int = 0 - -# UI References -@onready var status_label = $MarginContainer/VBoxContainer/StatusPanel/VBox/StatusLabel -@onready var progress_label = $MarginContainer/VBoxContainer/StatusPanel/VBox/ProgressLabel -@onready var progress_bar = $MarginContainer/VBoxContainer/StatusPanel/VBox/ProgressBar -@onready var rate_label = $MarginContainer/VBoxContainer/StatusPanel/VBox/RateLabel -@onready var eta_label = $MarginContainer/VBoxContainer/StatusPanel/VBox/ETALabel -@onready var cache_hits_label = $MarginContainer/VBoxContainer/CachePanel/VBox/CacheHitsLabel -@onready var cache_misses_label = $MarginContainer/VBoxContainer/CachePanel/VBox/CacheMissesLabel -@onready var cache_rate_label = $MarginContainer/VBoxContainer/CachePanel/VBox/CacheRateLabel -@onready var cache_size_label = $MarginContainer/VBoxContainer/CachePanel/VBox/CacheSizeLabel -@onready var results_label = $MarginContainer/VBoxContainer/ResultsPanel/VBox/ScrollContainer/ResultsLabel - -func _ready(): - GameManager.tick.stop() - print("=== CACHED Unlock Simulator Started ===") - print("Using aggressive chunk-based caching for 10-100x speedup") - var cpu_count = OS.get_processor_count() - print("CPU cores detected: %d" % cpu_count) - print("Creating %d worker threads (adjust num_threads variable for more/less)" % num_threads) - - # Update UI - status_label.text = "Status: Starting cached simulation..." - results_label.text = "[b]CACHED Unlock Simulator Started[/b]\n\nCPU cores: %d\nWorker threads: %d\n\nGenerating combinations..." % [cpu_count, num_threads] - - run_comprehensive_test() - -func _process(_delta): - if monitoring_active: - # Only update progress once per second - var current_time = Time.get_ticks_msec() - if current_time - last_progress_time >= 1000: - last_progress_time = current_time - update_progress() - -func update_progress(): - """Update progress display""" - var current_count = 0 - completed_mutex.lock() - current_count = completed_count - completed_mutex.unlock() - - # Check if all work is complete - if current_count >= total_combinations: - monitoring_active = false - finish_processing() - return - - var percent = float(current_count) / total_combinations * 100.0 - var elapsed = (Time.get_ticks_msec() - start_time) / 1000.0 - var rate = current_count / elapsed if elapsed > 0 else 0 - var eta_seconds = (total_combinations - current_count) / rate if rate > 0 else 0 - - # Calculate cache hit rate - var total_cache_checks = cache_hits + cache_misses - var cache_hit_rate = (float(cache_hits) / total_cache_checks * 100.0) if total_cache_checks > 0 else 0.0 - - # Format ETA - var eta_str = "" - if eta_seconds > 0: - var eta_minutes = int(eta_seconds) / 60 - var eta_secs = int(eta_seconds) % 60 - if eta_minutes > 0: - eta_str = "%dm %ds" % [eta_minutes, eta_secs] - else: - eta_str = "%ds" % eta_secs - else: - eta_str = "calculating..." - - print("Progress: %.1f%% (%d/%d) - %.1f combos/sec - Cache: %.1f%% hits - ETA: %s" % [ - percent, current_count, total_combinations, rate, cache_hit_rate, eta_str - ]) - - # Update UI - status_label.text = "Status: Running simulation..." - progress_label.text = "Progress: %.1f%% (%d/%d)" % [percent, current_count, total_combinations] - progress_bar.value = percent / 100.0 - rate_label.text = "Speed: %.1f combos/sec" % rate - eta_label.text = "ETA: %s" % eta_str - - # Update cache stats - cache_hits_label.text = "Cache Hits: %d" % cache_hits - cache_misses_label.text = "Cache Misses: %d" % cache_misses - cache_rate_label.text = "Hit Rate: %.1f%%" % cache_hit_rate - cache_mutex.lock() - cache_size_label.text = "Cache Entries: %d" % simulation_cache.size() - cache_mutex.unlock() - -func worker_thread(thread_id: int): - """Worker thread function that pulls tasks from the queue""" - # Local batch storage to reduce mutex contention - OPTIMIZATION - var local_results: Array[Dictionary] = [] - var local_count: int = 0 - var batch_size: int = 10 # Process 10 before syncing - - while true: - # Get next task from queue - var task_data = null - queue_mutex.lock() - if task_queue.size() > 0: - task_data = task_queue.pop_front() - queue_mutex.unlock() - - # If no more tasks, flush results and exit - if task_data == null: - if local_results.size() > 0: - results_mutex.lock() - all_results.append_array(local_results) - results_mutex.unlock() - - # CRITICAL FIX: Update completed count when flushing final batch - completed_mutex.lock() - completed_count += local_results.size() - completed_mutex.unlock() - break - - # Process the task - var result = simulate_rank_combination_pure(task_data.combo, task_data.unlock_data, 1000000) - - # Store in local batch - local_results.append(result) - local_count += 1 - - # Flush batch periodically to reduce mutex contention - if local_results.size() >= batch_size: - results_mutex.lock() - all_results.append_array(local_results) - results_mutex.unlock() - - # CRITICAL FIX: Update progress counter with mutex protection - completed_mutex.lock() - completed_count += local_results.size() - completed_mutex.unlock() - - local_results.clear() - local_count = 0 - -func get_cache_key(current_ranks: Dictionary) -> String: - """Generate a cache key from current unlock ranks""" - var sorted_keys = current_ranks.keys() - sorted_keys.sort() - var key_parts = [] - for k in sorted_keys: - key_parts.append(str(k) + ":" + str(current_ranks[k])) - return ",".join(key_parts) - -func try_load_best_prefix_from_cache(rank_targets: Dictionary) -> Variant: - """Balanced cache lookup - fast with good coverage (~10-15 lookups)""" - - cache_mutex.lock() - - # Try exact match first - var full_key = get_cache_key(rank_targets) - if simulation_cache.has(full_key): - cache_hits += 1 - var result = simulation_cache[full_key] - cache_mutex.unlock() - return result - - # Sort unlock IDs for consistent ordering - var unlock_ids = rank_targets.keys() - unlock_ids.sort() - var num_unlocks = unlock_ids.size() - - var best_match = null - var best_rank_sum = 0 - - # STRATEGY: Try progressively shorter prefixes by dropping unlocks from the END - # This is the most common pattern: {1,2,3,4,5} → {1,2,3,4} → {1,2,3} → {1,2} → {1} - # Covers 80%+ of cache reuse because combinations are generated in sorted order - - for prefix_len in range(num_unlocks - 1, 0, -1): - var subset = {} - for i in range(prefix_len): - subset[unlock_ids[i]] = rank_targets[unlock_ids[i]] - - var key = get_cache_key(subset) - if simulation_cache.has(key): - var cached_entry = simulation_cache[key] - var rank_sum = 0 - for r in cached_entry.current_ranks.values(): - rank_sum += r - - # Keep best match (longest prefix) - if rank_sum > best_rank_sum: - best_match = cached_entry - best_rank_sum = rank_sum - # Early exit if we found a substantial match - if prefix_len >= num_unlocks - 2: - break - - if best_match != null: - cache_hits += 1 - else: - cache_misses += 1 - cache_mutex.unlock() - - return best_match - -func should_cache_state(current_ranks: Dictionary, targets_remaining: int) -> bool: - """Decide if this state is worth caching""" - # Don't cache if all targets reached - if targets_remaining == 0: - return false - - # Cache aggressively for early states - var total_ranks = 0 - var active_unlocks = 0 - - for rank in current_ranks.values(): - if rank > 0: - total_ranks += rank - active_unlocks += 1 - - # Cache if: multiple unlocks active OR significant progress on one - return (active_unlocks >= 2) or (total_ranks >= 2) - -func simulate_rank_combination_pure(rank_targets: Dictionary, unlock_data_array: Array, max_ticks: int, track_purchases: bool = false) -> Dictionary: - """Optimized pure simulation function with struct-based unlocks""" - var currency: float = 0.0 - var stock: float = 0.0 - var wood: float = 0.0 - - # Purchase tracking (only enabled for top results) - var purchases: Array[Dictionary] = [] - - # GLOBAL STRUCT CACHE - Build once, clone per simulation - # This avoids rebuilding cost/effect tables for every simulation - var unlocks: Array[Dictionary] = [] - var unlock_by_id: Dictionary = {} - - if not unlock_structs_initialized: - global_unlock_structs_mutex.lock() - if not unlock_structs_initialized: # Double-check pattern - for unlock_data in unlock_data_array: - var base_mods = unlock_data.base_modifiers - - # Pre-calculate cost table for first 20 ranks (avoid pow() in hot loop) - var cost_table: Array[float] = [] - if unlock_data.is_scaling: - # Use cost_ladder if defined, otherwise use exponential scaling - if unlock_data.has("cost_ladder") and unlock_data.cost_ladder.size() > 0: - # Use fixed cost ladder - for cost in unlock_data.cost_ladder: - cost_table.append(float(cost)) - # Fill remaining slots with last cost for safety - while cost_table.size() < 21: - cost_table.append(cost_table[cost_table.size() - 1]) - else: - # Fallback to exponential scaling - var base_cost_float = float(unlock_data.base_cost) - var mult = unlock_data.cost_scaling_multiplier - for r in range(21): # Pre-calc ranks 0-20 - cost_table.append(base_cost_float * pow(mult, r)) - else: - cost_table.append(float(unlock_data.base_cost)) - - # Pre-calculate effect scale factors for first 20 ranks - var effect_scale_table: Array[float] = [] - var effect_mult = unlock_data.effect_scaling_multiplier - for r in range(21): - if r == 0: - effect_scale_table.append(0.0) # No effect at rank 0 - elif r == 1: - effect_scale_table.append(1.0) # Base effect at rank 1 - else: - effect_scale_table.append(pow(effect_mult, r - 1)) - - var unlock_struct = { - "id": unlock_data.unlock_id, - "name": unlock_data.unlock_name, - "base_cost": unlock_data.base_cost, - "is_scaling": unlock_data.is_scaling, - "max_rank": unlock_data.max_rank, - "cost_multiplier": unlock_data.cost_scaling_multiplier, - "effect_multiplier": unlock_data.effect_scaling_multiplier, - "base_mods": base_mods, - "cost_table": cost_table, # Pre-calculated costs - "effect_scale_table": effect_scale_table, # Pre-calculated effect scales - # Pre-calculate whether this unlock affects each modifier (avoids string lookups) - "affects_sale_price": base_mods.has("sale_price_modifier"), - "affects_efficiency": base_mods.has("efficiency_modifier"), - "affects_wood_per_click": base_mods.has("wood_per_click_modifier"), - "affects_purchase_rate": base_mods.has("purchase_rate_modifier"), - "affects_autowood": base_mods.has("autowood_modifier"), - "is_multicraft": base_mods.has("multicraft_increase_modifier"), - # Cache base modifier values to avoid dictionary lookups - "sale_price_value": base_mods.get("sale_price_modifier", 1.0), - "efficiency_value": base_mods.get("efficiency_modifier", 1.0), - "wood_per_click_value": base_mods.get("wood_per_click_modifier", 1.0), - "purchase_rate_value": base_mods.get("purchase_rate_modifier", 1.0), - "autowood_value": base_mods.get("autowood_modifier", 0.0) - } - global_unlock_structs.append(unlock_struct) - - unlock_structs_initialized = true - global_unlock_structs_mutex.unlock() - - # Clone structs for this simulation (fast shallow copy) - for template in global_unlock_structs: - var unlock = template.duplicate(false) # Shallow copy - unlock.current_rank = 0 # Reset rank for this simulation - unlocks.append(unlock) - unlock_by_id[unlock.id] = unlock - - var ticks: int = 0 - # Removed purchases array - it's only needed for debug output and slows down simulation - - # Track how many targets still need to be reached - OPTIMIZATION - var targets_remaining: int = 0 - var current_ranks: Dictionary = {} - var active_unlock_ids: Array = [] # Only check unlocks that haven't reached target yet - for unlock_id in rank_targets.keys(): - current_ranks[unlock_id] = 0 - targets_remaining += rank_targets[unlock_id] - active_unlock_ids.append(unlock_id) - - # Modifiers as individual variables for faster access - MAJOR OPTIMIZATION - var sale_price_mod: float = 1.0 - var efficiency_mod: float = 1.0 - var wood_per_click_mod: float = 1.0 - var purchase_rate_mod: float = 1.0 - var autowood_mod: float = 0.0 - var multicraft_rank: int = 0 - - var wholesale_unlocked: bool = false - - # Pre-calculate constants - var wood_per_click_base: float = Global.wood_per_click - var cost_per_whittle: float = Global.cost_per_whittle - var base_sale_price: float = Global.base_sale_price - var base_purchase_rate: float = Global.base_purchase_rate - var wholesale_id: int = Global.wholesale_unlock_id - var wholesale_size: float = Global.wholesale_bundle_size - var wholesale_mult: float = Global.wholesale_discount_multiplier - - # CACHE LOOKUP: Try to load from cached intermediate state - # NOTE: Disable cache when tracking purchases to ensure all purchases are recorded - var cached_state = null - if not track_purchases: - cached_state = try_load_best_prefix_from_cache(rank_targets) - - if cached_state != null: - # Restore full state from cache - ticks = cached_state.ticks - currency = cached_state.currency - stock = cached_state.stock - wood = cached_state.wood - - # Restore modifiers - sale_price_mod = cached_state.modifiers.sale_price_mod - efficiency_mod = cached_state.modifiers.efficiency_mod - wood_per_click_mod = cached_state.modifiers.wood_per_click_mod - purchase_rate_mod = cached_state.modifiers.purchase_rate_mod - autowood_mod = cached_state.modifiers.autowood_mod - multicraft_rank = cached_state.modifiers.multicraft_rank - wholesale_unlocked = cached_state.modifiers.wholesale_unlocked - - # Restore unlock ranks - for unlock_id in cached_state.current_ranks.keys(): - if unlock_by_id.has(unlock_id): - unlock_by_id[unlock_id].current_rank = cached_state.current_ranks[unlock_id] - current_ranks[unlock_id] = cached_state.current_ranks[unlock_id] - - # Recalculate targets_remaining and active_unlock_ids - targets_remaining = 0 - active_unlock_ids.clear() - for unlock_id in rank_targets.keys(): - if not current_ranks.has(unlock_id): - current_ranks[unlock_id] = 0 - var remaining = rank_targets[unlock_id] - current_ranks[unlock_id] - if remaining > 0: - targets_remaining += remaining - active_unlock_ids.append(unlock_id) - - # PRE-CALCULATE all next costs to avoid repeated lookups in main loop - var next_costs: Array[float] = [] - next_costs.resize(active_unlock_ids.size()) - - for i in range(active_unlock_ids.size()): - var unlock = unlock_by_id[active_unlock_ids[i]] - var current_rank: int = unlock.current_rank - if current_rank < unlock.cost_table.size(): - next_costs[i] = unlock.cost_table[current_rank] - else: - next_costs[i] = unlock.base_cost * pow(unlock.cost_multiplier, current_rank) - - while ticks < max_ticks: - # Find cheapest affordable unlock using pre-calculated costs - var cheapest_unlock_id: int = -1 - var cheapest_cost: float = INF - var cheapest_unlock = null - var cheapest_index: int = -1 - - if targets_remaining > 0: - for i in range(active_unlock_ids.size()): - if next_costs[i] < cheapest_cost and currency >= next_costs[i]: - cheapest_cost = next_costs[i] - cheapest_unlock_id = active_unlock_ids[i] - cheapest_unlock = unlock_by_id[cheapest_unlock_id] - cheapest_index = i - - # If we can't afford anything and all targets are met, skip to earning 1M - if cheapest_unlock == null and targets_remaining == 0: - if currency >= 1000000.0: - break - # Skip ahead: calculate ticks needed to reach 1M currency - # Use current production rate to estimate - var currency_needed = 1000000.0 - currency - var price_per_item = base_sale_price * sale_price_mod - var items_per_tick = max(1.0, floor(base_purchase_rate * purchase_rate_mod)) - var revenue_per_tick = items_per_tick * price_per_item - - if revenue_per_tick > 0: - var ticks_needed = int(ceil(currency_needed / revenue_per_tick)) - ticks += ticks_needed - currency += revenue_per_tick * ticks_needed - break - - # Purchase the cheapest unlock if found - if cheapest_unlock != null: - currency -= cheapest_cost - cheapest_unlock.current_rank += 1 - current_ranks[cheapest_unlock_id] += 1 - targets_remaining -= 1 - - # Update wholesale cache - if cheapest_unlock_id == wholesale_id: - wholesale_unlocked = true - - # OPTIMIZED modifier update - use ratio instead of recalculating from scratch - var rank: int = cheapest_unlock.current_rank - var prev_rank: int = rank - 1 - - if cheapest_unlock.is_multicraft: - multicraft_rank = rank - - # Get scale factors from pre-calculated tables - var old_scale: float = cheapest_unlock.effect_scale_table[prev_rank] if prev_rank < cheapest_unlock.effect_scale_table.size() else 0.0 - var new_scale: float = cheapest_unlock.effect_scale_table[rank] if rank < cheapest_unlock.effect_scale_table.size() else pow(cheapest_unlock.effect_multiplier, rank - 1) - - # Apply incremental changes using ratio - if cheapest_unlock.affects_sale_price: - var base_bonus: float = cheapest_unlock.sale_price_value - 1.0 - var old_mult: float = 1.0 + base_bonus * old_scale - var new_mult: float = 1.0 + base_bonus * new_scale - sale_price_mod = sale_price_mod * (new_mult / old_mult) - - if cheapest_unlock.affects_efficiency: - var base_bonus: float = cheapest_unlock.efficiency_value - 1.0 - var old_mult: float = 1.0 + base_bonus * old_scale - var new_mult: float = 1.0 + base_bonus * new_scale - efficiency_mod = efficiency_mod * (new_mult / old_mult) - - if cheapest_unlock.affects_wood_per_click: - var base_bonus: float = cheapest_unlock.wood_per_click_value - 1.0 - var old_mult: float = 1.0 + base_bonus * old_scale - var new_mult: float = 1.0 + base_bonus * new_scale - wood_per_click_mod = wood_per_click_mod * (new_mult / old_mult) - - if cheapest_unlock.affects_purchase_rate: - var base_bonus: float = cheapest_unlock.purchase_rate_value - 1.0 - var old_mult: float = 1.0 + base_bonus * old_scale - var new_mult: float = 1.0 + base_bonus * new_scale - purchase_rate_mod = purchase_rate_mod * (new_mult / old_mult) - - if cheapest_unlock.affects_autowood: - autowood_mod = autowood_mod - cheapest_unlock.autowood_value * prev_rank + cheapest_unlock.autowood_value * rank - - # Track purchase if enabled - if track_purchases: - if purchases.size() == 0: - print("DEBUG: First purchase being tracked!") - purchases.append({ - "unlock_id": cheapest_unlock_id, - "unlock_name": cheapest_unlock.name, - "rank": rank, - "cost": cheapest_cost, - "tick": ticks, - "currency_after": currency - }) - - # Update next cost for this unlock or remove from active list - if current_ranks[cheapest_unlock_id] >= rank_targets[cheapest_unlock_id]: - # Target reached - swap with last element and shrink array - var last_idx = active_unlock_ids.size() - 1 - if cheapest_index != last_idx: - active_unlock_ids[cheapest_index] = active_unlock_ids[last_idx] - next_costs[cheapest_index] = next_costs[last_idx] - active_unlock_ids.resize(last_idx) - next_costs.resize(last_idx) - else: - # Update cost for next rank - var new_rank = cheapest_unlock.current_rank - if new_rank < cheapest_unlock.cost_table.size(): - next_costs[cheapest_index] = cheapest_unlock.cost_table[new_rank] - else: - next_costs[cheapest_index] = cheapest_unlock.base_cost * pow(cheapest_unlock.cost_multiplier, new_rank) - - # Removed purchase tracking for performance - only track in debug mode if needed - # Don't append to purchases array on every purchase - - # CACHE INSERTION: Cache this state if valuable - if should_cache_state(current_ranks, targets_remaining): - var cache_key = get_cache_key(current_ranks) - - cache_mutex.lock() - if not simulation_cache.has(cache_key): - simulation_cache[cache_key] = { - "cache_key": cache_key, - "ticks": ticks, - "currency": currency, - "stock": stock, - "wood": wood, - "current_ranks": current_ranks.duplicate(), - "modifiers": { - "sale_price_mod": sale_price_mod, - "efficiency_mod": efficiency_mod, - "wood_per_click_mod": wood_per_click_mod, - "purchase_rate_mod": purchase_rate_mod, - "autowood_mod": autowood_mod, - "multicraft_rank": multicraft_rank, - "wholesale_unlocked": wholesale_unlocked - } - } - cache_mutex.unlock() - - # Simulate one tick - HEAVILY OPTIMIZED - - # 1. Generate wood - var wood_per_click_modified = wood_per_click_base * wood_per_click_mod - - # Manual clicks based on tick range (pre-calculate to avoid repeated conditions) - var manual_clicks: float = 1.0 if ticks < 120 else (0.5 if ticks < 300 else (0.25 if (ticks < 600 and autowood_mod < 0.2) else 0.0)) - - # Total wood generation - var wood_gen: float = manual_clicks * wood_per_click_modified - if autowood_mod > 0.0: - wood_gen += max(wood_per_click_modified * autowood_mod, 1.0) - wood += wood_gen - - # 2. Whittle wood into stock - MATCHES tick_process.gd:19-32 - # Base whittling action (always happens once) - var multicraft_actions = 1 + multicraft_rank # 1 base + multicraft ranks - - # Each whittle action: items_produced_per_tick = cost_per_whittle * efficiency_modifier - var items_per_whittle = cost_per_whittle * efficiency_mod - - for action in range(multicraft_actions): - if wood >= 1: # Need at least 1 wood to whittle - # How much wood needed for this whittle (matches tick_process.gd:63-65) - var wood_needed = ceil(items_per_whittle) - var wood_to_use = min(wood, wood_needed) - var items_produced = wood_to_use # 1 wood = 1 item always - - wood -= wood_to_use - stock += items_produced - else: - break # Not enough wood for more whittle actions - - # 3. Sell stock for currency - MATCHES tick_process.gd:34-58 - var price_per_item = base_sale_price * sale_price_mod - - # 3a. Wholesale selling (if unlocked) - matches tick_process.gd:36-42 - # Sell ALL possible 100-item bundles at 1.2x price - if wholesale_unlocked: - while stock >= wholesale_size: - stock -= wholesale_size - currency += wholesale_size * price_per_item * wholesale_mult - - # 3b. Regular selling - matches tick_process.gd:45-58 - if stock > 0: - var purchase_rate = base_purchase_rate * purchase_rate_mod - var max_stock_to_sell = floor(purchase_rate) - # Always sell at least 1, up to the max - var actual_stock_to_sell = min(stock, max(1.0, max_stock_to_sell)) - stock -= actual_stock_to_sell - currency += actual_stock_to_sell * price_per_item - - ticks += 1 - - var success = currency >= 1000000.0 - - var result = { - "rank_targets": rank_targets, - "success": success, - "ticks": ticks if success else -1, - "final_currency": currency, - "time_formatted": format_time(ticks) if success else "Failed" - } - - # Include purchase timeline if tracking was enabled - if track_purchases: - result["purchases"] = purchases - print("DEBUG: track_purchases=true, purchases.size()=%d" % purchases.size()) - - return result - - -func format_time(ticks: int) -> String: - var seconds = ticks - var minutes = seconds / 60 - var hours = minutes / 60 - - if hours > 0: - return "%dh %dm %ds" % [hours, minutes % 60, seconds % 60] - elif minutes > 0: - return "%dm %ds" % [minutes, seconds % 60] - else: - return "%ds" % seconds - -func generate_all_combinations(unlimited_scaling_cap: int = 5) -> Array[Dictionary]: - """Generate combinations for ALL unlocks dynamically, respecting max_ranks from resource file - - Args: - unlimited_scaling_cap: Maximum rank to test for unlocks with unlimited scaling (default: 5) - Lower values = faster testing, higher = more comprehensive - Cap=3: ~13K combos (~28 sec) | Cap=5: ~47K combos (~93 sec) - Cap=7: ~111K combos (~3.7min) | Cap=10: ~287K combos (~9.6min) - """ - var combinations: Array[Dictionary] = [] - - # Build constraint list from resource file - var unlock_constraints = [] - for unlock in unlock_collection.unlocks: - var max_rank: int - if unlock.max_rank > 0: - max_rank = unlock.max_rank - elif not unlock.is_scaling: - max_rank = 1 # One-shot unlocks - else: - max_rank = unlimited_scaling_cap # Configurable cap for unlimited scaling - - unlock_constraints.append({ - "id": unlock.unlock_id, - "name": unlock.unlock_name, - "max_rank": max_rank - }) - - print("\n=== Generating Combinations ===") - print("Reading from resource file: %d unlocks" % unlock_constraints.size()) - for c in unlock_constraints: - print(" - %s (ID %d): 0-%d ranks" % [c.name, c.id, c.max_rank]) - - # Recursive generation - _generate_combinations_recursive(unlock_constraints, 0, {}, combinations) - - print("Generated %d total combinations" % combinations.size()) - return combinations - -func _generate_combinations_recursive(constraints: Array, index: int, current: Dictionary, output: Array): - """Recursively generate all valid combinations""" - if index >= constraints.size(): - # Skip all-zeros combination - if current.size() > 0: - output.append(current.duplicate()) - return - - var constraint = constraints[index] - for rank in range(constraint.max_rank + 1): - if rank > 0: - current[constraint.id] = rank - - _generate_combinations_recursive(constraints, index + 1, current, output) - - if rank > 0: - current.erase(constraint.id) - -func serialize_unlock_data() -> Array: - """Convert unlock collection to serializable data for threads""" - var unlock_data = [] - for unlock in unlock_collection.unlocks: - unlock_data.append({ - "unlock_id": unlock.unlock_id, - "unlock_name": unlock.unlock_name, - "base_cost": unlock.base_cost, - "is_scaling": unlock.is_scaling, - "max_rank": unlock.max_rank, - "cost_scaling_multiplier": unlock.cost_scaling_multiplier, - "effect_scaling_multiplier": unlock.effect_scaling_multiplier, - "cost_ladder": unlock.cost_ladder.duplicate() if unlock.cost_ladder.size() > 0 else [], - "base_modifiers": unlock.base_modifiers.duplicate() - }) - return unlock_data - -func run_comprehensive_test(): - """Test all combinations dynamically generated from resource file""" - print("\n=== Available Unlocks ===") - for unlock in unlock_collection.unlocks: - var max_rank_str = str(unlock.max_rank) if unlock.max_rank > 0 else "unlimited" - print("ID: %d | %s | Base Cost: %d | Scaling: %s | Max Rank: %s" % [ - unlock.unlock_id, - unlock.unlock_name, - unlock.base_cost, - "Yes" if unlock.is_scaling else "No", - max_rank_str - ]) - print(" Modifiers: ", unlock.base_modifiers) - - print("\n=== Global Constants ===") - print("Base Sale Price: %s" % Global.base_sale_price) - print("Base Purchase Rate: %s" % Global.base_purchase_rate) - print("Cost Per Whittle: %s" % Global.cost_per_whittle) - - # Serialize unlock data for threads - var unlock_data = serialize_unlock_data() - - # CACHE WARMUP: Pre-populate cache with common single-unlock states - print("\n=== Cache Warmup ===") - print("Pre-populating cache with common prefixes...") - for unlock in unlock_collection.unlocks: - var max_warmup_rank = 3 - if unlock.max_rank > 0: - max_warmup_rank = min(unlock.max_rank, 3) - - for rank in range(1, max_warmup_rank + 1): - var warmup_target = {unlock.unlock_id: rank} - simulate_rank_combination_pure(warmup_target, unlock_data, 1000000) - - print("Cache warmup complete. Cache size: %d entries" % simulation_cache.size()) - - # Generate all combinations (configurable cap for unlimited scaling) - var unlimited_cap = 5 # Adjust this to test more/fewer ranks: 3=fast, 5=balanced, 7+=comprehensive - print("\n=== Generation Settings ===") - print("Unlimited scaling cap: %d ranks" % unlimited_cap) - var combinations = generate_all_combinations(unlimited_cap) - total_combinations = combinations.size() - print("\n=== Testing %d Combinations ===" % total_combinations) - - # Fill task queue - task_queue.clear() - for combo in combinations: - task_queue.append({ - "combo": combo, - "unlock_data": unlock_data - }) - - # Reset counters - completed_count = 0 - all_results.clear() - threads_done = false - start_time = Time.get_ticks_msec() - last_progress_time = start_time - monitoring_active = true - - # Create and start threads - print("Starting %d worker threads..." % num_threads) - for i in range(num_threads): - var thread = Thread.new() - thread.start(worker_thread.bind(i)) - threads.append(thread) - - print("All threads started, processing...") - -func finish_processing(): - """Called when all processing is complete""" - print("\nAll combinations complete! Waiting for threads to finish...") - - # Wait for all threads to finish - for thread in threads: - thread.wait_to_finish() - threads.clear() - threads_done = true - - print("All threads finished. Processing results...") - - var total_time = (Time.get_ticks_msec() - start_time) / 1000.0 - - # SAFETY CHECK: Verify result count matches - results_mutex.lock() - var actual_results = all_results.size() - results_mutex.unlock() - - if actual_results != total_combinations: - print("WARNING: Result count mismatch! Expected %d, got %d" % [total_combinations, actual_results]) - print("This indicates a threading issue where some results weren't flushed") - - # Print results - print("\n=== RESULTS ===") - print("Total time: %.1f seconds" % total_time) - print("Total combinations tested: %d (expected %d)" % [actual_results, total_combinations]) - - # Cache statistics - var total_cache_checks = cache_hits + cache_misses - var cache_hit_rate = (float(cache_hits) / total_cache_checks * 100.0) if total_cache_checks > 0 else 0.0 - cache_mutex.lock() - var cache_size = simulation_cache.size() - cache_mutex.unlock() - print("\n=== CACHE STATISTICS ===") - print("Cache hits: %d" % cache_hits) - print("Cache misses: %d" % cache_misses) - print("Hit rate: %.1f%%" % cache_hit_rate) - print("Cache entries stored: %d" % cache_size) - - var successful = all_results.filter(func(r): return r.success) - print("Successful strategies: %d" % successful.size()) - - # Update UI status - status_label.text = "Status: Complete!" - progress_label.text = "Progress: 100%% (%d/%d)" % [all_results.size(), total_combinations] - progress_bar.value = 1.0 - eta_label.text = "Total Time: %.1f seconds" % total_time - - # Build results text for UI - var results_text = "[b]SIMULATION COMPLETE[/b]\n\n" - results_text += "[color=green]Total time: %.1f seconds[/color]\n" % total_time - results_text += "Combinations tested: %d\n" % all_results.size() - results_text += "Successful strategies: %d\n\n" % successful.size() - - results_text += "[b]Cache Performance:[/b]\n" - results_text += " Hits: %d\n" % cache_hits - results_text += " Misses: %d\n" % cache_misses - results_text += " [color=cyan]Hit Rate: %.1f%%[/color]\n" % cache_hit_rate - results_text += " Entries: %d\n\n" % cache_size - - if successful.size() > 0: - # Sort by ticks (fastest first) - successful.sort_custom(func(a, b): return a.ticks < b.ticks) - - # Re-simulate top 10 with detailed purchase tracking - print("\n=== RE-SIMULATING TOP 10 WITH PURCHASE TRACKING ===") - var unlock_data = serialize_unlock_data() - var top_10_detailed: Array = [] - - for i in range(min(10, successful.size())): - var result = successful[i] - print("Re-simulating #%d with track_purchases=true..." % (i + 1)) - var detailed_result = simulate_rank_combination_pure(result.rank_targets, unlock_data, 1000000, true) - print(" Result has purchases key: %s" % detailed_result.has("purchases")) - if detailed_result.has("purchases"): - print(" Purchases array size: %d" % detailed_result.purchases.size()) - top_10_detailed.append(detailed_result) - - print("\n=== TOP 10 FASTEST STRATEGIES (WITH PURCHASE TIMELINE) ===") - results_text += "[b]TOP 10 FASTEST STRATEGIES:[/b]\n\n" - - for i in range(top_10_detailed.size()): - var result = top_10_detailed[i] - print("\n#%d: %s (%d ticks)" % [i + 1, result.time_formatted, result.ticks]) - - # Format ranks with unlock names - var rank_display = [] - for unlock_id in result.rank_targets.keys(): - var unlock_name = get_unlock_name_by_id(unlock_id) - var ranks = result.rank_targets[unlock_id] - rank_display.append("%s: %d" % [unlock_name, ranks]) - print("Target Ranks: %s" % ", ".join(rank_display)) - - # Add to UI - results_text += "[color=yellow]#%d: %s (%d ticks)[/color]\n" % [i + 1, result.time_formatted, result.ticks] - results_text += " Ranks: %s\n" % ", ".join(rank_display) - results_text += " Currency: %.0f\n" % result.final_currency - - # Add purchase timeline - if result.has("purchases") and result.purchases.size() > 0: - print("\nPurchase Timeline:") - results_text += " [b]Purchase Timeline:[/b]\n" - for purchase in result.purchases: - var time_str = format_time(purchase.tick) - print(" %s: %s Rank %d - Cost: %d¥ @ %s" % [ - time_str, purchase.unlock_name, purchase.rank, - purchase.cost, time_str - ]) - results_text += " • %s [color=cyan]%s Rank %d[/color] - %d¥ @ %s\n" % [ - format_time(purchase.tick), purchase.unlock_name, purchase.rank, - purchase.cost, time_str - ] - results_text += "\n" - else: - print("\nNo successful strategies found!") - results_text += "[color=red]No successful strategies found![/color]\n" - - # Update results UI - results_label.text = results_text - -func get_unlock_name_by_id(unlock_id: int) -> String: - """Helper function to get unlock name by ID""" - for unlock in unlock_collection.unlocks: - if unlock.unlock_id == unlock_id: - return unlock.unlock_name - return "Unknown" - -func _exit_tree(): - # Clean up threads - monitoring_active = false - for thread in threads: - if thread.is_alive(): - thread.wait_to_finish() diff --git a/scripts/sim_cached.gd.uid b/scripts/sim_cached.gd.uid deleted file mode 100644 index 642ef22..0000000 --- a/scripts/sim_cached.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bup76ad02kuse diff --git a/scripts/sim_direct.gd b/scripts/sim_direct.gd deleted file mode 100644 index aa28717..0000000 --- a/scripts/sim_direct.gd +++ /dev/null @@ -1,1036 +0,0 @@ -class_name SimulatorDirect -extends Control - -# DIRECT SIMULATOR - Uses real TickProcess with isolated state -# Zero code duplication via dependency injection - -# Load the actual game resources -var unlock_collection: UnlockDataCollection = load("res://resources/UnlockData.tres") - -# Results tracking -var all_results: Array[Dictionary] = [] -var results_mutex: Mutex = Mutex.new() - -# Chunk-based caching for intermediate simulation states -var simulation_cache: Dictionary = {} -var cache_mutex: Mutex = Mutex.new() - -# Global unlock template cache (build once, clone per simulation) -# Pre-calculated lookup tables (built once from UnlockDataResource, then used statically) -var precalc_costs: Dictionary = {} # {unlock_id: [cost_at_rank_0, cost_at_rank_1, ...]} -var precalc_modifiers: Dictionary = {} # {unlock_id: {rank: {"modifier_name": value}}} -var precalc_base_modifiers: Dictionary = {} # {unlock_id: base_modifiers_dict} -var precalc_max_ranks: Dictionary = {} # {unlock_id: max_rank} -var precalc_is_scaling: Dictionary = {} # {unlock_id: is_scaling} -var precalc_unlock_names: Dictionary = {} # {unlock_id: name} -var precalc_initialized: bool = false -var precalc_mutex: Mutex = Mutex.new() - -# Manual thread pool -var num_threads: int = 14 -var threads: Array[Thread] = [] -var task_queue: Array[Dictionary] = [] -var queue_mutex: Mutex = Mutex.new() -var completed_count: int = 0 -var completed_mutex: Mutex = Mutex.new() -var threads_done: bool = false - -var start_time: int = 0 -var total_combinations: int = 0 -var last_progress_time: int = 0 -var monitoring_active: bool = false -var cache_hits: int = 0 -var cache_misses: int = 0 - -# UI References -@onready var status_label = $MarginContainer/VBoxContainer/StatusPanel/VBox/StatusLabel -@onready var progress_label = $MarginContainer/VBoxContainer/StatusPanel/VBox/ProgressLabel -@onready var progress_bar = $MarginContainer/VBoxContainer/StatusPanel/VBox/ProgressBar -@onready var rate_label = $MarginContainer/VBoxContainer/StatusPanel/VBox/RateLabel -@onready var eta_label = $MarginContainer/VBoxContainer/StatusPanel/VBox/ETALabel -@onready var cache_hits_label = $MarginContainer/VBoxContainer/CachePanel/VBox/CacheHitsLabel -@onready var cache_misses_label = $MarginContainer/VBoxContainer/CachePanel/VBox/CacheMissesLabel -@onready var cache_rate_label = $MarginContainer/VBoxContainer/CachePanel/VBox/CacheRateLabel -@onready var cache_size_label = $MarginContainer/VBoxContainer/CachePanel/VBox/CacheSizeLabel -@onready var results_label = $MarginContainer/VBoxContainer/ResultsPanel/VBox/ScrollContainer/ResultsLabel - -func _ready(): - GameManager.tick.stop() - print("=== DIRECT Unlock Simulator Started ===") - print("Using real TickProcess with isolated state via dependency injection") - var cpu_count = OS.get_processor_count() - print("CPU cores detected: %d" % cpu_count) - print("Creating %d worker threads" % num_threads) - - # Update UI - status_label.text = "Status: Starting direct simulation..." - results_label.text = "[b]DIRECT Unlock Simulator Started[/b]\n\nCPU cores: %d\nWorker threads: %d\n\nGenerating combinations..." % [cpu_count, num_threads] - - run_comprehensive_test() - -func _process(_delta): - if monitoring_active: - var current_time = Time.get_ticks_msec() - if current_time - last_progress_time >= 1000: - last_progress_time = current_time - update_progress() - -# ============================================================================= -# ISOLATED GAME STATE CLASS -# ============================================================================= - -class IsolatedGameState: - """Isolated game state using pre-calculated lookup tables (NO object creation!)""" - - # Reference to pre-calculated tables (shared, read-only) - var costs: Dictionary # {unlock_id: [cost_array]} - var modifiers: Dictionary # {unlock_id: {rank: mods_dict}} - var max_ranks: Dictionary # {unlock_id: max_rank} - var is_scaling: Dictionary # {unlock_id: bool} - var base_mods: Dictionary # {unlock_id: base_modifiers} - - # Local inventory state (not Inventory singleton) - var currency: float = 0.0 - var wood: float = 0.0 - var stock: float = 0.0 - - # Cached modifiers (not Unlocks.current_modifiers) - var sale_price_mod: float = 1.0 - var efficiency_mod: float = 1.0 - var wood_per_click_mod: float = 1.0 - var purchase_rate_mod: float = 1.0 - var autowood_mod: float = 0.0 - var multicraft_rank: int = 0 - var wholesale_unlocked: bool = false - - # Simulation state - var ticks: int = 0 - var current_ranks: Dictionary = {} - - # Cache FakeUnlock objects to avoid repeated creation (OPTIMIZATION) - var fake_unlock_cache: Dictionary = {} # {unlock_id: FakeUnlock} - - func _init(precalc_costs: Dictionary, precalc_modifiers: Dictionary, precalc_max_ranks: Dictionary, precalc_is_scaling: Dictionary, precalc_base_mods: Dictionary): - """Initialize with references to pre-calculated lookup tables""" - costs = precalc_costs - modifiers = precalc_modifiers - max_ranks = precalc_max_ranks - is_scaling = precalc_is_scaling - base_mods = precalc_base_mods - - # ============================================================================= - # UNLOCKS SINGLETON INTERFACE REPLACEMENT - # ============================================================================= - - func get_unlock_by_id(unlock_id: int): - """Replacement for Unlocks.get_unlock_by_id() - returns cached fake unlock""" - if not current_ranks.has(unlock_id): - return null - - # Reuse cached FakeUnlock object (just update its state) - var fake = fake_unlock_cache.get(unlock_id) - if fake == null: - fake = FakeUnlock.new() - fake.unlock_id = unlock_id - fake.state = self - fake_unlock_cache[unlock_id] = fake - - # Update state from current_ranks - fake.current_rank = current_ranks.get(unlock_id, 0) - fake.is_unlocked = fake.current_rank > 0 - return fake - - # Nested class for fake unlock objects (uses parent's lookup tables) - class FakeUnlock: - var unlock_id: int - var current_rank: int - var is_unlocked: bool - var state: IsolatedGameState - - func get_next_cost() -> int: - var cost_array = state.costs.get(unlock_id, []) - if current_rank < cost_array.size(): - return cost_array[current_rank] - return 999999999 - - func get_current_modifiers() -> Dictionary: - if not is_unlocked or current_rank == 0: - return {} - var rank_mods = state.modifiers.get(unlock_id, {}) - return rank_mods.get(current_rank, {}) - - func get_modifiers_at_rank(rank: int) -> Dictionary: - """Get modifiers at a specific rank (for calculating deltas)""" - if rank == 0: - return {} - var rank_mods = state.modifiers.get(unlock_id, {}) - return rank_mods.get(rank, {}) - - func can_rank_up() -> bool: - var max_rank = state.max_ranks.get(unlock_id, -1) - if max_rank > 0 and current_rank >= max_rank: - return false - return true - - func unlock() -> bool: - if not can_rank_up(): - return false - current_rank += 1 - is_unlocked = true - state.current_ranks[unlock_id] = current_rank - return true - - func get_modifier_value(modifier_key: String) -> float: - """Replacement for Unlocks.get_modifier_value()""" - match modifier_key: - "sale_price_modifier": return sale_price_mod - "efficiency_modifier": return efficiency_mod - "wood_per_click_modifier": return wood_per_click_mod - "purchase_rate_modifier": return purchase_rate_mod - "autowood_modifier": return autowood_mod - _: return 1.0 - - func get_wood_per_click() -> float: - """Replacement for Unlocks.get_wood_per_click()""" - return Global.wood_per_click * wood_per_click_mod - - func get_items_produced_per_tick() -> float: - """Replacement for Unlocks.get_items_produced_per_tick()""" - return Global.cost_per_whittle * efficiency_mod - - func get_sale_price_per_item() -> float: - """Replacement for Unlocks.get_sale_price_per_item()""" - return Global.base_sale_price * sale_price_mod - - # ============================================================================= - # INVENTORY SINGLETON INTERFACE REPLACEMENT - # ============================================================================= - - func add_wood(amount: float): - """Replacement for Inventory.add_wood()""" - wood += amount - - func get_wood() -> float: - """Replacement for Inventory.get_wood()""" - return wood - - func spend_wood(amount: float) -> bool: - """Replacement for Inventory.spend_wood()""" - if wood >= amount: - wood -= amount - return true - return false - - func add_stock(amount: float): - """Replacement for Inventory.add_stock()""" - stock += amount - - func get_stock() -> float: - """Replacement for Inventory.get_stock()""" - return stock - - func spend_stock(amount: float) -> bool: - """Replacement for Inventory.spend_stock()""" - if stock >= amount: - stock -= amount - return true - return false - - func add_currency(amount: float): - """Replacement for Inventory.add_currency()""" - currency += amount - - func spend_currency(amount: float) -> bool: - """Replacement for Inventory.spend_currency()""" - if currency >= amount: - currency -= amount - return true - return false - - # ============================================================================= - # UNLOCK PURCHASE AND MODIFIER MANAGEMENT - # ============================================================================= - - func purchase_unlock(unlock_id: int) -> bool: - """Purchase an unlock and update modifiers""" - var unlock = get_unlock_by_id(unlock_id) - if not unlock or not unlock.can_rank_up(): - return false - - var cost = unlock.get_next_cost() - if not spend_currency(cost): - return false - - var prev_rank = unlock.current_rank - unlock.unlock() - current_ranks[unlock_id] = unlock.current_rank - - # Update modifier cache - update_modifiers_for_unlock(unlock, prev_rank) - - # Update special flags - if unlock_id == Global.wholesale_unlock_id: - wholesale_unlocked = true - if unlock_id == Global.multicraft_unlock_id: - multicraft_rank = unlock.current_rank - if unlock_id == Global.autowood_unlock_id: - # Autowood is additive, not multiplicative - autowood_mod = calculate_autowood_modifier() - - return true - - func update_modifiers_for_unlock(unlock, prev_rank: int): - """Update cached modifiers when unlock rank changes""" - var old_mods = unlock.get_modifiers_at_rank(prev_rank) - var new_mods = unlock.get_current_modifiers() - - # Calculate ratio for multiplicative modifiers - for key in new_mods.keys(): - var old_val = old_mods.get(key, 1.0) - var new_val = new_mods[key] - var ratio = new_val / old_val if old_val != 0 else new_val - - match key: - "sale_price_modifier": - sale_price_mod *= ratio - "efficiency_modifier": - efficiency_mod *= ratio - "wood_per_click_modifier": - wood_per_click_mod *= ratio - "purchase_rate_modifier": - purchase_rate_mod *= ratio - - func calculate_autowood_modifier() -> float: - """Calculate autowood modifier from scratch (additive)""" - var total = 0.0 - for unlock_id in current_ranks.keys(): - var rank = current_ranks[unlock_id] - if rank > 0: - var rank_mods = modifiers.get(unlock_id, {}) - var mods = rank_mods.get(rank, {}) - if mods.has("autowood_modifier"): - total += mods["autowood_modifier"] - return total - - # ============================================================================= - # INLINED TICK LOGIC (optimized for performance, matches TickProcess exactly) - # ============================================================================= - - func execute_tick(): - """Execute one game tick - INLINED for performance (no TickProcess overhead)""" - # 1. Generate wood from autowood - if autowood_mod > 0.0: - var wood_to_gather = max(get_wood_per_click() * autowood_mod, 1.0) - wood += wood_to_gather - - # 2. Whittle wood into stock - if wood >= 1: - # Base whittling action - var items_produced_per_tick = get_items_produced_per_tick() - var wood_needed = ceil(items_produced_per_tick) - var wood_to_whittle = min(wood, wood_needed) - var items_produced = wood_to_whittle - wood -= wood_to_whittle - stock += items_produced - - # Multicraft additional whittles - for i in range(multicraft_rank): - if wood >= 1: - wood_needed = ceil(items_produced_per_tick) - wood_to_whittle = min(wood, wood_needed) - items_produced = wood_to_whittle - wood -= wood_to_whittle - stock += items_produced - else: - break - - # 3. Sell stock for currency - if stock > 0: - var price_per_item = get_sale_price_per_item() - - # 3a. Wholesale selling (if unlocked) - if wholesale_unlocked: - while stock >= Global.wholesale_bundle_size: - stock -= Global.wholesale_bundle_size - currency += Global.wholesale_bundle_size * price_per_item * Global.wholesale_discount_multiplier - - # 3b. Regular selling - if stock > 0: - var purchase_rate = Global.base_purchase_rate * purchase_rate_mod - var max_stock_to_sell = floor(purchase_rate) - var actual_stock_to_sell = min(stock, max(1.0, max_stock_to_sell)) - stock -= actual_stock_to_sell - currency += actual_stock_to_sell * price_per_item - - ticks += 1 - - # ============================================================================= - # RESET FOR REUSE - # ============================================================================= - - func reset_for_new_simulation(): - """Reset state for a new simulation (OPTIMIZATION: reuse same IsolatedGameState)""" - # Reset inventory - currency = 0.0 - wood = 0.0 - stock = 0.0 - - # Reset modifiers - sale_price_mod = 1.0 - efficiency_mod = 1.0 - wood_per_click_mod = 1.0 - purchase_rate_mod = 1.0 - autowood_mod = 0.0 - multicraft_rank = 0 - wholesale_unlocked = false - - # Reset simulation state - ticks = 0 - current_ranks.clear() - - # ============================================================================= - # SNAPSHOT AND RESTORE FOR CACHING - # ============================================================================= - - func snapshot_for_cache() -> Dictionary: - """Create a snapshot for cache storage""" - return { - "ticks": ticks, - "currency": currency, - "stock": stock, - "wood": wood, - "current_ranks": current_ranks.duplicate(), - "modifiers": { - "sale_price_mod": sale_price_mod, - "efficiency_mod": efficiency_mod, - "wood_per_click_mod": wood_per_click_mod, - "purchase_rate_mod": purchase_rate_mod, - "autowood_mod": autowood_mod, - "multicraft_rank": multicraft_rank, - "wholesale_unlocked": wholesale_unlocked - } - } - - func restore_from_cache(snapshot: Dictionary): - """Restore state from a cache snapshot""" - ticks = snapshot.ticks - currency = snapshot.currency - stock = snapshot.stock - wood = snapshot.wood - - # Restore modifiers - var mods = snapshot.modifiers - sale_price_mod = mods.sale_price_mod - efficiency_mod = mods.efficiency_mod - wood_per_click_mod = mods.wood_per_click_mod - purchase_rate_mod = mods.purchase_rate_mod - autowood_mod = mods.autowood_mod - multicraft_rank = mods.multicraft_rank - wholesale_unlocked = mods.wholesale_unlocked - - # Restore unlock ranks - current_ranks = snapshot.current_ranks.duplicate() - for unlock_id in current_ranks.keys(): - var unlock = get_unlock_by_id(unlock_id) - if unlock: - var target_rank = current_ranks[unlock_id] - unlock.current_rank = target_rank - unlock.is_unlocked = target_rank > 0 - -# ============================================================================= -# TEMPLATE MANAGEMENT -# ============================================================================= - -func build_precalculated_tables(): - """Pre-calculate all costs and modifiers from real UnlockDataResource objects""" - precalc_mutex.lock() - if precalc_initialized: - precalc_mutex.unlock() - return - - print("Pre-calculating unlock tables from UnlockDataResource...") - var start_time = Time.get_ticks_msec() - - for unlock in unlock_collection.unlocks: - var unlock_id = unlock.unlock_id - precalc_unlock_names[unlock_id] = unlock.unlock_name - precalc_is_scaling[unlock_id] = unlock.is_scaling - precalc_max_ranks[unlock_id] = unlock.max_rank if unlock.is_scaling else 1 - precalc_base_modifiers[unlock_id] = unlock.base_modifiers.duplicate(true) - - # Calculate cost ladder for all possible ranks - var cost_array = [] - var max_rank_to_calc = unlock.max_rank if (unlock.is_scaling and unlock.max_rank > 0) else (100 if unlock.is_scaling else 1) - for rank in range(max_rank_to_calc + 1): - unlock.current_rank = rank - cost_array.append(unlock.get_next_cost()) - precalc_costs[unlock_id] = cost_array - - # Calculate modifier values for all possible ranks - var mods_by_rank = {} - for rank in range(max_rank_to_calc + 1): - unlock.current_rank = rank - unlock.is_unlocked = rank > 0 - mods_by_rank[rank] = unlock.get_current_modifiers() - precalc_modifiers[unlock_id] = mods_by_rank - - # Reset unlock state - unlock.current_rank = 0 - unlock.is_unlocked = false - - precalc_initialized = true - var elapsed = Time.get_ticks_msec() - start_time - print("Pre-calculation complete in %d ms for %d unlocks" % [elapsed, precalc_costs.size()]) - precalc_mutex.unlock() - -func create_isolated_state() -> IsolatedGameState: - """Create a new isolated game state using pre-calculated tables""" - if not precalc_initialized: - build_precalculated_tables() - return IsolatedGameState.new(precalc_costs, precalc_modifiers, precalc_max_ranks, precalc_is_scaling, precalc_base_modifiers) - -# ============================================================================= -# CACHE SYSTEM (same as sim_cached.gd) -# ============================================================================= - -func get_cache_key(current_ranks: Dictionary) -> String: - """Generate a cache key from current unlock ranks""" - var sorted_keys = current_ranks.keys() - sorted_keys.sort() - var key_parts = [] - for k in sorted_keys: - key_parts.append(str(k) + ":" + str(current_ranks[k])) - return ",".join(key_parts) - -func try_load_best_prefix_from_cache(rank_targets: Dictionary) -> Variant: - """Balanced cache lookup - fast with good coverage""" - cache_mutex.lock() - - # Try exact match first - var full_key = get_cache_key(rank_targets) - if simulation_cache.has(full_key): - cache_hits += 1 - var result = simulation_cache[full_key] - cache_mutex.unlock() - return result - - # Sort unlock IDs for consistent ordering - var unlock_ids = rank_targets.keys() - unlock_ids.sort() - var num_unlocks = unlock_ids.size() - - var best_match = null - var best_rank_sum = 0 - - # Try progressively shorter prefixes - for prefix_len in range(num_unlocks - 1, 0, -1): - var subset = {} - for i in range(prefix_len): - subset[unlock_ids[i]] = rank_targets[unlock_ids[i]] - - var key = get_cache_key(subset) - if simulation_cache.has(key): - var cached_entry = simulation_cache[key] - var rank_sum = 0 - for r in cached_entry.current_ranks.values(): - rank_sum += r - - if rank_sum > best_rank_sum: - best_match = cached_entry - best_rank_sum = rank_sum - if prefix_len >= num_unlocks - 2: - break - - if best_match != null: - cache_hits += 1 - else: - cache_misses += 1 - cache_mutex.unlock() - - return best_match - -func should_cache_state(current_ranks: Dictionary, targets_remaining: int) -> bool: - """Decide if this state is worth caching""" - if targets_remaining == 0: - return false - - var total_ranks = 0 - var active_unlocks = 0 - - for rank in current_ranks.values(): - if rank > 0: - total_ranks += rank - active_unlocks += 1 - - return (active_unlocks >= 2) or (total_ranks >= 2) - -# ============================================================================= -# MAIN SIMULATION FUNCTION -# ============================================================================= - -func simulate_rank_combination_direct( - rank_targets: Dictionary, - max_ticks: int, - track_purchases: bool = false, - _unused_tick_process = null, # Kept for API compatibility but not used - _unused_state = null # Kept for API compatibility but not used -) -> Dictionary: - """Pure simulation using isolated state with inlined tick logic""" - - # Always create fresh isolated state to avoid thread conflicts - var state = create_isolated_state() - - # Initialize targets - var targets_remaining = 0 - var active_unlock_ids: Array = [] - for unlock_id in rank_targets.keys(): - state.current_ranks[unlock_id] = 0 - targets_remaining += rank_targets[unlock_id] - active_unlock_ids.append(unlock_id) - - # Purchase tracking - var purchases: Array[Dictionary] = [] - - # Try cache restoration - var cached_state = null - if not track_purchases: - cached_state = try_load_best_prefix_from_cache(rank_targets) - - if cached_state != null: - state.restore_from_cache(cached_state) - - # Recalculate remaining targets - targets_remaining = 0 - active_unlock_ids.clear() - for unlock_id in rank_targets.keys(): - var remaining = rank_targets[unlock_id] - state.current_ranks.get(unlock_id, 0) - if remaining > 0: - targets_remaining += remaining - active_unlock_ids.append(unlock_id) - - # Pre-calculate next costs directly from lookup table - var next_costs: Array[float] = [] - next_costs.resize(active_unlock_ids.size()) - for i in range(active_unlock_ids.size()): - var unlock_id = active_unlock_ids[i] - var current_rank = state.current_ranks.get(unlock_id, 0) - var cost_array = state.costs.get(unlock_id, []) - next_costs[i] = cost_array[current_rank] if current_rank < cost_array.size() else 999999999 - - # Main simulation loop - while state.ticks < max_ticks: - # Find cheapest affordable unlock - var cheapest_index = -1 - var cheapest_cost = INF - var cheapest_unlock_id = -1 - - if targets_remaining > 0: - for i in range(active_unlock_ids.size()): - if next_costs[i] < cheapest_cost and state.currency >= next_costs[i]: - cheapest_cost = next_costs[i] - cheapest_unlock_id = active_unlock_ids[i] - cheapest_index = i - - # Exit early if all targets met and goal reached - if cheapest_index == -1 and targets_remaining == 0: - if state.currency >= 1000000.0: - break - # Skip ahead to 1M - var currency_needed = 1000000.0 - state.currency - var price_per_item = state.get_sale_price_per_item() - var items_per_tick = max(1.0, floor( - Global.base_purchase_rate * state.get_modifier_value("purchase_rate_modifier") - )) - var revenue_per_tick = items_per_tick * price_per_item - - if revenue_per_tick > 0: - var ticks_needed = int(ceil(currency_needed / revenue_per_tick)) - state.ticks += ticks_needed - state.currency += revenue_per_tick * ticks_needed - break - - # Purchase unlock if affordable - if cheapest_index != -1: - state.purchase_unlock(cheapest_unlock_id) - targets_remaining -= 1 - - # Track purchase if enabled - if track_purchases: - var current_rank = state.current_ranks[cheapest_unlock_id] - purchases.append({ - "unlock_id": cheapest_unlock_id, - "unlock_name": get_unlock_name_by_id(cheapest_unlock_id), - "rank": current_rank, - "cost": cheapest_cost, - "tick": state.ticks, - "currency_after": state.currency - }) - - # Update next cost or remove from active list - if state.current_ranks[cheapest_unlock_id] >= rank_targets[cheapest_unlock_id]: - # Target reached - var last_idx = active_unlock_ids.size() - 1 - if cheapest_index != last_idx: - active_unlock_ids[cheapest_index] = active_unlock_ids[last_idx] - next_costs[cheapest_index] = next_costs[last_idx] - active_unlock_ids.resize(last_idx) - next_costs.resize(last_idx) - else: - # Update cost for next rank directly from lookup table - var current_rank = state.current_ranks[cheapest_unlock_id] - var cost_array = state.costs.get(cheapest_unlock_id, []) - next_costs[cheapest_index] = cost_array[current_rank] if current_rank < cost_array.size() else 999999999 - - # Cache this state if valuable - if should_cache_state(state.current_ranks, targets_remaining): - var cache_key = get_cache_key(state.current_ranks) - cache_mutex.lock() - if not simulation_cache.has(cache_key): - simulation_cache[cache_key] = state.snapshot_for_cache() - cache_mutex.unlock() - - # Simulate manual clicking to bootstrap economy (matches sim_cached.gd logic) - # Manual clicks based on tick range (pre-calculate to avoid repeated conditions) - var manual_clicks: float = 1.0 if state.ticks < 120 else (0.5 if state.ticks < 300 else (0.25 if (state.ticks < 600 and state.autowood_mod < 0.2) else 0.0)) - if manual_clicks > 0.0: - var wood_from_clicks = manual_clicks * state.get_wood_per_click() - state.add_wood(wood_from_clicks) - - # Execute one tick using inlined logic (optimized for performance) - state.execute_tick() - - # Build result - var success = state.currency >= 1000000.0 - var result = { - "rank_targets": rank_targets, - "success": success, - "ticks": state.ticks if success else -1, - "final_currency": state.currency, - "time_formatted": format_time(state.ticks) if success else "Failed" - } - - if track_purchases: - result["purchases"] = purchases - - return result - -# ============================================================================= -# HELPER FUNCTIONS -# ============================================================================= - -func format_time(ticks: int) -> String: - var seconds = ticks - var minutes = seconds / 60 - var hours = minutes / 60 - - if hours > 0: - return "%dh %dm %ds" % [hours, minutes % 60, seconds % 60] - elif minutes > 0: - return "%dm %ds" % [minutes, seconds % 60] - else: - return "%ds" % seconds - -func generate_all_combinations(unlimited_scaling_cap: int = 5) -> Array[Dictionary]: - """Generate combinations for ALL unlocks dynamically""" - var combinations: Array[Dictionary] = [] - - var unlock_constraints = [] - for unlock in unlock_collection.unlocks: - var max_rank: int - if unlock.max_rank > 0: - max_rank = unlock.max_rank - elif not unlock.is_scaling: - max_rank = 1 - else: - max_rank = unlimited_scaling_cap - - unlock_constraints.append({ - "id": unlock.unlock_id, - "name": unlock.unlock_name, - "max_rank": max_rank - }) - - print("\n=== Generating Combinations ===") - print("Reading from resource file: %d unlocks" % unlock_constraints.size()) - for c in unlock_constraints: - print(" - %s (ID %d): 0-%d ranks" % [c.name, c.id, c.max_rank]) - - _generate_combinations_recursive(unlock_constraints, 0, {}, combinations) - - print("Generated %d total combinations" % combinations.size()) - return combinations - -func _generate_combinations_recursive(constraints: Array, index: int, current: Dictionary, output: Array): - """Recursively generate all valid combinations""" - if index >= constraints.size(): - if current.size() > 0: - output.append(current.duplicate()) - return - - var constraint = constraints[index] - for rank in range(constraint.max_rank + 1): - if rank > 0: - current[constraint.id] = rank - - _generate_combinations_recursive(constraints, index + 1, current, output) - - if rank > 0: - current.erase(constraint.id) - -func get_unlock_name_by_id(unlock_id: int) -> String: - """Helper function to get unlock name by ID""" - if not precalc_initialized: - build_precalculated_tables() - return precalc_unlock_names.get(unlock_id, "Unknown") - -# ============================================================================= -# THREADING AND PROGRESS -# ============================================================================= - -func worker_thread(thread_id: int): - """Worker thread function""" - var local_results: Array[Dictionary] = [] - var batch_size: int = 10 - - while true: - var task_data = null - queue_mutex.lock() - if task_queue.size() > 0: - task_data = task_queue.pop_front() - queue_mutex.unlock() - - if task_data == null: - if local_results.size() > 0: - results_mutex.lock() - all_results.append_array(local_results) - results_mutex.unlock() - - completed_mutex.lock() - completed_count += local_results.size() - completed_mutex.unlock() - break - - var result = simulate_rank_combination_direct(task_data.combo, 1000000, false, null, null) - - local_results.append(result) - - if local_results.size() >= batch_size: - results_mutex.lock() - all_results.append_array(local_results) - results_mutex.unlock() - - completed_mutex.lock() - completed_count += local_results.size() - completed_mutex.unlock() - - local_results.clear() - -func update_progress(): - """Update progress display""" - var current_count = 0 - completed_mutex.lock() - current_count = completed_count - completed_mutex.unlock() - - if current_count >= total_combinations: - monitoring_active = false - finish_processing() - return - - var percent = float(current_count) / total_combinations * 100.0 - var elapsed = (Time.get_ticks_msec() - start_time) / 1000.0 - var rate = current_count / elapsed if elapsed > 0 else 0 - var eta_seconds = (total_combinations - current_count) / rate if rate > 0 else 0 - - var total_cache_checks = cache_hits + cache_misses - var cache_hit_rate = (float(cache_hits) / total_cache_checks * 100.0) if total_cache_checks > 0 else 0.0 - - var eta_str = "" - if eta_seconds > 0: - var eta_minutes = int(eta_seconds) / 60 - var eta_secs = int(eta_seconds) % 60 - if eta_minutes > 0: - eta_str = "%dm %ds" % [eta_minutes, eta_secs] - else: - eta_str = "%ds" % eta_secs - else: - eta_str = "calculating..." - - print("Progress: %.1f%% (%d/%d) - %.1f combos/sec - Cache: %.1f%% hits - ETA: %s" % [ - percent, current_count, total_combinations, rate, cache_hit_rate, eta_str - ]) - - status_label.text = "Status: Running simulation..." - progress_label.text = "Progress: %.1f%% (%d/%d)" % [percent, current_count, total_combinations] - progress_bar.value = percent / 100.0 - rate_label.text = "Speed: %.1f combos/sec" % rate - eta_label.text = "ETA: %s" % eta_str - - cache_hits_label.text = "Cache Hits: %d" % cache_hits - cache_misses_label.text = "Cache Misses: %d" % cache_misses - cache_rate_label.text = "Hit Rate: %.1f%%" % cache_hit_rate - cache_mutex.lock() - cache_size_label.text = "Cache Entries: %d" % simulation_cache.size() - cache_mutex.unlock() - -func finish_processing(): - """Called when all processing is complete""" - print("\nAll combinations complete! Waiting for threads to finish...") - - for thread in threads: - thread.wait_to_finish() - threads.clear() - threads_done = true - - print("All threads finished. Processing results...") - - var total_time = (Time.get_ticks_msec() - start_time) / 1000.0 - - results_mutex.lock() - var actual_results = all_results.size() - results_mutex.unlock() - - if actual_results != total_combinations: - print("WARNING: Result count mismatch! Expected %d, got %d" % [total_combinations, actual_results]) - - print("\n=== RESULTS ===") - print("Total time: %.1f seconds" % total_time) - print("Total combinations tested: %d" % actual_results) - - var total_cache_checks = cache_hits + cache_misses - var cache_hit_rate = (float(cache_hits) / total_cache_checks * 100.0) if total_cache_checks > 0 else 0.0 - cache_mutex.lock() - var cache_size = simulation_cache.size() - cache_mutex.unlock() - print("\n=== CACHE STATISTICS ===") - print("Cache hits: %d" % cache_hits) - print("Cache misses: %d" % cache_misses) - print("Hit rate: %.1f%%" % cache_hit_rate) - print("Cache entries stored: %d" % cache_size) - - var successful = all_results.filter(func(r): return r.success) - print("Successful strategies: %d" % successful.size()) - - status_label.text = "Status: Complete!" - progress_label.text = "Progress: 100%% (%d/%d)" % [all_results.size(), total_combinations] - progress_bar.value = 1.0 - eta_label.text = "Total Time: %.1f seconds" % total_time - - var results_text = "[b]SIMULATION COMPLETE[/b]\n\n" - results_text += "[color=green]Total time: %.1f seconds[/color]\n" % total_time - results_text += "Combinations tested: %d\n" % all_results.size() - results_text += "Successful strategies: %d\n\n" % successful.size() - - results_text += "[b]Cache Performance:[/b]\n" - results_text += " Hits: %d\n" % cache_hits - results_text += " Misses: %d\n" % cache_misses - results_text += " [color=cyan]Hit Rate: %.1f%%[/color]\n" % cache_hit_rate - results_text += " Entries: %d\n\n" % cache_size - - if successful.size() > 0: - successful.sort_custom(func(a, b): return a.ticks < b.ticks) - - print("\n=== RE-SIMULATING TOP 10 WITH PURCHASE TRACKING ===") - var top_10_detailed: Array = [] - - for i in range(min(10, successful.size())): - var result = successful[i] - print("Re-simulating #%d..." % (i + 1)) - var detailed_result = simulate_rank_combination_direct(result.rank_targets, 1000000, true) - top_10_detailed.append(detailed_result) - - print("\n=== TOP 10 FASTEST STRATEGIES (WITH PURCHASE TIMELINE) ===") - results_text += "[b]TOP 10 FASTEST STRATEGIES:[/b]\n\n" - - for i in range(top_10_detailed.size()): - var result = top_10_detailed[i] - print("\n#%d: %s (%d ticks)" % [i + 1, result.time_formatted, result.ticks]) - - var rank_display = [] - for unlock_id in result.rank_targets.keys(): - var unlock_name = get_unlock_name_by_id(unlock_id) - var ranks = result.rank_targets[unlock_id] - rank_display.append("%s: %d" % [unlock_name, ranks]) - print("Target Ranks: %s" % ", ".join(rank_display)) - - results_text += "[color=yellow]#%d: %s (%d ticks)[/color]\n" % [i + 1, result.time_formatted, result.ticks] - results_text += " Ranks: %s\n" % ", ".join(rank_display) - results_text += " Currency: %.0f\n" % result.final_currency - - if result.has("purchases") and result.purchases.size() > 0: - print("\nPurchase Timeline:") - results_text += " [b]Purchase Timeline:[/b]\n" - for purchase in result.purchases: - var time_str = format_time(purchase.tick) - print(" %s: %s Rank %d - Cost: %d¥ @ %s" % [ - time_str, purchase.unlock_name, purchase.rank, - purchase.cost, time_str - ]) - results_text += " • %s [color=cyan]%s Rank %d[/color] - %d¥ @ %s\n" % [ - format_time(purchase.tick), purchase.unlock_name, purchase.rank, - purchase.cost, time_str - ] - results_text += "\n" - else: - print("\nNo successful strategies found!") - results_text += "[color=red]No successful strategies found![/color]\n" - - results_label.text = results_text - -func run_comprehensive_test(): - """Test all combinations dynamically generated from resource file""" - print("\n=== Available Unlocks ===") - for unlock in unlock_collection.unlocks: - var max_rank_str = str(unlock.max_rank) if unlock.max_rank > 0 else "unlimited" - print("ID: %d | %s | Base Cost: %d | Scaling: %s | Max Rank: %s" % [ - unlock.unlock_id, - unlock.unlock_name, - unlock.base_cost, - "Yes" if unlock.is_scaling else "No", - max_rank_str - ]) - print(" Modifiers: ", unlock.base_modifiers) - - print("\n=== Global Constants ===") - print("Base Sale Price: %s" % Global.base_sale_price) - print("Base Purchase Rate: %s" % Global.base_purchase_rate) - print("Cost Per Whittle: %s" % Global.cost_per_whittle) - - # Build pre-calculated tables - build_precalculated_tables() - - # Generate combinations - var unlimited_cap = 5 - print("\n=== Generation Settings ===") - print("Unlimited scaling cap: %d ranks" % unlimited_cap) - var combinations = generate_all_combinations(unlimited_cap) - total_combinations = combinations.size() - print("\n=== Testing %d Combinations ===" % total_combinations) - - # Fill task queue - task_queue.clear() - for combo in combinations: - task_queue.append({"combo": combo}) - - # Reset counters - completed_count = 0 - all_results.clear() - threads_done = false - start_time = Time.get_ticks_msec() - last_progress_time = start_time - monitoring_active = true - - # Create and start threads - print("Starting %d worker threads..." % num_threads) - for i in range(num_threads): - var thread = Thread.new() - thread.start(worker_thread.bind(i)) - threads.append(thread) - - print("All threads started, processing...") - -func _exit_tree(): - monitoring_active = false - for thread in threads: - if thread.is_alive(): - thread.wait_to_finish() diff --git a/scripts/sim_direct.gd.uid b/scripts/sim_direct.gd.uid deleted file mode 100644 index e374a83..0000000 --- a/scripts/sim_direct.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://citjokiv6skqi diff --git a/scripts/unlock_data_lightweight.gd b/scripts/unlock_data_lightweight.gd deleted file mode 100644 index 0aeca12..0000000 --- a/scripts/unlock_data_lightweight.gd +++ /dev/null @@ -1,124 +0,0 @@ -class_name UnlockDataLightweight -## Lightweight unlock data structure for simulations -## Contains the same calculation logic as UnlockDataResource but without Resource overhead - -var unlock_id: int = 0 -var unlock_name: String = "" -var base_cost: int = 0 -var is_unlocked: bool = false - -# Scaling settings -var is_scaling: bool = false -var current_rank: int = 0 -var max_rank: int = -1 - -# Cost scaling -var cost_scaling_type: int = 1 # 0=Linear, 1=Exponential -var cost_scaling_multiplier: float = 1.5 -var cost_linear_increase: int = 100 -var cost_ladder: Array[int] = [] - -# Effect ladder -var effect_ladder: Array[float] = [] - -# Base modifiers -var base_modifiers: Dictionary = {} - -## Static factory method to create from UnlockDataResource (one-time conversion) -static func from_resource(resource: UnlockDataResource) -> UnlockDataLightweight: - var data = UnlockDataLightweight.new() - data.unlock_id = resource.unlock_id - data.unlock_name = resource.unlock_name - data.base_cost = resource.base_cost - data.is_scaling = resource.is_scaling - data.max_rank = resource.max_rank - data.cost_scaling_type = resource.cost_scaling_type - data.cost_scaling_multiplier = resource.cost_scaling_multiplier - data.cost_linear_increase = resource.cost_linear_increase - data.cost_ladder = resource.cost_ladder.duplicate() - data.effect_ladder = resource.effect_ladder.duplicate() - data.base_modifiers = resource.base_modifiers.duplicate(true) - # Start fresh - data.is_unlocked = false - data.current_rank = 0 - return data - -## Clone for thread safety (fast - no Resource creation) -func clone() -> UnlockDataLightweight: - var copy = UnlockDataLightweight.new() - copy.unlock_id = unlock_id - copy.unlock_name = unlock_name - copy.base_cost = base_cost - copy.is_scaling = is_scaling - copy.max_rank = max_rank - copy.cost_scaling_type = cost_scaling_type - copy.cost_scaling_multiplier = cost_scaling_multiplier - copy.cost_linear_increase = cost_linear_increase - copy.cost_ladder = cost_ladder # Shared - read-only - copy.effect_ladder = effect_ladder # Shared - read-only - copy.base_modifiers = base_modifiers # Shared - read-only - # Mutable state - copy.is_unlocked = false - copy.current_rank = 0 - return copy - -## Same logic as UnlockDataResource.get_next_cost() -func get_next_cost() -> int: - if not is_scaling: - return base_cost - - if cost_ladder.size() > 0 and current_rank < cost_ladder.size(): - return cost_ladder[current_rank] - - if cost_scaling_type == 0: # Linear - return base_cost + (cost_linear_increase * current_rank) - else: # Exponential - return int(base_cost * pow(cost_scaling_multiplier, current_rank)) - -## Same logic as UnlockDataResource.get_current_modifiers() -func get_current_modifiers() -> Dictionary: - if not is_unlocked or current_rank == 0: - return {} - return get_modifiers_at_rank(current_rank) - -## Same logic as UnlockDataResource.get_modifiers_at_rank() -func get_modifiers_at_rank(rank: int) -> Dictionary: - if rank == 0: - return {} - - # For one-shot unlocks or empty ladder, return base_modifiers - if effect_ladder.size() == 0: - return base_modifiers.duplicate() - - var ladder_index = rank - 1 - if ladder_index >= effect_ladder.size(): - ladder_index = effect_ladder.size() - 1 # Use last value if rank exceeds ladder - - var result = {} - for key in base_modifiers.keys(): - result[key] = effect_ladder[ladder_index] - return result - -## Same logic as UnlockDataResource.can_rank_up() -func can_rank_up() -> bool: - if not is_scaling: - return not is_unlocked - - if max_rank > 0 and current_rank >= max_rank: - return false - - return true - -## Same logic as UnlockDataResource.unlock() -func unlock() -> bool: - if not can_rank_up(): - return false - - if not is_scaling: - is_unlocked = true - current_rank = 1 - else: - current_rank += 1 - is_unlocked = true - - return true diff --git a/scripts/unlock_data_lightweight.gd.uid b/scripts/unlock_data_lightweight.gd.uid deleted file mode 100644 index 3336c42..0000000 --- a/scripts/unlock_data_lightweight.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://yx6cnoob2can