diff --git a/docs/previews/heroes-villains/boss_agent_army.png b/docs/previews/heroes-villains/boss_agent_army.png index 4b6e9d1e..df949e14 100644 Binary files a/docs/previews/heroes-villains/boss_agent_army.png and b/docs/previews/heroes-villains/boss_agent_army.png differ diff --git a/docs/previews/heroes-villains/boss_ai_augmented_hacker.png b/docs/previews/heroes-villains/boss_ai_augmented_hacker.png index e9ba55fb..ab57a6b5 100644 Binary files a/docs/previews/heroes-villains/boss_ai_augmented_hacker.png and b/docs/previews/heroes-villains/boss_ai_augmented_hacker.png differ diff --git a/docs/previews/heroes-villains/boss_ai_slop.png b/docs/previews/heroes-villains/boss_ai_slop.png index f384bddf..52a50efa 100644 Binary files a/docs/previews/heroes-villains/boss_ai_slop.png and b/docs/previews/heroes-villains/boss_ai_slop.png differ diff --git a/docs/previews/heroes-villains/boss_ten_thousand_line_pr.png b/docs/previews/heroes-villains/boss_ten_thousand_line_pr.png index ed224849..8394f96a 100644 Binary files a/docs/previews/heroes-villains/boss_ten_thousand_line_pr.png and b/docs/previews/heroes-villains/boss_ten_thousand_line_pr.png differ diff --git a/docs/previews/heroes-villains/ending.png b/docs/previews/heroes-villains/ending.png index 6da65291..3f334c49 100644 Binary files a/docs/previews/heroes-villains/ending.png and b/docs/previews/heroes-villains/ending.png differ diff --git a/docs/previews/heroes-villains/ending_hacker_falls.png b/docs/previews/heroes-villains/ending_hacker_falls.png index 17363406..eaa097a7 100644 Binary files a/docs/previews/heroes-villains/ending_hacker_falls.png and b/docs/previews/heroes-villains/ending_hacker_falls.png differ diff --git a/docs/previews/heroes-villains/ending_rabbit_stands.png b/docs/previews/heroes-villains/ending_rabbit_stands.png index 4af8a53b..fbed0210 100644 Binary files a/docs/previews/heroes-villains/ending_rabbit_stands.png and b/docs/previews/heroes-villains/ending_rabbit_stands.png differ diff --git a/docs/previews/heroes-villains/ending_trust_restored.png b/docs/previews/heroes-villains/ending_trust_restored.png index 6da65291..3f334c49 100644 Binary files a/docs/previews/heroes-villains/ending_trust_restored.png and b/docs/previews/heroes-villains/ending_trust_restored.png differ diff --git a/docs/previews/heroes-villains/fight_agent_army_phase_1.png b/docs/previews/heroes-villains/fight_agent_army_phase_1.png index 80a80405..373f1d24 100644 Binary files a/docs/previews/heroes-villains/fight_agent_army_phase_1.png and b/docs/previews/heroes-villains/fight_agent_army_phase_1.png differ diff --git a/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_1.png b/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_1.png index cf04349f..8e31976d 100644 Binary files a/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_1.png and b/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_1.png differ diff --git a/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_2.png b/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_2.png index 44791496..280747a2 100644 Binary files a/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_2.png and b/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_2.png differ diff --git a/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_3.png b/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_3.png index 220d65c9..a53c84bf 100644 Binary files a/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_3.png and b/docs/previews/heroes-villains/fight_ai_augmented_hacker_phase_3.png differ diff --git a/docs/previews/heroes-villains/fight_ai_slop_phase_1.png b/docs/previews/heroes-villains/fight_ai_slop_phase_1.png index 033d64cb..6211dbde 100644 Binary files a/docs/previews/heroes-villains/fight_ai_slop_phase_1.png and b/docs/previews/heroes-villains/fight_ai_slop_phase_1.png differ diff --git a/docs/previews/heroes-villains/fight_ai_slop_phase_2.png b/docs/previews/heroes-villains/fight_ai_slop_phase_2.png index edbbb976..23ab6912 100644 Binary files a/docs/previews/heroes-villains/fight_ai_slop_phase_2.png and b/docs/previews/heroes-villains/fight_ai_slop_phase_2.png differ diff --git a/docs/previews/heroes-villains/fight_ai_slop_phase_3.png b/docs/previews/heroes-villains/fight_ai_slop_phase_3.png index 0af4d30a..f01e88a9 100644 Binary files a/docs/previews/heroes-villains/fight_ai_slop_phase_3.png and b/docs/previews/heroes-villains/fight_ai_slop_phase_3.png differ diff --git a/docs/previews/heroes-villains/fight_ten_thousand_line_pr_phase_1.png b/docs/previews/heroes-villains/fight_ten_thousand_line_pr_phase_1.png index 7fac0abb..19fda918 100644 Binary files a/docs/previews/heroes-villains/fight_ten_thousand_line_pr_phase_1.png and b/docs/previews/heroes-villains/fight_ten_thousand_line_pr_phase_1.png differ diff --git a/docs/previews/heroes-villains/hoppy_victory_0.png b/docs/previews/heroes-villains/hoppy_victory_0.png index 728ec7d2..b0074f58 100644 Binary files a/docs/previews/heroes-villains/hoppy_victory_0.png and b/docs/previews/heroes-villains/hoppy_victory_0.png differ diff --git a/docs/previews/heroes-villains/hoppy_victory_1.png b/docs/previews/heroes-villains/hoppy_victory_1.png index d0049e45..fb76d9be 100644 Binary files a/docs/previews/heroes-villains/hoppy_victory_1.png and b/docs/previews/heroes-villains/hoppy_victory_1.png differ diff --git a/docs/previews/heroes-villains/hoppy_victory_2.png b/docs/previews/heroes-villains/hoppy_victory_2.png index 4f956705..63844cd0 100644 Binary files a/docs/previews/heroes-villains/hoppy_victory_2.png and b/docs/previews/heroes-villains/hoppy_victory_2.png differ diff --git a/docs/previews/heroes-villains/minimap.png b/docs/previews/heroes-villains/minimap.png index d2013d91..7e659a33 100644 Binary files a/docs/previews/heroes-villains/minimap.png and b/docs/previews/heroes-villains/minimap.png differ diff --git a/docs/previews/heroes-villains/minimap_boss.png b/docs/previews/heroes-villains/minimap_boss.png index d1b917dc..d8ddd6b3 100644 Binary files a/docs/previews/heroes-villains/minimap_boss.png and b/docs/previews/heroes-villains/minimap_boss.png differ diff --git a/docs/previews/heroes-villains/minimap_boss_dual.png b/docs/previews/heroes-villains/minimap_boss_dual.png index cc075982..e15f5fcc 100644 Binary files a/docs/previews/heroes-villains/minimap_boss_dual.png and b/docs/previews/heroes-villains/minimap_boss_dual.png differ diff --git a/docs/previews/heroes-villains/mode_select.png b/docs/previews/heroes-villains/mode_select.png index 3d110314..2efe1eae 100644 Binary files a/docs/previews/heroes-villains/mode_select.png and b/docs/previews/heroes-villains/mode_select.png differ diff --git a/docs/previews/heroes-villains/opening.png b/docs/previews/heroes-villains/opening.png index dafbd536..aa58d4b0 100644 Binary files a/docs/previews/heroes-villains/opening.png and b/docs/previews/heroes-villains/opening.png differ diff --git a/docs/previews/heroes-villains/opening_complication.png b/docs/previews/heroes-villains/opening_complication.png index a589d0a0..aef9c0c0 100644 Binary files a/docs/previews/heroes-villains/opening_complication.png and b/docs/previews/heroes-villains/opening_complication.png differ diff --git a/docs/previews/heroes-villains/opening_resolution.png b/docs/previews/heroes-villains/opening_resolution.png index cf0d74cf..b35617f2 100644 Binary files a/docs/previews/heroes-villains/opening_resolution.png and b/docs/previews/heroes-villains/opening_resolution.png differ diff --git a/docs/previews/heroes-villains/opening_situation.png b/docs/previews/heroes-villains/opening_situation.png index 5043b2ba..76c4c2d3 100644 Binary files a/docs/previews/heroes-villains/opening_situation.png and b/docs/previews/heroes-villains/opening_situation.png differ diff --git a/docs/previews/heroes-villains/stage_1_room_1.png b/docs/previews/heroes-villains/stage_1_room_1.png index 657d754d..9dc34b0e 100644 Binary files a/docs/previews/heroes-villains/stage_1_room_1.png and b/docs/previews/heroes-villains/stage_1_room_1.png differ diff --git a/docs/previews/heroes-villains/stage_1_room_2.png b/docs/previews/heroes-villains/stage_1_room_2.png index 4c64ed5c..2231116d 100644 Binary files a/docs/previews/heroes-villains/stage_1_room_2.png and b/docs/previews/heroes-villains/stage_1_room_2.png differ diff --git a/docs/previews/heroes-villains/stage_1_room_3.png b/docs/previews/heroes-villains/stage_1_room_3.png index 033d64cb..8417acf3 100644 Binary files a/docs/previews/heroes-villains/stage_1_room_3.png and b/docs/previews/heroes-villains/stage_1_room_3.png differ diff --git a/docs/previews/heroes-villains/stage_2_room_1.png b/docs/previews/heroes-villains/stage_2_room_1.png index f076aec4..f7ee03dd 100644 Binary files a/docs/previews/heroes-villains/stage_2_room_1.png and b/docs/previews/heroes-villains/stage_2_room_1.png differ diff --git a/docs/previews/heroes-villains/stage_2_room_2.png b/docs/previews/heroes-villains/stage_2_room_2.png index 185f92dd..4f5376c0 100644 Binary files a/docs/previews/heroes-villains/stage_2_room_2.png and b/docs/previews/heroes-villains/stage_2_room_2.png differ diff --git a/docs/previews/heroes-villains/stage_2_room_3.png b/docs/previews/heroes-villains/stage_2_room_3.png index 1cfcfddf..373f1d24 100644 Binary files a/docs/previews/heroes-villains/stage_2_room_3.png and b/docs/previews/heroes-villains/stage_2_room_3.png differ diff --git a/docs/previews/heroes-villains/stage_3_room_1.png b/docs/previews/heroes-villains/stage_3_room_1.png index 0c59bdac..496b8eda 100644 Binary files a/docs/previews/heroes-villains/stage_3_room_1.png and b/docs/previews/heroes-villains/stage_3_room_1.png differ diff --git a/docs/previews/heroes-villains/stage_3_room_2.png b/docs/previews/heroes-villains/stage_3_room_2.png index 421e5444..c62033f5 100644 Binary files a/docs/previews/heroes-villains/stage_3_room_2.png and b/docs/previews/heroes-villains/stage_3_room_2.png differ diff --git a/docs/previews/heroes-villains/stage_3_room_3.png b/docs/previews/heroes-villains/stage_3_room_3.png index cf04349f..8e31976d 100644 Binary files a/docs/previews/heroes-villains/stage_3_room_3.png and b/docs/previews/heroes-villains/stage_3_room_3.png differ diff --git a/docs/previews/heroes-villains/stage_enter_banner_0.png b/docs/previews/heroes-villains/stage_enter_banner_0.png index 45d895d5..34663cdc 100644 Binary files a/docs/previews/heroes-villains/stage_enter_banner_0.png and b/docs/previews/heroes-villains/stage_enter_banner_0.png differ diff --git a/docs/previews/heroes-villains/stage_enter_banner_1.png b/docs/previews/heroes-villains/stage_enter_banner_1.png index db9c7499..d113586a 100644 Binary files a/docs/previews/heroes-villains/stage_enter_banner_1.png and b/docs/previews/heroes-villains/stage_enter_banner_1.png differ diff --git a/docs/previews/heroes-villains/stage_enter_banner_2.png b/docs/previews/heroes-villains/stage_enter_banner_2.png index f71a4fe0..1268d196 100644 Binary files a/docs/previews/heroes-villains/stage_enter_banner_2.png and b/docs/previews/heroes-villains/stage_enter_banner_2.png differ diff --git a/docs/previews/heroes-villains/stage_exit_fade_0.png b/docs/previews/heroes-villains/stage_exit_fade_0.png index 86c9ff44..b0705148 100644 Binary files a/docs/previews/heroes-villains/stage_exit_fade_0.png and b/docs/previews/heroes-villains/stage_exit_fade_0.png differ diff --git a/docs/previews/heroes-villains/stage_exit_fade_1.png b/docs/previews/heroes-villains/stage_exit_fade_1.png index 258cbe52..84ca5a32 100644 Binary files a/docs/previews/heroes-villains/stage_exit_fade_1.png and b/docs/previews/heroes-villains/stage_exit_fade_1.png differ diff --git a/docs/previews/heroes-villains/unlock_change_stack.png b/docs/previews/heroes-villains/unlock_change_stack.png index 8e96edf6..e9e4f8f0 100644 Binary files a/docs/previews/heroes-villains/unlock_change_stack.png and b/docs/previews/heroes-villains/unlock_change_stack.png differ diff --git a/docs/previews/heroes-villains/unlock_review.png b/docs/previews/heroes-villains/unlock_review.png index cc774c41..cec9711e 100644 Binary files a/docs/previews/heroes-villains/unlock_review.png and b/docs/previews/heroes-villains/unlock_review.png differ diff --git a/docs/previews/heroes-villains/unlock_security.png b/docs/previews/heroes-villains/unlock_security.png index ef6be109..c735dae1 100644 Binary files a/docs/previews/heroes-villains/unlock_security.png and b/docs/previews/heroes-villains/unlock_security.png differ diff --git a/docs/previews/heroes-villains/unlock_triage.png b/docs/previews/heroes-villains/unlock_triage.png index 689b46dc..d1efceee 100644 Binary files a/docs/previews/heroes-villains/unlock_triage.png and b/docs/previews/heroes-villains/unlock_triage.png differ diff --git a/docs/previews/heroes-villains/victory.png b/docs/previews/heroes-villains/victory.png index 8eb6ac75..ca140585 100644 Binary files a/docs/previews/heroes-villains/victory.png and b/docs/previews/heroes-villains/victory.png differ diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.png b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.png new file mode 100644 index 00000000..33bc5164 Binary files /dev/null and b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.png differ diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.png.import b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.png.import new file mode 100644 index 00000000..58af60f8 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://b2hlxtv3bl2sk" +path="res://.godot/imported/change_stack_baton.png-70356cb4c1c8036127a00e1224e6d7bf.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.png" +dest_files=["res://.godot/imported/change_stack_baton.png-70356cb4c1c8036127a00e1224e6d7bf.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/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.tres b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.tres new file mode 100644 index 00000000..92d6f626 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.tres @@ -0,0 +1,78 @@ +[gd_resource type="SpriteFrames" load_steps=18 format=3] + +[ext_resource type="Texture2D" path="res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/change_stack_baton.png" id="1"] + +[sub_resource type="AtlasTexture" id="Frame_0"] +atlas = ExtResource("1") +region = Rect2(0, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_1"] +atlas = ExtResource("1") +region = Rect2(256, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_2"] +atlas = ExtResource("1") +region = Rect2(512, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_3"] +atlas = ExtResource("1") +region = Rect2(768, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_4"] +atlas = ExtResource("1") +region = Rect2(0, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_5"] +atlas = ExtResource("1") +region = Rect2(256, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_6"] +atlas = ExtResource("1") +region = Rect2(512, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_7"] +atlas = ExtResource("1") +region = Rect2(768, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_8"] +atlas = ExtResource("1") +region = Rect2(0, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_9"] +atlas = ExtResource("1") +region = Rect2(256, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_10"] +atlas = ExtResource("1") +region = Rect2(512, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_11"] +atlas = ExtResource("1") +region = Rect2(768, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_12"] +atlas = ExtResource("1") +region = Rect2(0, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_13"] +atlas = ExtResource("1") +region = Rect2(256, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_14"] +atlas = ExtResource("1") +region = Rect2(512, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_15"] +atlas = ExtResource("1") +region = Rect2(768, 480, 256, 160) + +[resource] +animations = [ +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_0")}], "loop": true, "name": &"hold_idle", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_1")}, {"duration": 1.0, "texture": SubResource("Frame_2")}, {"duration": 1.0, "texture": SubResource("Frame_3")}, {"duration": 1.0, "texture": SubResource("Frame_4")}], "loop": true, "name": &"run", "speed": 7.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_5")}, {"duration": 1.0, "texture": SubResource("Frame_6")}, {"duration": 1.0, "texture": SubResource("Frame_7")}], "loop": false, "name": &"step1", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_8")}, {"duration": 1.0, "texture": SubResource("Frame_9")}, {"duration": 1.0, "texture": SubResource("Frame_10")}], "loop": false, "name": &"step2", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_11")}, {"duration": 1.0, "texture": SubResource("Frame_12")}, {"duration": 1.0, "texture": SubResource("Frame_13")}], "loop": false, "name": &"step3", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_14")}], "loop": false, "name": &"equip", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_15")}], "loop": false, "name": &"hurt", "speed": 1.0} +] diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.png b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.png new file mode 100644 index 00000000..d2720cc6 Binary files /dev/null and b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.png differ diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.png.import b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.png.import new file mode 100644 index 00000000..8e46ac89 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ccquiq0ledwd8" +path="res://.godot/imported/pr_merger.png-b8773009ff03eb05a00fb78941802e50.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.png" +dest_files=["res://.godot/imported/pr_merger.png-b8773009ff03eb05a00fb78941802e50.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/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.tres b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.tres new file mode 100644 index 00000000..22a24076 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.tres @@ -0,0 +1,78 @@ +[gd_resource type="SpriteFrames" load_steps=18 format=3] + +[ext_resource type="Texture2D" path="res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/pr_merger.png" id="1"] + +[sub_resource type="AtlasTexture" id="Frame_0"] +atlas = ExtResource("1") +region = Rect2(0, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_1"] +atlas = ExtResource("1") +region = Rect2(256, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_2"] +atlas = ExtResource("1") +region = Rect2(512, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_3"] +atlas = ExtResource("1") +region = Rect2(768, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_4"] +atlas = ExtResource("1") +region = Rect2(0, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_5"] +atlas = ExtResource("1") +region = Rect2(256, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_6"] +atlas = ExtResource("1") +region = Rect2(512, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_7"] +atlas = ExtResource("1") +region = Rect2(768, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_8"] +atlas = ExtResource("1") +region = Rect2(0, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_9"] +atlas = ExtResource("1") +region = Rect2(256, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_10"] +atlas = ExtResource("1") +region = Rect2(512, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_11"] +atlas = ExtResource("1") +region = Rect2(768, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_12"] +atlas = ExtResource("1") +region = Rect2(0, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_13"] +atlas = ExtResource("1") +region = Rect2(256, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_14"] +atlas = ExtResource("1") +region = Rect2(512, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_15"] +atlas = ExtResource("1") +region = Rect2(768, 480, 256, 160) + +[resource] +animations = [ +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_0")}], "loop": true, "name": &"hold_idle", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_1")}, {"duration": 1.0, "texture": SubResource("Frame_2")}, {"duration": 1.0, "texture": SubResource("Frame_3")}, {"duration": 1.0, "texture": SubResource("Frame_4")}], "loop": true, "name": &"run", "speed": 7.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_5")}, {"duration": 1.0, "texture": SubResource("Frame_6")}, {"duration": 1.0, "texture": SubResource("Frame_7")}], "loop": false, "name": &"step1", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_8")}, {"duration": 1.0, "texture": SubResource("Frame_9")}, {"duration": 1.0, "texture": SubResource("Frame_10")}], "loop": false, "name": &"step2", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_11")}, {"duration": 1.0, "texture": SubResource("Frame_12")}, {"duration": 1.0, "texture": SubResource("Frame_13")}], "loop": false, "name": &"step3", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_14")}], "loop": false, "name": &"equip", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_15")}], "loop": false, "name": &"hurt", "speed": 1.0} +] diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.png b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.png new file mode 100644 index 00000000..63774192 Binary files /dev/null and b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.png differ diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.png.import b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.png.import new file mode 100644 index 00000000..bfc7dc60 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://wctvs2npyl28" +path="res://.godot/imported/review_stamp.png-0d9c800db823621856ff0cbbc3a17453.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.png" +dest_files=["res://.godot/imported/review_stamp.png-0d9c800db823621856ff0cbbc3a17453.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/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.tres b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.tres new file mode 100644 index 00000000..05a99d7f --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.tres @@ -0,0 +1,78 @@ +[gd_resource type="SpriteFrames" load_steps=18 format=3] + +[ext_resource type="Texture2D" path="res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/review_stamp.png" id="1"] + +[sub_resource type="AtlasTexture" id="Frame_0"] +atlas = ExtResource("1") +region = Rect2(0, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_1"] +atlas = ExtResource("1") +region = Rect2(256, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_2"] +atlas = ExtResource("1") +region = Rect2(512, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_3"] +atlas = ExtResource("1") +region = Rect2(768, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_4"] +atlas = ExtResource("1") +region = Rect2(0, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_5"] +atlas = ExtResource("1") +region = Rect2(256, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_6"] +atlas = ExtResource("1") +region = Rect2(512, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_7"] +atlas = ExtResource("1") +region = Rect2(768, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_8"] +atlas = ExtResource("1") +region = Rect2(0, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_9"] +atlas = ExtResource("1") +region = Rect2(256, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_10"] +atlas = ExtResource("1") +region = Rect2(512, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_11"] +atlas = ExtResource("1") +region = Rect2(768, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_12"] +atlas = ExtResource("1") +region = Rect2(0, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_13"] +atlas = ExtResource("1") +region = Rect2(256, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_14"] +atlas = ExtResource("1") +region = Rect2(512, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_15"] +atlas = ExtResource("1") +region = Rect2(768, 480, 256, 160) + +[resource] +animations = [ +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_0")}], "loop": true, "name": &"hold_idle", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_1")}, {"duration": 1.0, "texture": SubResource("Frame_2")}, {"duration": 1.0, "texture": SubResource("Frame_3")}, {"duration": 1.0, "texture": SubResource("Frame_4")}], "loop": true, "name": &"run", "speed": 7.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_5")}, {"duration": 1.0, "texture": SubResource("Frame_6")}, {"duration": 1.0, "texture": SubResource("Frame_7")}], "loop": false, "name": &"step1", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_8")}, {"duration": 1.0, "texture": SubResource("Frame_9")}, {"duration": 1.0, "texture": SubResource("Frame_10")}], "loop": false, "name": &"step2", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_11")}, {"duration": 1.0, "texture": SubResource("Frame_12")}, {"duration": 1.0, "texture": SubResource("Frame_13")}], "loop": false, "name": &"step3", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_14")}], "loop": false, "name": &"equip", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_15")}], "loop": false, "name": &"hurt", "speed": 1.0} +] diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.png b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.png new file mode 100644 index 00000000..f2c48ed8 Binary files /dev/null and b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.png differ diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.png.import b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.png.import new file mode 100644 index 00000000..a09f72e0 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://ofogadw1h1ak" +path="res://.godot/imported/security_scanner.png-e917bb40b7e02218987343d12f3857d3.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.png" +dest_files=["res://.godot/imported/security_scanner.png-e917bb40b7e02218987343d12f3857d3.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/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.tres b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.tres new file mode 100644 index 00000000..0b1dbb70 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.tres @@ -0,0 +1,78 @@ +[gd_resource type="SpriteFrames" load_steps=18 format=3] + +[ext_resource type="Texture2D" path="res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/security_scanner.png" id="1"] + +[sub_resource type="AtlasTexture" id="Frame_0"] +atlas = ExtResource("1") +region = Rect2(0, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_1"] +atlas = ExtResource("1") +region = Rect2(256, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_2"] +atlas = ExtResource("1") +region = Rect2(512, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_3"] +atlas = ExtResource("1") +region = Rect2(768, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_4"] +atlas = ExtResource("1") +region = Rect2(0, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_5"] +atlas = ExtResource("1") +region = Rect2(256, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_6"] +atlas = ExtResource("1") +region = Rect2(512, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_7"] +atlas = ExtResource("1") +region = Rect2(768, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_8"] +atlas = ExtResource("1") +region = Rect2(0, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_9"] +atlas = ExtResource("1") +region = Rect2(256, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_10"] +atlas = ExtResource("1") +region = Rect2(512, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_11"] +atlas = ExtResource("1") +region = Rect2(768, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_12"] +atlas = ExtResource("1") +region = Rect2(0, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_13"] +atlas = ExtResource("1") +region = Rect2(256, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_14"] +atlas = ExtResource("1") +region = Rect2(512, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_15"] +atlas = ExtResource("1") +region = Rect2(768, 480, 256, 160) + +[resource] +animations = [ +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_0")}], "loop": true, "name": &"hold_idle", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_1")}, {"duration": 1.0, "texture": SubResource("Frame_2")}, {"duration": 1.0, "texture": SubResource("Frame_3")}, {"duration": 1.0, "texture": SubResource("Frame_4")}], "loop": true, "name": &"run", "speed": 7.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_5")}, {"duration": 1.0, "texture": SubResource("Frame_6")}, {"duration": 1.0, "texture": SubResource("Frame_7")}], "loop": false, "name": &"step1", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_8")}, {"duration": 1.0, "texture": SubResource("Frame_9")}, {"duration": 1.0, "texture": SubResource("Frame_10")}], "loop": false, "name": &"step2", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_11")}, {"duration": 1.0, "texture": SubResource("Frame_12")}, {"duration": 1.0, "texture": SubResource("Frame_13")}], "loop": false, "name": &"step3", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_14")}], "loop": false, "name": &"equip", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_15")}], "loop": false, "name": &"hurt", "speed": 1.0} +] diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.png b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.png new file mode 100644 index 00000000..5ff068a6 Binary files /dev/null and b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.png differ diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.png.import b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.png.import new file mode 100644 index 00000000..c9405288 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://68wpxsj5573" +path="res://.godot/imported/triage_hammer.png-dacf60f563ced69a38a1ce65d90935bb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.png" +dest_files=["res://.godot/imported/triage_hammer.png-dacf60f563ced69a38a1ce65d90935bb.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/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.tres b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.tres new file mode 100644 index 00000000..948b25d6 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.tres @@ -0,0 +1,78 @@ +[gd_resource type="SpriteFrames" load_steps=18 format=3] + +[ext_resource type="Texture2D" path="res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/triage_hammer.png" id="1"] + +[sub_resource type="AtlasTexture" id="Frame_0"] +atlas = ExtResource("1") +region = Rect2(0, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_1"] +atlas = ExtResource("1") +region = Rect2(256, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_2"] +atlas = ExtResource("1") +region = Rect2(512, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_3"] +atlas = ExtResource("1") +region = Rect2(768, 0, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_4"] +atlas = ExtResource("1") +region = Rect2(0, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_5"] +atlas = ExtResource("1") +region = Rect2(256, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_6"] +atlas = ExtResource("1") +region = Rect2(512, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_7"] +atlas = ExtResource("1") +region = Rect2(768, 160, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_8"] +atlas = ExtResource("1") +region = Rect2(0, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_9"] +atlas = ExtResource("1") +region = Rect2(256, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_10"] +atlas = ExtResource("1") +region = Rect2(512, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_11"] +atlas = ExtResource("1") +region = Rect2(768, 320, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_12"] +atlas = ExtResource("1") +region = Rect2(0, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_13"] +atlas = ExtResource("1") +region = Rect2(256, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_14"] +atlas = ExtResource("1") +region = Rect2(512, 480, 256, 160) + +[sub_resource type="AtlasTexture" id="Frame_15"] +atlas = ExtResource("1") +region = Rect2(768, 480, 256, 160) + +[resource] +animations = [ +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_0")}], "loop": true, "name": &"hold_idle", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_1")}, {"duration": 1.0, "texture": SubResource("Frame_2")}, {"duration": 1.0, "texture": SubResource("Frame_3")}, {"duration": 1.0, "texture": SubResource("Frame_4")}], "loop": true, "name": &"run", "speed": 7.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_5")}, {"duration": 1.0, "texture": SubResource("Frame_6")}, {"duration": 1.0, "texture": SubResource("Frame_7")}], "loop": false, "name": &"step1", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_8")}, {"duration": 1.0, "texture": SubResource("Frame_9")}, {"duration": 1.0, "texture": SubResource("Frame_10")}], "loop": false, "name": &"step2", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_11")}, {"duration": 1.0, "texture": SubResource("Frame_12")}, {"duration": 1.0, "texture": SubResource("Frame_13")}], "loop": false, "name": &"step3", "speed": 9.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_14")}], "loop": false, "name": &"equip", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("Frame_15")}], "loop": false, "name": &"hurt", "speed": 1.0} +] diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.png b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.png new file mode 100644 index 00000000..4bfc47b4 Binary files /dev/null and b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.png differ diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.png.import b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.png.import new file mode 100644 index 00000000..a618bd50 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dbcwc5r2hsmis" +path="res://.godot/imported/coderabbit_hero.png-6e29fa3ccfd5a6f50211df9b8214ed88.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.png" +dest_files=["res://.godot/imported/coderabbit_hero.png-6e29fa3ccfd5a6f50211df9b8214ed88.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/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.tres b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.tres new file mode 100644 index 00000000..49d0f0c7 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.tres @@ -0,0 +1,73 @@ +[gd_resource type="SpriteFrames" load_steps=18 format=3] +[ext_resource type="Texture2D" path="res://Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.png" id="1"] +[sub_resource type="AtlasTexture" id="F0"] +atlas = ExtResource("1") +region = Rect2(0, 0, 128, 128) +[sub_resource type="AtlasTexture" id="F1"] +atlas = ExtResource("1") +region = Rect2(128, 0, 128, 128) +[sub_resource type="AtlasTexture" id="F2"] +atlas = ExtResource("1") +region = Rect2(256, 0, 128, 128) +[sub_resource type="AtlasTexture" id="F3"] +atlas = ExtResource("1") +region = Rect2(384, 0, 128, 128) +[sub_resource type="AtlasTexture" id="F4"] +atlas = ExtResource("1") +region = Rect2(0, 128, 128, 128) +[sub_resource type="AtlasTexture" id="F5"] +atlas = ExtResource("1") +region = Rect2(128, 128, 128, 128) +[sub_resource type="AtlasTexture" id="F6"] +atlas = ExtResource("1") +region = Rect2(256, 128, 128, 128) +[sub_resource type="AtlasTexture" id="F7"] +atlas = ExtResource("1") +region = Rect2(384, 128, 128, 128) +[sub_resource type="AtlasTexture" id="F8"] +atlas = ExtResource("1") +region = Rect2(0, 256, 128, 128) +[sub_resource type="AtlasTexture" id="F9"] +atlas = ExtResource("1") +region = Rect2(128, 256, 128, 128) +[sub_resource type="AtlasTexture" id="F10"] +atlas = ExtResource("1") +region = Rect2(256, 256, 128, 128) +[sub_resource type="AtlasTexture" id="F11"] +atlas = ExtResource("1") +region = Rect2(384, 256, 128, 128) +[sub_resource type="AtlasTexture" id="F12"] +atlas = ExtResource("1") +region = Rect2(0, 384, 128, 128) +[sub_resource type="AtlasTexture" id="F13"] +atlas = ExtResource("1") +region = Rect2(128, 384, 128, 128) +[sub_resource type="AtlasTexture" id="F14"] +atlas = ExtResource("1") +region = Rect2(256, 384, 128, 128) +[sub_resource type="AtlasTexture" id="F15"] +atlas = ExtResource("1") +region = Rect2(384, 384, 128, 128) +[resource] +animations = [ +{"frames": [{"duration": 1.0, "texture": SubResource("F0")}], "loop": true, "name": &"idle", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F1")}], "loop": false, "name": &"run_a", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F2")}], "loop": false, "name": &"run_b", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F3")}], "loop": false, "name": &"jump", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F4")}], "loop": false, "name": &"jab_warning", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F5")}], "loop": false, "name": &"jab", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F6")}], "loop": false, "name": &"swing_warning", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F7")}], "loop": false, "name": &"swing", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F8")}], "loop": false, "name": &"kick_warning", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F9")}], "loop": false, "name": &"kick", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F10")}], "loop": false, "name": &"hurt", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F11")}], "loop": false, "name": &"defeat", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F12")}], "loop": false, "name": &"pickup", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F13")}], "loop": false, "name": &"block", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F14")}], "loop": false, "name": &"victory", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F15")}], "loop": false, "name": &"scanner", "speed": 1.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F1")}, {"duration": 1.0, "texture": SubResource("F2")}], "loop": true, "name": &"run", "speed": 8.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F4")}, {"duration": 1.0, "texture": SubResource("F5")}, {"duration": 1.0, "texture": SubResource("F0")}], "loop": false, "name": &"jab_action", "speed": 10.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F6")}, {"duration": 1.0, "texture": SubResource("F7")}, {"duration": 1.0, "texture": SubResource("F0")}], "loop": false, "name": &"swing_action", "speed": 8.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F8")}, {"duration": 1.0, "texture": SubResource("F9")}, {"duration": 1.0, "texture": SubResource("F0")}], "loop": false, "name": &"kick_action", "speed": 8.0} +] diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.png b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.png new file mode 100644 index 00000000..73fe2b71 Binary files /dev/null and b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.png differ diff --git a/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.png.import b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.png.import new file mode 100644 index 00000000..15f338e1 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://c2dp53ulqr0m8" +path="res://.godot/imported/coderabbit_hero_locomotion.png-c2a0528dc767d62a39debcd0a9aca60c.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.png" +dest_files=["res://.godot/imported/coderabbit_hero_locomotion.png-c2a0528dc767d62a39debcd0a9aca60c.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/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.tres b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.tres new file mode 100644 index 00000000..cc63d143 --- /dev/null +++ b/hoppys-adventure-arcade/Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.tres @@ -0,0 +1,78 @@ +[gd_resource type="SpriteFrames" load_steps=26 format=3] +[ext_resource type="Texture2D" path="res://Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.png" id="1"] +[sub_resource type="AtlasTexture" id="F0"] +atlas = ExtResource("1") +region = Rect2(0, 0, 128, 128) +[sub_resource type="AtlasTexture" id="F1"] +atlas = ExtResource("1") +region = Rect2(128, 0, 128, 128) +[sub_resource type="AtlasTexture" id="F2"] +atlas = ExtResource("1") +region = Rect2(256, 0, 128, 128) +[sub_resource type="AtlasTexture" id="F3"] +atlas = ExtResource("1") +region = Rect2(384, 0, 128, 128) +[sub_resource type="AtlasTexture" id="F4"] +atlas = ExtResource("1") +region = Rect2(0, 128, 128, 128) +[sub_resource type="AtlasTexture" id="F5"] +atlas = ExtResource("1") +region = Rect2(128, 128, 128, 128) +[sub_resource type="AtlasTexture" id="F6"] +atlas = ExtResource("1") +region = Rect2(256, 128, 128, 128) +[sub_resource type="AtlasTexture" id="F7"] +atlas = ExtResource("1") +region = Rect2(384, 128, 128, 128) +[sub_resource type="AtlasTexture" id="F8"] +atlas = ExtResource("1") +region = Rect2(0, 256, 128, 128) +[sub_resource type="AtlasTexture" id="F9"] +atlas = ExtResource("1") +region = Rect2(128, 256, 128, 128) +[sub_resource type="AtlasTexture" id="F10"] +atlas = ExtResource("1") +region = Rect2(256, 256, 128, 128) +[sub_resource type="AtlasTexture" id="F11"] +atlas = ExtResource("1") +region = Rect2(384, 256, 128, 128) +[sub_resource type="AtlasTexture" id="F12"] +atlas = ExtResource("1") +region = Rect2(0, 384, 128, 128) +[sub_resource type="AtlasTexture" id="F13"] +atlas = ExtResource("1") +region = Rect2(128, 384, 128, 128) +[sub_resource type="AtlasTexture" id="F14"] +atlas = ExtResource("1") +region = Rect2(256, 384, 128, 128) +[sub_resource type="AtlasTexture" id="F15"] +atlas = ExtResource("1") +region = Rect2(384, 384, 128, 128) +[sub_resource type="AtlasTexture" id="F16"] +atlas = ExtResource("1") +region = Rect2(0, 512, 128, 128) +[sub_resource type="AtlasTexture" id="F17"] +atlas = ExtResource("1") +region = Rect2(128, 512, 128, 128) +[sub_resource type="AtlasTexture" id="F18"] +atlas = ExtResource("1") +region = Rect2(256, 512, 128, 128) +[sub_resource type="AtlasTexture" id="F19"] +atlas = ExtResource("1") +region = Rect2(384, 512, 128, 128) +[sub_resource type="AtlasTexture" id="F20"] +atlas = ExtResource("1") +region = Rect2(0, 640, 128, 128) +[sub_resource type="AtlasTexture" id="F21"] +atlas = ExtResource("1") +region = Rect2(128, 640, 128, 128) +[sub_resource type="AtlasTexture" id="F22"] +atlas = ExtResource("1") +region = Rect2(256, 640, 128, 128) +[sub_resource type="AtlasTexture" id="F23"] +atlas = ExtResource("1") +region = Rect2(384, 640, 128, 128) +[resource] +animations = [{"frames": [{"duration": 1.0, "texture": SubResource("F0")}, {"duration": 1.0, "texture": SubResource("F1")}, {"duration": 1.0, "texture": SubResource("F2")}, {"duration": 1.0, "texture": SubResource("F3")}, {"duration": 1.0, "texture": SubResource("F4")}, {"duration": 1.0, "texture": SubResource("F5")}, {"duration": 1.0, "texture": SubResource("F6")}, {"duration": 1.0, "texture": SubResource("F7")}], "loop": true, "name": &"run", "speed": 14.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F8")}, {"duration": 1.0, "texture": SubResource("F9")}, {"duration": 1.0, "texture": SubResource("F10")}, {"duration": 1.0, "texture": SubResource("F11")}, {"duration": 1.0, "texture": SubResource("F12")}, {"duration": 1.0, "texture": SubResource("F13")}, {"duration": 1.0, "texture": SubResource("F14")}, {"duration": 1.0, "texture": SubResource("F15")}], "loop": true, "name": &"walk_up", "speed": 14.0}, +{"frames": [{"duration": 1.0, "texture": SubResource("F16")}, {"duration": 1.0, "texture": SubResource("F17")}, {"duration": 1.0, "texture": SubResource("F18")}, {"duration": 1.0, "texture": SubResource("F19")}, {"duration": 1.0, "texture": SubResource("F20")}, {"duration": 1.0, "texture": SubResource("F21")}, {"duration": 1.0, "texture": SubResource("F22")}, {"duration": 1.0, "texture": SubResource("F23")}], "loop": true, "name": &"walk_down", "speed": 14.0}] diff --git a/hoppys-adventure-arcade/Assets/beatemup-story/heroes/rabbit_stands.png b/hoppys-adventure-arcade/Assets/beatemup-story/heroes/rabbit_stands.png index 91619978..c4e7eb7e 100644 Binary files a/hoppys-adventure-arcade/Assets/beatemup-story/heroes/rabbit_stands.png and b/hoppys-adventure-arcade/Assets/beatemup-story/heroes/rabbit_stands.png differ diff --git a/hoppys-adventure-arcade/Assets/beatemup-story/heroes/send_in_the_rabbit.png b/hoppys-adventure-arcade/Assets/beatemup-story/heroes/send_in_the_rabbit.png index 7b4e2753..21cfa7b7 100644 Binary files a/hoppys-adventure-arcade/Assets/beatemup-story/heroes/send_in_the_rabbit.png and b/hoppys-adventure-arcade/Assets/beatemup-story/heroes/send_in_the_rabbit.png differ diff --git a/hoppys-adventure-arcade/app/how_to_play_data.gd b/hoppys-adventure-arcade/app/how_to_play_data.gd index 5e1b4f78..56a038d4 100644 --- a/hoppys-adventure-arcade/app/how_to_play_data.gd +++ b/hoppys-adventure-arcade/app/how_to_play_data.gd @@ -26,6 +26,9 @@ const ORBIT := "res://Assets/orbital-ops-assets-only/orbital_ops/" const FOREST := "res://Assets/enchanted-repo-assets-only/enchanted_repo/" const CITY := "res://Assets/code-city-brawl-assets-only/code_city/" const HEROES := "res://Assets/heroes-vs-villains-assets-only/heroes_villains/" +## Heroes vs. Villains' player sheets (modes/beatem_up/reinvent/player.gd). +const HERO := "res://Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.tres" +const HERO_MOTION := "res://Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.tres" const RUNNER := "res://Assets/runner/menu_previews/" const PLATFORMER_HOPPY := BAY + "hoppy/platformer.tres" const BRAWL_HOPPY := "res://Assets/code-city-brawl-player/combat.tres" @@ -164,10 +167,10 @@ const GAMES := { _PAUSE, ], "poses": { - "move": [HEROES + "hoppy_locomotion.tres", "walk_down"], - "jump": [HEROES + "hoppy.tres", "jump"], - "attack": [HEROES + "hoppy.tres", "jab_action"], - "pickup": [HEROES + "hoppy.tres", "pickup"], + "move": [HERO_MOTION, "walk_down"], + "jump": [HERO, "jump"], + "attack": [HERO, "jab_action"], + "pickup": [HERO, "pickup"], "review": [HEROES + "capabilities_fx.tres", "review_burst", "pulse"], }, }, diff --git a/hoppys-adventure-arcade/app/how_to_play_demo.gd b/hoppys-adventure-arcade/app/how_to_play_demo.gd index 40d36f5f..e2848020 100644 --- a/hoppys-adventure-arcade/app/how_to_play_demo.gd +++ b/hoppys-adventure-arcade/app/how_to_play_demo.gd @@ -633,7 +633,7 @@ func _brawl_tips() -> void: ## Hoppy runs in and combos two AI Slop sprites while the boss watches. func _heroes_goal() -> void: var start := Vector2(150, FLOOR_Y) - var hoppy := actor(Data.HEROES + "hoppy_locomotion.tres", "run", start, 1.65) + var hoppy := actor(Data.HERO_MOTION, "run", start, 1.65) # reinvent/game.gd: AI Slop stands at room x 945, y 565, at 1.6x; its minions at 1.7x. var boss := actor(Data.HEROES + "ai_slop.tres", "idle", Vector2(1080, 565), 1.6) var slops: Array[AnimatedSprite2D] = [ @@ -642,7 +642,7 @@ func _heroes_goal() -> void: reset(func() -> void: hoppy.position = start hoppy.modulate = Color.WHITE - _wear(hoppy, Data.HEROES + "hoppy_locomotion.tres", "run") + _wear(hoppy, Data.HERO_MOTION, "run") boss.play("idle") for slop in slops: slop.modulate = Color.WHITE @@ -651,17 +651,17 @@ func _heroes_goal() -> void: var at := start for index in slops.size(): var stop := slops[index].position - Vector2(130, 0) - pose(hoppy, Data.HEROES + "hoppy_locomotion.tres", "run") + pose(hoppy, Data.HERO_MOTION, "run") move(hoppy, stop, 0.4 + (stop.x - at.x) / 1100.0) at = stop for clip in ["jab_action", "kick_action"]: - pose(hoppy, Data.HEROES + "hoppy.tres", clip) + pose(hoppy, Data.HERO, clip) flash(slops[index]) pause_for(0.35) play(slops[index], "defeat") fade(slops[index], 0.0, 0.3) say("Then take on the villain") - pose(hoppy, Data.HEROES + "hoppy.tres", "victory") + pose(hoppy, Data.HERO, "victory") play(boss, "hurt") flash(boss) pause_for(1.5) @@ -671,7 +671,7 @@ func _heroes_goal() -> void: ## Bugs close in; Hoppy holds REVIEW and the burst stuns them all. func _heroes_review() -> void: var center := Vector2(640, FLOOR_Y) - var hoppy := actor(Data.HEROES + "hoppy.tres", "idle", center, 1.65) + var hoppy := actor(Data.HERO, "idle", center, 1.65) var outer := [140.0, 310.0, 970.0, 1140.0] var inner := [400.0, 480.0, 800.0, 880.0] var slops: Array[AnimatedSprite2D] = [] diff --git a/hoppys-adventure-arcade/modes/beatem_up/reinvent/art.gd b/hoppys-adventure-arcade/modes/beatem_up/reinvent/art.gd index 1be770d5..20ead395 100644 --- a/hoppys-adventure-arcade/modes/beatem_up/reinvent/art.gd +++ b/hoppys-adventure-arcade/modes/beatem_up/reinvent/art.gd @@ -30,6 +30,9 @@ const ARMED_CELL := Vector2(256, 160) const ARMED_PIVOT := Vector2(96, 147) const ARMED_ROOT := "res://Assets/hoppy-armed/heroes/" +## The CodeRabbit hero's armed sheets (tools/build_coderabbit_hero_2d.py): the armed-Hoppy sheets +## recoloured, same cells and clips. The manifest and the weapon-only carry icons stay in ARMED_ROOT. +const HERO_ARMED_ROOT := "res://Assets/Character/CodeRabbit/2D/heroes_villains/armed/" static var armed_manifest: Dictionary = {} static var armed_manifest_loaded := false static var armed_sheets: Dictionary = {} @@ -60,7 +63,7 @@ static func armed_frames(weapon_id: String) -> SpriteFrames: return armed_sheets[weapon_id] var spec := armed_spec(weapon_id) var file_name := str(spec.get("resource", weapon_id + ".tres")) - var path := ARMED_ROOT + file_name + var path := HERO_ARMED_ROOT + file_name var frames: SpriteFrames = null if ResourceLoader.exists(path): frames = load(path) as SpriteFrames diff --git a/hoppys-adventure-arcade/modes/beatem_up/reinvent/player.gd b/hoppys-adventure-arcade/modes/beatem_up/reinvent/player.gd index beb6bc70..3a689651 100644 --- a/hoppys-adventure-arcade/modes/beatem_up/reinvent/player.gd +++ b/hoppys-adventure-arcade/modes/beatem_up/reinvent/player.gd @@ -1,9 +1,11 @@ extends CharacterBody2D const Art = preload("res://modes/beatem_up/reinvent/art.gd") const Catalog = preload("res://modes/beatem_up/reinvent/catalog.gd") -const BASE_FRAMES = preload("res://Assets/heroes-vs-villains-assets-only/heroes_villains/hoppy.tres") +## The CodeRabbit hero rabbit from the artist handover, posed in 2D on Hoppy's clip layout +## (tools/build_coderabbit_hero_2d.py). Weapon sheets (Art.armed_frames) still draw the old Hoppy. +const BASE_FRAMES = preload("res://Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero.tres") const COMBAT_FRAMES = BASE_FRAMES -const MOTION_FRAMES = preload("res://Assets/heroes-vs-villains-assets-only/heroes_villains/hoppy_locomotion.tres") +const MOTION_FRAMES = preload("res://Assets/Character/CodeRabbit/2D/heroes_villains/coderabbit_hero_locomotion.tres") const MOVE_SPEED := 264.0 const UNARMED_OFFSET := Vector2(0, -51) const SHEET_POSES := ["idle", "run", "jab", "swing", "kick", "pickup", "hurt"] diff --git a/hoppys-adventure-arcade/scenes/beatem_up/beatem_menu.gd b/hoppys-adventure-arcade/scenes/beatem_up/beatem_menu.gd index a8d3355c..a3466042 100644 --- a/hoppys-adventure-arcade/scenes/beatem_up/beatem_menu.gd +++ b/hoppys-adventure-arcade/scenes/beatem_up/beatem_menu.gd @@ -14,6 +14,7 @@ var selected_mode := 0 var started := false var local_music: AudioStreamPlayer var fade: Tween +const TILE_MIN_HEIGHT := 240.0 func _ready() -> void: Heroes.install_input() @@ -40,24 +41,41 @@ func _ready() -> void: column.add_child(Style.label("BEAT'EM UP / MODE SELECT",38)) description=Style.label("",17) column.add_child(description) - var play := Style.button("Play Code City Brawl →",54) + var tiles := HBoxContainer.new() + tiles.add_theme_constant_override("separation",36) + tiles.size_flags_vertical=Control.SIZE_EXPAND_FILL + column.add_child(tiles) + var play := game_tile("Code City Brawl","res://Assets/beatemup-story/code_city/hoppy_clocks_in.png") play.name="CodeCityButton" - column.add_child(play) + tiles.add_child(play) play.pressed.connect(func(): launch("code_city_brawl")) play.mouse_entered.connect(func(): focus_mode(0)) play.focus_entered.connect(func(): focus_mode(0)) - var heroes := Style.button("Play Heroes vs. Villains →",54) + var heroes := game_tile("Heroes vs. Villains","res://Assets/beatemup-story/heroes/send_in_the_rabbit.png") heroes.name="HeroesButton" - column.add_child(heroes) + tiles.add_child(heroes) heroes.pressed.connect(func(): launch("heroes_vs_villains")) heroes.mouse_entered.connect(func(): focus_mode(1)) heroes.focus_entered.connect(func(): focus_mode(1)) - var back := Style.button("Return to Main Menu",48) - column.add_child(back) + # Room for the selected card's grow, so it never covers the buttons below. + var gap := Control.new() + gap.custom_minimum_size.y=12 + column.add_child(gap) + var row := HBoxContainer.new() + row.add_theme_constant_override("separation",16) + column.add_child(row) + var back := Style.button("Return to Main Menu",44) + back.custom_minimum_size.x=0 + row.add_child(back) back.pressed.connect(func(): AppRouter.go("hub")) - var quit := Style.button("Quit Game",48) - column.add_child(quit) + var quit := Style.button("Quit Game",44) + quit.custom_minimum_size.x=0 + row.add_child(quit) quit.pressed.connect(func(): AppRouter.quit_game()) + play.focus_neighbor_bottom=play.get_path_to(back) + heroes.focus_neighbor_bottom=heroes.get_path_to(back) + back.focus_neighbor_top=back.get_path_to(play) + quit.focus_neighbor_top=quit.get_path_to(heroes) var controls := Style.label("",14) Prompts.track(controls, func() -> String: return "%s: move · %s: jump · %s: combo\n%s: pick up weapon · %s: pause\nHeroes: hold %s to charge REVIEW after unlocking it" % [ @@ -73,6 +91,35 @@ func _ready() -> void: focus_mode(0,true) play.grab_focus() +## A square game card: the mode's painting on top, its title underneath. +func game_tile(title: String, image: String) -> Button: + # Each card takes half the row and all the height the column leaves it. + var tile := Style.button("",TILE_MIN_HEIGHT) + tile.custom_minimum_size=Vector2(0,TILE_MIN_HEIGHT) + tile.size_flags_horizontal=Control.SIZE_EXPAND_FILL + tile.size_flags_vertical=Control.SIZE_EXPAND_FILL + tile.active_scale=1.025 + var card := VBoxContainer.new() + card.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + for side in ["left","top","right"]: card.set("offset_"+side,6 if side!="right" else -6) + card.offset_bottom=-4 + card.add_theme_constant_override("separation",6) + card.mouse_filter=Control.MOUSE_FILTER_IGNORE + tile.add_child(card) + var picture := TextureRect.new() + picture.texture=load(image) + picture.expand_mode=TextureRect.EXPAND_IGNORE_SIZE + picture.stretch_mode=TextureRect.STRETCH_KEEP_ASPECT_COVERED + picture.size_flags_vertical=Control.SIZE_EXPAND_FILL + picture.clip_contents=true + picture.mouse_filter=Control.MOUSE_FILTER_IGNORE + card.add_child(picture) + var name_label := Style.label(title,26) + name_label.horizontal_alignment=HORIZONTAL_ALIGNMENT_CENTER + name_label.mouse_filter=Control.MOUSE_FILTER_IGNORE + card.add_child(name_label) + return tile + func launch(route: String) -> void: if started: return started=true diff --git a/hoppys-adventure-arcade/scenes/main_menu/menu_button.gd b/hoppys-adventure-arcade/scenes/main_menu/menu_button.gd index f4f9a67f..89601068 100644 --- a/hoppys-adventure-arcade/scenes/main_menu/menu_button.gd +++ b/hoppys-adventure-arcade/scenes/main_menu/menu_button.gd @@ -2,6 +2,8 @@ extends Button ## Hover/focus grow. The parent container owns position, size and scale: every re-sort resets scale ## to 1, so the pivot tracks resizes and the target scale is re-applied after each sort. const ACTIVE_SCALE := 1.05 +## Big cards set a smaller grow so they stay clear of their neighbours. +var active_scale := ACTIVE_SCALE var motion: Tween func _ready() -> void: mouse_entered.connect(_refresh) @@ -16,7 +18,7 @@ func _notification(what: int) -> void: if what == NOTIFICATION_RESIZED: pivot_offset = size * 0.5 func _target() -> Vector2: - return Vector2.ONE * (ACTIVE_SCALE if is_hovered() or has_focus() else 1.0) + return Vector2.ONE * (active_scale if is_hovered() or has_focus() else 1.0) func _restore_scale() -> void: if not (motion and motion.is_running()): scale = _target() diff --git a/hoppys-adventure-arcade/tests/how_to_play.gd b/hoppys-adventure-arcade/tests/how_to_play.gd index d9afe075..a67ae4d6 100644 --- a/hoppys-adventure-arcade/tests/how_to_play.gd +++ b/hoppys-adventure-arcade/tests/how_to_play.gd @@ -223,7 +223,7 @@ func enemies_face_hoppy(demo: Node, where: String) -> void: if not child is AnimatedSprite2D: continue var file: String = child.sprite_frames.resource_path.get_file() - if file in ["platformer.tres", "combat.tres", "hoppy.tres", "hoppy_locomotion.tres"]: + if file in ["platformer.tres", "combat.tres", "hoppy.tres", "hoppy_locomotion.tres", "coderabbit_hero.tres", "coderabbit_hero_locomotion.tres"]: hoppy = child elif not file in ["pickups.tres", "interactives.tres", "hazards.tres", "items_allies.tres", "weapons.tres", "capabilities_fx.tres"]: others.append(child) diff --git a/hoppys-adventure-arcade/tools/build_coderabbit_hero_2d.py b/hoppys-adventure-arcade/tools/build_coderabbit_hero_2d.py new file mode 100644 index 00000000..e4a869f7 --- /dev/null +++ b/hoppys-adventure-arcade/tools/build_coderabbit_hero_2d.py @@ -0,0 +1,455 @@ +"""Re-skin Hoppy's Heroes vs. Villains sheets in the CodeRabbit hero rabbit's colours. + +The artist handover (a low-poly 3D rabbit: white fur, orange-and-white ears, orange cape and +suit accents, black belt, orange boots) only changes how the hero looks, so this keeps every +pose of Hoppy's existing art and swaps its palette pixel by pixel, keeping each pixel's shading: + +- orange fur and cream muzzle -> white fur with cool grey shadows +- pink inner ears -> orange +- charcoal suit and cape -> orange, bright to dark following the original value +- gold belt and buckle -> black belt +- orange in the bottom band of a standing pose (boots) -> stays orange +- near-black ink outline and eyes -> unchanged +- the stray third ear on the walk_down frames -> cut away and re-inked +- effect marks (swing arcs, shield, scanner, stars) -> unchanged: in the cells that have them, + warm pixels outside the filled ink outline are left as drawn + +Outputs `coderabbit_hero.png` / `coderabbit_hero_locomotion.png` plus SpriteFrames copied from +`hoppy.tres` / `hoppy_locomotion.tres` (same cells, clip names, fps and loop flags). + + uv run --python 3.12 --no-project --with pillow --with numpy --with scipy python tools/build_coderabbit_hero_2d.py +""" + +import re +from pathlib import Path + +import numpy as np +from PIL import Image +from scipy import ndimage + +ROOT = Path(__file__).resolve().parents[1] +HOPPY = ROOT / "Assets/heroes-vs-villains-assets-only/heroes_villains" +OUT = ROOT / "Assets/Character/CodeRabbit/2D/heroes_villains" +RES = "res://Assets/Character/CodeRabbit/2D/heroes_villains/" +CELL = 128 + +FUR_DARK = np.array([138, 140, 160]) +FUR_LIGHT = np.array([252, 250, 246]) +CAPE_DARK = np.array([196, 70, 18]) +CAPE_LIGHT = np.array([255, 150, 64]) +EAR = np.array([246, 112, 34]) +BELT = np.array([26, 24, 28]) + + +def hsv(rgb: np.ndarray) -> tuple[np.ndarray, np.ndarray, np.ndarray]: + r, g, b = (rgb[..., i] / 255.0 for i in range(3)) + mx, mn = np.maximum(np.maximum(r, g), b), np.minimum(np.minimum(r, g), b) + d = mx - mn + h = np.zeros_like(mx) + safe = d > 1e-6 + rm, gm, bm = safe & (mx == r), safe & (mx == g) & (mx != r), safe & (mx == b) & (mx != r) & (mx != g) + h[rm] = ((g - b)[rm] / d[rm]) % 6 + h[gm] = (b - r)[gm] / d[gm] + 2 + h[bm] = (r - g)[bm] / d[bm] + 4 + s = np.where(mx > 0, d / np.maximum(mx, 1e-6), 0) + return h * 60, s, mx + + +def lerp(a: np.ndarray, b: np.ndarray, t: np.ndarray) -> np.ndarray: + t = np.clip(t, 0, 1)[..., None] + return a + (b - a) * t + + +def figure_rows(solid: np.ndarray, cell: tuple[int, int]): + """(y0, x0, top, bottom) of the figure in each cell, cell-local; None for an empty cell.""" + cw, ch = cell + for y0 in range(0, solid.shape[0], ch): + for x0 in range(0, solid.shape[1], cw): + ys = np.nonzero(solid[y0:y0 + ch, x0:x0 + cw].any(1))[0] + yield y0, x0, (ys[0], ys[-1]) if ys.size else None + + +def below_head(solid: np.ndarray, cell: tuple[int, int]) -> np.ndarray: + """Everything under the top 42% of each standing cell's figure (lying cells: nothing).""" + cw, ch = cell + mask = np.zeros(solid.shape, bool) + for y0, x0, span in figure_rows(solid, cell): + if span is None or span[1] - span[0] < 60: + continue + mask[y0 + span[0] + int(0.42 * (span[1] - span[0])):y0 + ch, x0:x0 + cw] = True + return mask + + +def boots_mask(solid: np.ndarray, cell: tuple[int, int]) -> np.ndarray: + """The bottom band of each standing cell, where Hoppy's boots are.""" + cw = cell[0] + mask = np.zeros(solid.shape, bool) + for y0, x0, span in figure_rows(solid, cell): + if span is None or span[1] - span[0] < 60: # lying down (defeat): no boots band + continue + mask[y0 + span[1] - 13:y0 + span[1] + 1, x0:x0 + cw] = True + return mask + + +# Combat cells (index in hoppy.png's 4x4 grid) with baked effect marks, and the cell-local box +# (x0, y0, x1, y1) each mark sits in: swing arc, kick_warning swirl, kick arc, hurt stars, +# block shield, scanner card. The locomotion sheet has none. +FX_BOXES = { + 7: (74, 0, 128, 128), + 8: (0, 60, 70, 128), + 9: (64, 30, 128, 128), + 10: (74, 0, 128, 70), + 13: (78, 0, 128, 128), + 15: (80, 0, 128, 128), +} + + +def boots(cls: np.ndarray, s: np.ndarray, figure: np.ndarray, cell: tuple[int, int]) -> np.ndarray: + """Hoppy's orange boots, kept orange as whole shapes: an ink-bounded orange patch that sits + mostly in a standing figure's lower 30% (a foot raised mid-run is above the bottom band), plus + the lower-30% part of any patch touching the band. Paws are higher, and stay fur.""" + orange = (cls == FUR) & (s >= 0.5) + band = boots_mask(figure, cell) + low = np.zeros(figure.shape, bool) + cw, ch = cell + for y0, x0, span in figure_rows(figure, cell): + if span is None or span[1] - span[0] < 60: + continue + low[y0 + span[0] + int(0.7 * (span[1] - span[0])):y0 + ch, x0:x0 + cw] = True + labels, count = ndimage.label(orange) + if count == 0: + return orange + index = range(1, count + 1) + total = ndimage.sum(np.ones_like(labels), labels, index) + in_low = ndimage.sum(low, labels, index) + touches = ndimage.maximum(band, labels, index) + mostly_low = np.zeros(count + 1, bool) + mostly_low[1:] = np.asarray(in_low) >= 0.6 * np.asarray(total) + touching = np.zeros(count + 1, bool) + touching[1:] = np.asarray(touches) > 0 + # A planted boot can run into the cape lining or a trailing arm with no ink between them: of a + # patch that only touches the band, just its lower part is boot. + return mostly_low[labels] | (touching[labels] & low) + + +def box_region(shape, boxes: dict, cell: tuple[int, int]) -> np.ndarray: + """Cell-local (x0, y0, x1, y1) boxes, keyed by cell index (one box or a list of boxes).""" + cw, ch = cell + cols = shape[1] // cw + region = np.zeros(shape, bool) + for index, entry in boxes.items(): + ox, oy = (index % cols) * cw, (index // cols) * ch + for x0, y0, x1, y1 in entry if isinstance(entry, list) else [entry]: + region[oy + y0:oy + y1, ox + x0:ox + x1] = True + return region + + +def effect_marks(warm: np.ndarray, ink: np.ndarray, boxes: dict, cell: tuple[int, int]) -> np.ndarray: + """Warm pixels in an effect box and outside every ink-outlined shape (fur sits inside one).""" + inside = ndimage.binary_fill_holes(ndimage.binary_dilation(ink, iterations=2)) + return warm & ~inside & box_region(warm.shape, boxes, cell) + + +def held_items(cls: np.ndarray, alpha: np.ndarray, boxes: dict, cell: tuple[int, int], + hard: dict | None = None, skin: dict | None = None) -> np.ndarray: + """A held weapon painted into the sheet: inside its box, every patch of one colour region + (orange, dark, ...) that lies (almost) wholly in the box. Patches split at ink and at region + changes, so the paw holding the weapon and the sleeve behind it reach out of the box and are + still repainted; the ink bordering the kept patches is kept with them.""" + region = box_region(alpha.shape, boxes, cell) + keep = np.zeros(alpha.shape, bool) + for group in ((FUR, EAR_IN), (SUIT, BELT_C)): + paint = (alpha > 40) & np.isin(cls, group) + labels, count = ndimage.label(paint) # 4-connected: one-pixel ink lines separate patches + if count == 0: + continue + index = range(1, count + 1) + total = ndimage.sum(np.ones_like(labels), labels, index) + inside = ndimage.sum(region, labels, index) + held = np.zeros(count + 1, bool) + held[1:] = inside >= 0.8 * total + keep |= held[labels] + # Where the weapon touches the paw with no ink between them, a tight hard box around the weapon + # part keeps everything in it. + if hard: + tight = box_region(alpha.shape, hard, cell) + grab = tight & (alpha > 0) + # The suit behind the weapon: a dark patch that mostly lies outside the tight box is body. + suit = (alpha > 40) & np.isin(cls, (SUIT, BELT_C)) + labels, count = ndimage.label(suit) + if count: + index = range(1, count + 1) + inside = ndimage.sum(tight, labels, index) / ndimage.sum(np.ones_like(labels), labels, index) + body = np.zeros(count + 1, bool) + body[1:] = inside < 0.6 + grab &= ~body[labels] + keep |= grab + keep |= region & (cls == INK) & ndimage.binary_dilation(keep, np.ones((3, 3)), iterations=2) + # Paws gripping a weapon sit wholly inside its box; skin boxes hand them back to the rabbit. + if skin: + keep &= ~box_region(alpha.shape, skin, cell) + return keep + + +NONE, INK, FUR, SUIT, EAR_IN, BELT_C, BOOT, FX_C = range(8) + + +def vote(cls: np.ndarray, open_: np.ndarray, size: int) -> np.ndarray: + """For every pixel, the most common confident class among its size x size neighbours.""" + counts = np.stack([ndimage.uniform_filter((cls == c).astype(float), size) for c in (FUR, SUIT, EAR_IN, BELT_C)]) + best = np.array([FUR, SUIT, EAR_IN, BELT_C])[counts.argmax(0)] + return np.where(open_ & (counts.max(0) > 0), best, cls) + + +def classify(h, s, v, lum, alpha) -> np.ndarray: + """Region per pixel: confident colours first, then blended and stray pixels join their neighbours.""" + seen = alpha > 0 + cls = np.full(alpha.shape, NONE) + ink = seen & (lum < 18) + cls[ink] = INK + body = seen & ~ink + hue = np.where(h > 330, h - 360, h) + cls[body & (hue >= 12) & (hue < 32) & (s >= 0.7) & (v >= 0.5)] = FUR + cls[body & (hue >= 18) & (hue < 60) & (s < 0.42) & (v >= 0.72)] = FUR # cream, eye shine + # Inner ear pink; the armed sheets paint it a lighter salmon (hue up to ~17). + cls[body & (hue >= -6) & (hue < 18) & (s >= 0.3) & (s < 0.68) & (v >= 0.6)] = EAR_IN + cls[body & (hue >= 36) & (hue < 55) & (s >= 0.5) & (v >= 0.5)] = BELT_C + cls[body & (s < 0.3) & (lum < 95)] = SUIT + # Blends (fur fading into ink, the suit's brown sheen) take their neighbours' region. + for size in (3, 5, 9, 17): + cls = vote(cls, body & (cls == NONE), size) + cls[body & (cls == NONE)] = SUIT + # Despeckle: a lone pixel unlike almost all its body neighbours joins them. + for _ in range(2): + majority = vote(cls, body, 3) + own = ndimage.uniform_filter((cls == majority).astype(float), 3) * 9 + same = np.zeros(cls.shape) + for c in (FUR, SUIT, EAR_IN, BELT_C): + same += (cls == c) * ndimage.uniform_filter((cls == c).astype(float), 3) * 9 + flip = body & (cls != majority) & (same <= 2) & (own >= 5) + cls[flip] = majority[flip] + return cls + + +def clean_ears(cls: np.ndarray, alpha: np.ndarray) -> np.ndarray: + """Hoppy's pink inner ear is painted as streaks with light fur between them, so mapped one + pixel at a time it comes out as broken orange with white specks. Close each streak into one + solid inner-ear shape (inside the ear's fur only) and drop specks too small to be an ear.""" + ear = cls == EAR_IN + soft = np.isin(cls, (FUR, EAR_IN)) & (alpha > 40) + solid = ndimage.binary_closing(ear, np.ones((3, 3)), iterations=2) & soft + solid = ndimage.binary_fill_holes(solid) & soft + labels, count = ndimage.label(solid, structure=np.ones((3, 3))) + if count: + sizes = ndimage.sum(np.ones_like(labels), labels, range(1, count + 1)) + big = np.zeros(count + 1, bool) + big[1:] = sizes >= 8 + solid &= big[labels] + # One-pixel nicks along the inner ear's edge: fur with inner ear on both sides joins it. + for axis in (0, 1): + before, after = np.roll(solid, 1, axis), np.roll(solid, -1, axis) + solid |= soft & before & after + out = cls.copy() + out[ear & ~solid] = FUR + out[solid] = EAR_IN + return out + + +def reskin(src: Path, cell: tuple[int, int], fx_boxes: dict, item_boxes: dict | None = None, + item_hard: dict | None = None, item_skin: dict | None = None) -> Image.Image: + a = np.array(Image.open(src).convert("RGBA")).astype(float) + rgb, alpha = a[..., :3], a[..., 3] + h, s, v = hsv(rgb) + lum = rgb @ np.array([0.299, 0.587, 0.114]) + out = rgb.copy() + cls = classify(h, s, v, lum, alpha) + item = held_items(cls, alpha, item_boxes, cell, item_hard, item_skin) if item_boxes else np.zeros(alpha.shape, bool) + figure = (alpha > 40) & ~item # head and boots are found on the rabbit, not on a raised weapon + + cls = clean_ears(cls, alpha) + warm = np.isin(cls, (FUR, EAR_IN)) & (v > 0.42) + cls[effect_marks(warm, (lum < 40) & (alpha > 128), fx_boxes, cell)] = FX_C + cls[boots(cls, s, figure, cell)] = BOOT + + fur = cls == FUR + # Orange fur keeps its light and shade as white-to-grey; cream and blends follow luminance, + # and anything darker than the shadow tone fades toward the ink instead of turning orange. + t_orange = (v - 0.45) / 0.55 + t_pale = (lum - 70) / 150 + t_fur = np.where(s >= 0.42, t_orange, t_pale) + fur_rgb = lerp(FUR_DARK, FUR_LIGHT, t_fur) + to_ink = np.clip(np.where(s >= 0.42, v / 0.45, lum / 70), 0, 1) + fur_rgb = np.where((to_ink < 1)[..., None], lerp(np.array([20, 18, 24]), FUR_DARK, to_ink), fur_rgb) + out[fur] = fur_rgb[fur] + ear = cls == EAR_IN + out[ear] = (EAR * (0.72 + 0.28 * v[..., None]))[ear] + suit = cls == SUIT + out[suit] = lerp(CAPE_DARK, CAPE_LIGHT, (lum - 16) / 55)[suit] + # Hoppy's deep shadows are solid ink blots inside the black cape; on an orange cape they read + # as holes. The core of a thick blot touching the suit becomes deep orange (or shadow grey on + # the white lining); its one-pixel rim stays ink. + ink = cls == INK + # "Thick" = six or more ink pixels in its 3x3, so two-pixel crease lines count; one-pixel + # outlines (three or four) stay ink. + core = ink & (ndimage.uniform_filter(ink.astype(float), 3) * 9 >= 5.5) + far = ~ndimage.binary_dilation(alpha <= 40, np.ones((3, 3))) # the outline touches transparency + near_suit = ndimage.uniform_filter(suit.astype(float), 9) + near_fur = ndimage.uniform_filter(fur.astype(float), 9) + # Blots by the suit, or below the head on a standing figure, are shadows. The eyes are blots + # too, but they sit in the head, away from the suit. + blot = core & far & ((near_suit > 0.12) | below_head(figure, cell)) + lining = blot & (near_fur > near_suit) + out[blot & ~lining] = CAPE_DARK * 0.62 + out[lining] = FUR_DARK * 0.72 + # Thin crease lines drawn inside the suit and cape: ink with only suit around it (no fur, not on + # the silhouette) becomes a deep orange crease. Outlines against fur stay ink. + no_fur = ~ndimage.binary_dilation(fur | ear, np.ones((3, 3))) + edge = ndimage.binary_dilation(alpha <= 40, np.ones((3, 3)), iterations=2) + crease = ink & no_fur & ~edge & (ndimage.uniform_filter(suit.astype(float), 3) > 0.2) + out[crease & ~blot] = CAPE_DARK * 0.5 + belt = cls == BELT_C + out[belt] = lerp(BELT, BELT + 60, (v - 0.45) / 0.55)[belt] + out[item] = rgb[item] + result = np.dstack([np.clip(out, 0, 255), alpha]).astype(np.uint8) + return Image.fromarray(result, "RGBA") + + +# hoppy_locomotion.png's walk_down cells (row 4-5) have a stray third ear: a stub on the head's +# top-right, behind the two real ears. Everything above this line (cell-local) right of the real +# ears is cut away, following the head's curve, and the cut edge is re-inked. +STUB_CELLS = range(16, 24) + + +def stub_cut(x: np.ndarray) -> np.ndarray: + return 44.0 + (x - 79.0) * 0.95 + + +def remove_stub_ear(img: Image.Image, cols: int) -> Image.Image: + a = np.array(img) + for index in STUB_CELLS: + ox, oy = (index % cols) * CELL, (index // cols) * CELL + cell = a[oy:oy + CELL, ox:ox + CELL] + ys, xs = np.mgrid[0:CELL, 0:CELL] + zone = (xs >= 79) & (xs <= 91) & (ys >= 20) & (ys <= 52) + cell[zone & (ys < stub_cut(xs))] = 0 + # One-pixel-wide slivers of the stub's outline: clear on both sides, left or right. + sliver = (xs >= 72) & (xs <= 92) & (ys >= 24) & (ys <= 50) + for _ in range(3): + solid = cell[..., 3] > 120 # half-transparent fringe does not anchor a line + lone = solid.copy() + lone[:, 1:-1] &= ~solid[:, :-2] & ~solid[:, 2:] + lone[:, [0, -1]] = False + cell[lone & sliver] = 0 + cell[sliver & (cell[..., 3] <= 120) & ~ndimage.binary_dilation(cell[..., 3] > 120, np.ones((3, 3)))] = 0 + # The stub's own left outline survives beside the real ear's tip; drop such leftover slivers. + labels, count = ndimage.label(cell[..., 3] > 0, structure=np.ones((3, 3))) + sizes = ndimage.sum(np.ones_like(labels), labels, range(1, count + 1)) + near = (xs >= 70) & (xs <= 95) & (ys >= 20) & (ys <= 56) + for i, size in enumerate(sizes, start=1): + piece = labels == i + if size < 40 and (piece & near).any(): + cell[piece] = 0 + # Re-ink the head's new top edge: opaque zone pixels with nothing directly above. + clear_above = np.zeros((CELL, CELL), bool) + clear_above[1:] = cell[:-1, :, 3] == 0 + edge = zone & (ys >= stub_cut(xs)) & (ys < stub_cut(xs) + 2) & (cell[..., 3] > 0) & clear_above + cell[edge] = (8, 6, 8, 255) + return Image.fromarray(a, "RGBA") + + +# Armed sheets (Assets/hoppy-armed/heroes/.png, 4x4 cells of 256x160): the weapon is painted +# in Hoppy's own orange and black, so colour cannot tell it apart. Each frame's box (cell-local +# x0, y0, x1, y1, traced from the sheet) bounds the weapon; held_items keeps what lies inside it. +ARMED = ROOT / "Assets/hoppy-armed/heroes" +ARMED_CELL = (256, 160) +WEAPON_BOXES = { + "review_stamp": { + 0: (98, 92, 134, 139), 1: (100, 92, 140, 138), 2: (103, 92, 144, 138), 3: (108, 93, 144, 138), + 4: (112, 94, 144, 130), 5: (85, 52, 135, 98), 6: (103, 114, 136, 156), 7: (121, 102, 151, 150), + 8: (44, 78, 79, 108), 9: (126, 95, 160, 137), 10: (133, 95, 165, 138), 11: (70, 42, 104, 76), + 12: (105, 110, 141, 153), 13: (127, 110, 162, 153), 14: (111, 71, 145, 118), 15: (106, 81, 143, 122), + }, + "triage_hammer": { + 0: (104, 92, 154, 134), 1: (96, 92, 142, 134), 2: (100, 90, 145, 133), 3: (100, 88, 140, 130), + 4: (98, 78, 146, 118), 5: [(78, 40, 116, 80), (110, 58, 134, 80)], 6: (140, 110, 186, 152), + 7: (152, 100, 194, 144), 8: (56, 76, 106, 112), 9: (156, 94, 206, 138), 10: (156, 90, 200, 134), + 11: [(74, 38, 116, 76), (112, 62, 130, 80)], 12: (152, 110, 196, 152), 13: (134, 70, 178, 116), + 14: [(52, 48, 88, 88), (84, 64, 94, 80)], 15: [(116, 66, 154, 106), (104, 96, 122, 114)], + }, + "change_stack_baton": { + 0: (58, 108, 154, 130), 1: (54, 100, 140, 124), 2: (54, 100, 141, 124), 3: (54, 104, 138, 126), + 4: [(48, 90, 82, 104), (80, 96, 114, 112), (112, 104, 146, 126)], 5: (130, 108, 180, 126), + 6: (150, 98, 202, 118), 7: (144, 102, 184, 120), 8: [(74, 74, 100, 92), (98, 86, 130, 112)], + 9: (146, 122, 196, 138), 10: (146, 98, 194, 118), 11: (76, 96, 118, 112), 12: (150, 98, 216, 118), + 13: (136, 102, 174, 118), 14: (120, 104, 154, 120), + 15: [(54, 104, 82, 122), (80, 94, 108, 112), (106, 82, 132, 100)], + }, + "security_scanner": { + 0: (112, 92, 139, 116), 1: (121, 80, 148, 107), 2: (121, 82, 150, 110), 3: (116, 88, 143, 112), + 4: (126, 82, 152, 108), 5: (77, 96, 106, 121), 6: (154, 84, 184, 108), 7: (161, 102, 189, 126), + 8: (146, 78, 176, 104), 9: (148, 70, 178, 97), 10: (142, 86, 171, 112), 11: (163, 97, 193, 123), + 12: (180, 88, 209, 116), 13: (146, 68, 173, 95), 14: (63, 42, 99, 75), 15: (108, 68, 131, 90), + }, + "pr_merger": { + 0: (112, 100, 140, 134), 1: (75, 102, 102, 130), 2: (106, 98, 144, 129), 3: (100, 96, 139, 125), + 4: (116, 102, 146, 131), 5: (86, 96, 117, 128), 6: (144, 96, 181, 123), 7: (144, 98, 176, 126), + 8: (103, 96, 130, 126), 9: (146, 92, 178, 122), 10: (139, 92, 161, 122), 11: (103, 110, 133, 139), + 12: (125, 55, 156, 90), 13: (133, 106, 162, 136), 14: (96, 66, 130, 101), 15: (111, 106, 140, 136), + }, +} +# Tight boxes around weapon parts that touch the paw without ink between them (see held_items). +WEAPON_HARD = { + "review_stamp": { + # Idle and run: knob, then the stamp body, which touches the paw. + 0: [(112, 97, 132, 115), (104, 114, 125, 133)], 1: [(110, 111, 131, 131)], + 2: [(116, 93, 137, 112), (106, 108, 134, 131)], 3: [(113, 110, 134, 133)], + 4: [(117, 106, 136, 129)], + 5: (86, 51, 108, 72), 7: (126, 102, 146, 121), + 8: [(58, 78, 78, 96), (44, 84, 66, 110)], 10: (140, 97, 161, 117), 13: (134, 109, 156, 129), + }, + "change_stack_baton": {4: (48, 90, 78, 104), 8: (74, 74, 100, 96)}, +} +# The scanner's boxes hug the device, and its orange grip runs straight into the paw: keep it all. +WEAPON_HARD["security_scanner"] = WEAPON_BOXES["security_scanner"] +# The PR merger is a gauntlet worn over the fist: the whole box is the weapon. +WEAPON_HARD["pr_merger"] = WEAPON_BOXES["pr_merger"] +# Paws inside a weapon box (see held_items). +WEAPON_SKIN = { + "review_stamp": { + 0: (88, 112, 104, 127), 1: (96, 110, 110, 126), 2: (90, 108, 106, 125), 3: (98, 108, 113, 126), + 4: (102, 102, 117, 120), 14: (108, 100, 126, 118), 15: (100, 104, 118, 122), + }, + "change_stack_baton": { + 0: [(82, 108, 97, 123), (100, 108, 114, 122)], 1: [(79, 104, 93, 119), (99, 104, 112, 119)], + 2: [(78, 104, 92, 118), (97, 104, 110, 118)], 3: [(78, 108, 92, 122), (97, 108, 110, 122)], + 4: [(77, 98, 91, 112), (98, 106, 110, 119)], 5: [(125, 105, 139, 119), (142, 108, 155, 123)], 6: (147, 103, 162, 117), + 8: (97, 91, 111, 106), + }, +} + + +def retarget(source: Path, png: str, dest: Path) -> None: + text = source.read_text() + text, n = re.subn(r'(\[ext_resource type="Texture2D" path=")[^"]+(")', rf"\g<1>{RES}{png}\g<2>", text) + assert n == 1, source + dest.write_text(text) + + +def main() -> None: + OUT.mkdir(parents=True, exist_ok=True) + reskin(HOPPY / "hoppy.png", (CELL, CELL), FX_BOXES).save(OUT / "coderabbit_hero.png", optimize=True) + loco = remove_stub_ear(reskin(HOPPY / "hoppy_locomotion.png", (CELL, CELL), {}), 4) + loco.save(OUT / "coderabbit_hero_locomotion.png", optimize=True) + retarget(HOPPY / "hoppy.tres", "coderabbit_hero.png", OUT / "coderabbit_hero.tres") + retarget(HOPPY / "hoppy_locomotion.tres", "coderabbit_hero_locomotion.png", + OUT / "coderabbit_hero_locomotion.tres") + (OUT / "armed").mkdir(exist_ok=True) + for weapon, boxes in WEAPON_BOXES.items(): + sheet = reskin(ARMED / f"{weapon}.png", ARMED_CELL, {}, boxes, WEAPON_HARD.get(weapon), + WEAPON_SKIN.get(weapon)) + sheet.save(OUT / "armed" / f"{weapon}.png", optimize=True) + retarget(ARMED / f"{weapon}.tres", f"armed/{weapon}.png", OUT / "armed" / f"{weapon}.tres") + print("wrote", OUT) + + +main() diff --git a/hoppys-adventure-arcade/tools/build_coderabbit_hero_story.py b/hoppys-adventure-arcade/tools/build_coderabbit_hero_story.py new file mode 100644 index 00000000..76337100 --- /dev/null +++ b/hoppys-adventure-arcade/tools/build_coderabbit_hero_story.py @@ -0,0 +1,144 @@ +"""Re-skin the CodeRabbit hero in the two Heroes vs. Villains story paintings. + +`send_in_the_rabbit.png` (third intro card) and `rabbit_stands.png` (last ending card) show orange +Hoppy. This repaints only the rabbit in the same colours as the in-game sheets +(`tools/build_coderabbit_hero_2d.py`): white fur and cape lining, orange inner ears, orange armour +and cape. Ink, eyes and the background are untouched. + +The rabbit is cut out with OpenCV GrabCut, seeded by the hand-traced polygons below (outside = +background, eroded inside = rabbit, plus "sure" polygons for the pale ears that look like stage +light). Edges are feathered by a pixel. + +It reads the selected masters (never its own output), so it is safe to re-run. Run it after +`tools/build_story_scenes.py`, which rebuilds these PNGs from the untouched masters: + + uv run --python 3.12 --no-project --with pillow --with numpy --with opencv-python-headless \\ + python tools/build_coderabbit_hero_story.py +""" + +import json +import math +from pathlib import Path + +import cv2 +import numpy as np +from PIL import Image, ImageDraw, ImageFilter + +ROOT = Path(__file__).resolve().parents[1] +STORY_ROOT = ROOT / "Assets/beatemup-story" +STORY = STORY_ROOT / "heroes" +SCENE_SIZE = (1600, 900) + +FUR_DARK = np.array([150, 152, 172]) +FUR_LIGHT = np.array([252, 250, 246]) +CAPE_DARK = np.array([200, 76, 20]) +CAPE_LIGHT = np.array([255, 160, 74]) +EAR = np.array([246, 112, 34]) + +SCENES = { + "send_in_the_rabbit": { + "outline": [(195, 365), (330, 240), (450, 222), (600, 262), (700, 296), (792, 296), (742, 92), + (746, 72), (805, 72), (872, 212), (908, 232), (958, 132), (1012, 136), (1022, 200), + (988, 290), (982, 378), (995, 455), (1060, 520), (1140, 640), (1132, 682), (1060, 662), + (1062, 792), (912, 792), (905, 722), (860, 690), (805, 642), (700, 640), (640, 652), + (622, 700), (614, 800), (444, 800), (455, 700), (560, 642), (605, 580), (600, 568), + (490, 568), (480, 520), (380, 505), (300, 470), (240, 420)], + "sure": [], + }, + "rabbit_stands": { + "outline": [(296, 418), (450, 378), (600, 355), (700, 328), (704, 280), (655, 58), (702, 60), + (792, 190), (843, 168), (858, 98), (918, 108), (918, 222), (906, 300), (906, 338), + (944, 378), (955, 450), (995, 560), (1010, 622), (965, 652), (952, 700), (1034, 800), + (1034, 842), (852, 842), (858, 700), (836, 694), (790, 694), (754, 706), (746, 780), (714, 842), + (566, 842), (588, 760), (640, 702), (600, 692), (548, 652), (450, 642), (380, 592), + (328, 582), (296, 522)], + "sure": [[(668, 72), (698, 74), (785, 195), (760, 240), (725, 230), (700, 160)], + [(862, 108), (910, 116), (908, 215), (880, 240), (850, 190)], + [(792, 604), (834, 612), (832, 686), (794, 686)]], # cape lining between the legs + }, +} + + +def selected_master(name: str) -> Image.Image: + """The scene's selected master, cover-cropped exactly as tools/build_story_scenes.py does, so + this always repaints the original art and running it twice changes nothing.""" + records = json.loads((STORY_ROOT / "refinements.json").read_text()) + record = next(r for r in records if r["mode"] == "heroes" and r["scene"] == name) + source = Image.open(STORY_ROOT / record["master"]).convert("RGBA") + scale = max(SCENE_SIZE[0] / source.width, SCENE_SIZE[1] / source.height) + size = (max(SCENE_SIZE[0], math.ceil(source.width * scale)), max(SCENE_SIZE[1], math.ceil(source.height * scale))) + resized = source.resize(size, Image.Resampling.LANCZOS) + left, top = (resized.width - SCENE_SIZE[0]) // 2, (resized.height - SCENE_SIZE[1]) // 2 + return resized.crop((left, top, left + SCENE_SIZE[0], top + SCENE_SIZE[1])).convert("RGB") + + +def polygon_mask(size: tuple[int, int], polygons: list) -> np.ndarray: + canvas = Image.new("L", size, 0) + for poly in polygons: + ImageDraw.Draw(canvas).polygon(poly, fill=1) + return np.asarray(canvas).astype(bool) + + +def rabbit_mask(rgb: np.ndarray, spec: dict) -> np.ndarray: + size = (rgb.shape[1], rgb.shape[0]) + inside = polygon_mask(size, [spec["outline"]]) + core = cv2.erode(inside.astype(np.uint8), np.ones((3, 3), np.uint8), iterations=28).astype(bool) + mask = np.full(inside.shape, cv2.GC_BGD, np.uint8) + mask[inside] = cv2.GC_PR_FGD + mask[core] = cv2.GC_FGD + if spec["sure"]: + mask[polygon_mask(size, spec["sure"]) & inside] = cv2.GC_FGD + bgd, fgd = np.zeros((1, 65)), np.zeros((1, 65)) + cv2.grabCut(cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR), mask, None, bgd, fgd, 6, cv2.GC_INIT_WITH_MASK) + return (mask == cv2.GC_FGD) | (mask == cv2.GC_PR_FGD) + + +def hsv(rgb: np.ndarray): + out = cv2.cvtColor(rgb.astype(np.uint8), cv2.COLOR_RGB2HSV_FULL).astype(float) + return out[..., 0] * 360 / 256, out[..., 1] / 255, out[..., 2] / 255 + + +def lerp(a, b, t): + return a + (b - a) * np.clip(t, 0, 1)[..., None] + + +def repaint(rgb: np.ndarray) -> np.ndarray: + h, s, v = hsv(rgb) + lum = rgb.astype(float) @ np.array([0.299, 0.587, 0.114]) + out = rgb.astype(float).copy() + ink = lum < 28 + eyes = (h >= 70) & (h < 170) & (s > 0.25) + keep = ink | eyes + # Region labels ordered so a median of neighbours lands on a sensible one. + KEEP, DARK, ORANGE, CREAM, PINK, OTHER = 0, 1, 2, 3, 4, 5 + cls = np.full(lum.shape, OTHER, np.uint8) + pink = ((h >= 340) | (h < 18)) & (s >= 0.15) & (s < 0.6) & (v > 0.6) + cream = ~pink & (h >= 15) & (h < 55) & (s < 0.38) & (v > 0.7) + orange = ~pink & ~cream & (h >= 8) & (h < 48) & (s >= 0.4) & (v >= 0.3) + dark = ~pink & ~cream & ~orange & (lum < 125) + cls[dark], cls[orange], cls[cream], cls[pink] = DARK, ORANGE, CREAM, PINK + # Painted texture speckles single pixels across regions (pink flecks in an ear); a 5x5 median + # of the labels settles each pixel into its surroundings. Ink and eyes stay exact. + smooth = cv2.medianBlur(cls, 5) + cls = np.where(keep, KEEP, np.where(cls == OTHER, OTHER, smooth)) + orange, cream, pink, dark = cls == ORANGE, cls == CREAM, cls == PINK, cls == DARK + out[orange] = lerp(FUR_DARK, FUR_LIGHT, (v - 0.35) / 0.6)[orange] + out[cream] = lerp(FUR_DARK, FUR_LIGHT, 0.75 + 0.25 * v)[cream] + out[pink] = (EAR * (0.7 + 0.3 * v[..., None]))[pink] + out[dark] = lerp(CAPE_DARK, CAPE_LIGHT, (lum - 28) / 65)[dark] + return np.clip(out, 0, 255) + + +def main() -> None: + for name, spec in SCENES.items(): + path = STORY / f"{name}.png" + rgb = np.asarray(selected_master(name)) + mask = rabbit_mask(rgb, spec) + soft = np.asarray(Image.fromarray((mask * 255).astype(np.uint8)).filter(ImageFilter.GaussianBlur(0.8))) + alpha = (soft / 255.0)[..., None] + result = rgb * (1 - alpha) + repaint(rgb) * alpha + Image.fromarray(result.astype(np.uint8)).save(path, optimize=True) + print("repainted", path.relative_to(ROOT)) + + +main()