From e8096a2d9467c26b73915bf1c05f1427f9ebfa3f Mon Sep 17 00:00:00 2001 From: Pepe20129 <72659707+Pepe20129@users.noreply.github.com> Date: Wed, 27 Dec 2023 01:09:04 +0100 Subject: [PATCH] Better actor viewer (#3544) * Update actorViewer.cpp * Add more actors * Add a search function * Update actorViewer.cpp * Add advanced mode for params --- soh/soh/ActorDB.cpp | 8 +- soh/soh/ActorDB.h | 1 + soh/soh/Enhancements/debugger/actorViewer.cpp | 835 +++++++++++++++++- 3 files changed, 840 insertions(+), 4 deletions(-) diff --git a/soh/soh/ActorDB.cpp b/soh/soh/ActorDB.cpp index a66048b32..ea8450ac4 100644 --- a/soh/soh/ActorDB.cpp +++ b/soh/soh/ActorDB.cpp @@ -74,7 +74,7 @@ static std::unordered_map actorDescriptions = { { ACTOR_EN_BUBBLE, "Shabom" }, { ACTOR_DOOR_SHUTTER, "Shutter Door" }, { ACTOR_EN_DODOJR, "Baby Dodongo" }, - { ACTOR_EN_BDFIRE, "Empty" }, + { ACTOR_EN_BDFIRE, "King Dodongo's Fire Breath" }, { ACTOR_EN_BOOM, "Boomerang" }, { ACTOR_EN_TORCH2, "Dark Link" }, { ACTOR_EN_BILI, "Biri" }, @@ -132,7 +132,7 @@ static std::unordered_map actorDescriptions = { { ACTOR_BG_TOKI_HIKARI, "Windows (Temple of Time)" }, { ACTOR_EN_YUKABYUN, "Flying Floor Tile" }, { ACTOR_BG_TOKI_SWD, "Master Sword" }, - { ACTOR_EN_FHG_FIRE, "Empty" }, + { ACTOR_EN_FHG_FIRE, "Phantom Ganon's Lighting Attack" }, { ACTOR_BG_MJIN, "Warp Song Pad" }, { ACTOR_BG_HIDAN_KOUSI, "Sliding Metal Gate" }, { ACTOR_DOOR_TOKI, "Door of Time Collision" }, @@ -548,6 +548,10 @@ int ActorDB::RetrieveId(const std::string& name) { return entry->second; } +int ActorDB::GetEntryCount() { + return db.size(); +} + ActorDB::Entry::Entry() { entry.name = nullptr; entry.desc = nullptr; diff --git a/soh/soh/ActorDB.h b/soh/soh/ActorDB.h index afb033a2f..8bcbb3f6a 100644 --- a/soh/soh/ActorDB.h +++ b/soh/soh/ActorDB.h @@ -64,6 +64,7 @@ public: static void AddBuiltInCustomActors(); + int GetEntryCount(); private: Entry& AddEntry(const std::string& name, const std::string& desc, size_t index); Entry& AddEntry(const std::string& name, const std::string& desc, const ActorInit& init); diff --git a/soh/soh/Enhancements/debugger/actorViewer.cpp b/soh/soh/Enhancements/debugger/actorViewer.cpp index f83449efe..e2b45a419 100644 --- a/soh/soh/Enhancements/debugger/actorViewer.cpp +++ b/soh/soh/Enhancements/debugger/actorViewer.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ extern PlayState* gPlayState; #include "textures/icon_item_24_static/icon_item_24_static.h" } +#define DEKUNUTS_FLOWER 10 #define DEBUG_ACTOR_NAMETAG_TAG "debug_actor_viewer" typedef struct { @@ -107,6 +109,783 @@ void PopulateActorDropdown(int i, std::vector& data) { } } +//actors that don't use params at all +static std::vector noParamsActors = { + ACTOR_ARMS_HOOK, + ACTOR_ARROW_FIRE, + ACTOR_ARROW_ICE, + ACTOR_ARROW_LIGHT, + ACTOR_BG_BOM_GUARD, + ACTOR_BG_DY_YOSEIZO, + ACTOR_BG_GATE_SHUTTER, + ACTOR_BG_GJYO_BRIDGE, + ACTOR_BG_HIDAN_FSLIFT, + ACTOR_BG_HIDAN_RSEKIZOU, + ACTOR_BG_HIDAN_SYOKU, + ACTOR_BG_JYA_GOROIWA, + ACTOR_BG_MIZU_UZU, + ACTOR_BG_MORI_RAKKATENJO, + ACTOR_BG_PUSHBOX, + ACTOR_BG_SPOT01_FUSYA, + ACTOR_BG_SPOT01_IDOHASHIRA, + ACTOR_BG_SPOT01_IDOMIZU, + ACTOR_BG_SPOT01_IDOSOKO, + ACTOR_BG_SPOT11_OASIS, + ACTOR_BG_SPOT15_SAKU, + ACTOR_BG_SPOT18_FUTA, + ACTOR_BG_TOKI_SWD, + ACTOR_BG_TREEMOUTH, + ACTOR_BG_VB_SIMA, + ACTOR_BOSS_DODONGO, + ACTOR_BOSS_FD, + ACTOR_BOSS_GOMA, + ACTOR_DEMO_EXT, + ACTOR_DEMO_SHD, + ACTOR_DEMO_TRE_LGT, + ACTOR_DOOR_TOKI, + ACTOR_EFC_ERUPC, + ACTOR_EN_ANI, + ACTOR_EN_AROW_TRAP, + ACTOR_EN_BIRD, + ACTOR_EN_BLKOBJ, + ACTOR_EN_BOM_BOWL_MAN, + ACTOR_EN_BOM_BOWL_PIT, + ACTOR_EN_BOM_CHU, + ACTOR_EN_BUBBLE, + ACTOR_EN_DIVING_GAME, + ACTOR_EN_DNT_DEMO, + ACTOR_EN_DNT_JIJI, + ACTOR_EN_DS, + ACTOR_EN_DU, + ACTOR_EN_EG, + ACTOR_EN_FU, + ACTOR_EN_GB, + ACTOR_EN_GE3, + ACTOR_EN_GUEST, + ACTOR_EN_HATA, + ACTOR_EN_HORSE_GANON, + ACTOR_EN_HORSE_LINK_CHILD, + ACTOR_EN_HORSE_ZELDA, + ACTOR_EN_HS2, + ACTOR_EN_JS, + ACTOR_EN_KAKASI, + ACTOR_EN_KAKASI3, + ACTOR_EN_MA1, + ACTOR_EN_MA2, + ACTOR_EN_MA3, + ACTOR_EN_MAG, + ACTOR_EN_MK, + ACTOR_EN_MS, + ACTOR_EN_NIW_LADY, + ACTOR_EN_NWC, + ACTOR_EN_OE2, + ACTOR_EN_OKARINA_EFFECT, + ACTOR_EN_RR, + ACTOR_EN_SA, + ACTOR_EN_SCENE_CHANGE, + ACTOR_EN_SKJNEEDLE, + ACTOR_EN_SYATEKI_ITM, + ACTOR_EN_SYATEKI_MAN, + ACTOR_EN_TAKARA_MAN, + ACTOR_EN_TORYO, + ACTOR_EN_VASE, + ACTOR_EN_ZL1, + ACTOR_MAGIC_DARK, + ACTOR_MAGIC_FIRE, + ACTOR_OBJ_DEKUJR, + ACTOR_OCEFF_SPOT, + + ACTOR_UNSET_1, + ACTOR_UNSET_3, + ACTOR_UNSET_5, + ACTOR_UNSET_6, + ACTOR_UNSET_17, + ACTOR_UNSET_1A, + ACTOR_UNSET_1F, + ACTOR_UNSET_22, + ACTOR_UNSET_31, + ACTOR_UNSET_36, + ACTOR_UNSET_53, + ACTOR_UNSET_73, + ACTOR_UNSET_74, + ACTOR_UNSET_75, + ACTOR_UNSET_76, + ACTOR_UNSET_78, + ACTOR_UNSET_79, + ACTOR_UNSET_7A, + ACTOR_UNSET_7B, + ACTOR_UNSET_7E, + ACTOR_UNSET_7F, + ACTOR_UNSET_83, + ACTOR_UNSET_A0, + ACTOR_UNSET_B2, + ACTOR_UNSET_CE, + ACTOR_UNSET_D8, + ACTOR_UNSET_EA, + ACTOR_UNSET_EB, + ACTOR_UNSET_F2, + ACTOR_UNSET_F3, + ACTOR_UNSET_FB, + ACTOR_UNSET_109, + ACTOR_UNSET_10D, + ACTOR_UNSET_10E, + ACTOR_UNSET_128, + ACTOR_UNSET_129, + ACTOR_UNSET_134, + ACTOR_UNSET_154, + ACTOR_UNSET_15D, + ACTOR_UNSET_161, + ACTOR_UNSET_180, + ACTOR_UNSET_1AA +}; + +static std::unordered_map> actorSpecificData; + +void CreateActorSpecificData() { + if (!actorSpecificData.empty()) { + return; + } + + actorSpecificData[ACTOR_EN_DEKUNUTS] = [](s16 params) -> s16 { + bool isFlower = params == DEKUNUTS_FLOWER; + s16 shotsPerRound = (params >> 8) & 0xFF; + if (shotsPerRound == 0xFF || shotsPerRound == 0) { + shotsPerRound = 1; + } + ImGui::Checkbox("Flower", &isFlower); + if (!isFlower) { + ImGui::InputScalar("Shots Per Round", ImGuiDataType_S16, &shotsPerRound); + } + + return isFlower ? DEKUNUTS_FLOWER : (shotsPerRound << 8); + }; + + actorSpecificData[ACTOR_EN_TITE] = [](s16 params) -> s16 { + static const char* items[] = { "Blue", "Red" }; + if (params == 0) { + params = -2; + } + //the + 2 is because the params are -2 & -1 instead of 0 & 1 + int selectedItem = params + 2; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem - 2; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_AM] = [](s16 params) -> s16 { + static const char* items[] = { "Statue", "Enemy" }; + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_BG_ICE_TURARA] = [](s16 params) -> s16 { + static const char* items[] = { "Stalagmite", "Stalactite", "Stalactite (Regrow)" }; + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_BG_BREAKWALL] = [](s16 params) -> s16 { + static const char* items[] = { "DC Entrance", "Wall", "KD Floor", "KD Lava Cover" }; + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_TEST] = [](s16 params) -> s16 { + static const char* items[] = { "Invisible", "1", "2", "Ceiling", "4", "5" }; + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_TANA] = [](s16 params) -> s16 { + static const char* items[] = { "Wooden", "Stone (1)", "Stone (2)" }; + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_XC] = [](s16 params) -> s16 { + static const char* items[] = { "0", "1", "2", "3", "4", "5", "Minuet", "Bolero", "Serenade", "9" }; + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_SHOT_SUN] = [](s16 params) -> s16 { + static const char* items[] = { "Sun's Song", "Song of Storms", "LH Sun" }; + if (params == 0) { + params = 0x40; + } + //the - 0x40 is because the params are 0x40 & 0x41 instead of 0 & 1 + int selectedItem = params - 0x40; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem + 0x40; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_HONOTRAP] = [](s16 params) -> s16 { + static const char* items[] = { "Eye", "Flame Move", "Flame Drop" }; + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_REEBA] = [](s16 params) -> s16 { + bool isBig = params != 0; + ImGui::Checkbox("Big", &isBig); + + return isBig; + }; + + actorSpecificData[ACTOR_EN_TK] = [](s16 params) -> s16 { + bool canTurn = params >= 0; + ImGui::Checkbox("Can Turn", &canTurn); + + return canTurn ? 0 : -1; + }; + + actorSpecificData[ACTOR_EN_ITEM00] = [](s16 params) -> s16 { + bool autoCollect = params & 0x8000; + ImGui::Checkbox("Automatically Collect", &autoCollect); + u8 collectibleFlag = (params & 0x3F00) >> 8; + ImGui::InputScalar("Collectible Flag", ImGuiDataType_U8, &collectibleFlag); + if (collectibleFlag > 0x3F) { + collectibleFlag = 0x3F; + } + + static const char* items[] = { + "Green Rupee", + "Blue Rupee", + "Red Rupee", + "Recovery Heart", + "Bombs (A)", + "Arrow", + "Heart Piece", + "Heart Container", + "Arrows (5)", + "Arrows (10)", + "Arrows (30)", + "Bombs (B)", + "Deku Nuts (5)", + "Deku Stick", + "Magic (Large)", + "Magic (Small)", + "Deku Seeds (5)", + "Small Key", + "Flexible", + "Gold Rupee", + "Purple Rupee", + "Deku Shield", + "Hylian Shield", + "Zora Tunic", + "Goron Tunic", + "Bombs (Special)", + "Bombchus" + }; + + int selectedItem = params & 0xFF; + ImGui::Combo("Item", &selectedItem, items, IM_ARRAYSIZE(items)); + + return autoCollect * 0x8000 + (collectibleFlag << 8) + selectedItem; + }; + + actorSpecificData[ACTOR_OBJ_COMB] = [](s16 params) -> s16 { + static const char* items[] = { + "Green Rupee", + "Blue Rupee", + "Red Rupee", + "Recovery Heart", + "Bombs (A)", + "Arrow", + "Heart Piece", + "Heart Container", + "Arrows (5)", + "Arrows (10)", + "Arrows (30)", + "Bombs (B)", + "Deku Nuts (5)", + "Deku Stick", + "Magic (Large)", + "Magic (Small)", + "Deku Seeds (5)", + "Small Key", + "Flexible", + "Gold Rupee", + "Purple Rupee", + "Deku Shield", + "Hylian Shield", + "Zora Tunic", + "Goron Tunic", + "Bombs (Special)", + "Bombchus" + }; + + int selectedItem = params & 0xFF; + ImGui::Combo("Item Drop", &selectedItem, items, IM_ARRAYSIZE(items)); + + u8 collectibleFlag = (params & 0x3F00) >> 8; + if (selectedItem == 6) { + ImGui::InputScalar("PoH Collectible Flag", ImGuiDataType_U8, &collectibleFlag); + if (collectibleFlag > 0x3F) { + collectibleFlag = 0x3F; + } + } + + return (collectibleFlag << 8) + selectedItem; + }; + + actorSpecificData[ACTOR_EN_GM] = [](s16 params) -> s16 { + u8 switchFlag = (params & 0x3F00) >> 8; + + ImGui::InputScalar("Switch Flag", ImGuiDataType_U8, &switchFlag); + if (switchFlag > 0x3F) { + switchFlag = 0x3F; + } + + return switchFlag << 8; + }; + + actorSpecificData[ACTOR_EN_GIRLA] = [](s16 params) -> s16 { + static const char* items[] = { + "Deku Nuts (5)", + "Arrows (30)", + "Arrows (50)", + "Bombs (5) (25 Rupees)", + "Deku Nuts (10)", + "Deku Stick", + "Bombs (10)", + "Fish", + "Red Potion (30 Rupees)", + "Green Potion", + "Blue Potion", + "Longsword", + "Hylian Shield", + "Deku Shield", + "Goron Tunic", + "Zora Tunic", + "Heart", + "Milk Bottle", + "Weird Egg", + "19", + "20", + "Bomchu (10) [1]", + "Bomchu (20) [1]", + "Bomchu (20) [2]", + "Bomchu (10) [2]", + "Bomchu (10) [3]", + "Bomchu (20) [3]", + "Bomchu (20) [4]", + "Bomchu (10) [4]", + "Deku Seeds (30)", + "Keaton Mask", + "Spooky Mask", + "Skull Mask", + "Bunny Hood", + "Mask Of Truth", + "Zora Mask", + "Goron Mask", + "Gerudo Mask", + "Sold Out", + "Blue Fire", + "Bugs", + "Big Poe", + "Poe", + "Fairy", + "Arrows (10)", + "Bombs (20)", + "Bombs (30)", + "Bombs (5) (35 Rupees)", + "Red Potion (40 Rupees)", + "Red Potion (50 Rupees)", + "Randomizer Item" + }; + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_FIRE_ROCK] = [](s16 params) -> s16 { + static const char* items[] = { + "Spawned Falling (1)", + "Broken Piece (1)", + "Broken Piece (2)", + "Spawned Falling (2)", + //"INVALID", + "Ceiling Spot Spawner", + "On Floor" + }; + int selectedItem = params > 3 ? params - 1 : params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem > 3 ? selectedItem + 1 : selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_EX_ITEM] = [](s16 params) -> s16 { + static const char* items[] = { + "Bomb Bag Bowling", + "Heart Piece Bowling", + "Bombchus Bowling", + "Bombs Bowling", + "Purple Rupee Bowling", + "Bomb Bag Counter", + "Heart Piece Counter", + "Bombchus Counter", + "Bombs Counter", + "Purple Rupee Counter", + "Green Rupee Chest", + "Blue Rupee Chest", + "Red Rupee Chest", + "13", + "14", + "Small Key Chest", + "Magic Fire", + "Magic Wind", + "Magic Dark", + "Bullet Bag" + }; + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_ELF] = [](s16 params) -> s16 { + static const char* items[] = { + "Navi", + "Revive Bottle", + "Heal Timed", + "Kokiri", + "Spawner", + "Revive Death", + "Heal", + "Heal Big" + }; + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_CLEAR_TAG] = [](s16 params) -> s16 { + static const char* items[] = { + "Cutscene", //0 + "Normal", //1 + "Laser" //100 + }; + int selectedItem = params == 100 ? 2 : params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem == 2 ? 100 : selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_BOMBF] = [](s16 params) -> s16 { + static const char* items[] = { "Flower", "Body", "Explosion" }; + //the + 1 is because the params are -1, 0 & 1 instead of 0, 1 & 2 + int selectedItem = params + 1; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem - 1; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_BOM] = [](s16 params) -> s16 { + static const char* items[] = { "Body", "Explosion" }; + + int selectedItem = params; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_DOOR_WARP1] = [](s16 params) -> s16 { + static const char* items[] = { + "Blue Crystal", // -2 + "Dungeon Adult", + "Dungeon Child", + "Clear Flag", // Activate on temp clear flag + "Sages", // Used by sages warping into chamber of sages during their cutscene + "Purple Crystal", + "Yellow", // The colored variants don't warp, they are cutscene setpieces + "Blue Ruto", + "Destination", // Spawning in after having taken a warp + "UNK 7", + "Orange", + "Green", + "Red" + }; + int selectedItem = params + 2; + if (ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem - 2; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_DY_EXTRA] = [](s16 params) -> s16 { + static const char* items[] = { "Orange", "Green" }; + + int selectedItem = params; + if (ImGui::Combo("Color", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; + + actorSpecificData[ACTOR_EN_SKB] = [](s16 params) -> s16 { + u8 size = params; + ImGui::InputScalar("Size", ImGuiDataType_U8, &size); + + return size; + }; + + actorSpecificData[ACTOR_EN_WF] = [](s16 params) -> s16 { + static const char* items[] = { "Normal", "White" }; + + int selectedItem = params; + ImGui::Combo("Type", &selectedItem, items, IM_ARRAYSIZE(items)); + + u8 switchFlag = (params & 0x3F00) >> 8; + ImGui::InputScalar("Switch Flag", ImGuiDataType_U8, &switchFlag); + return (switchFlag << 8) + selectedItem; + }; + + actorSpecificData[ACTOR_EN_BOX] = [](s16 params) -> s16 { + /* + trasureFlag = params & 0x1F; //0b0000 0000 0001 1111 + itemId = (params >> 5) & 0x7F; //0b0000 1111 1110 0000 + type = (params >> 12) & 0xF; //0b1111 0000 0000 0000 + */ + u8 treasureFlag = params & 0x1F; + ImGui::InputScalar("Treasure Flag", ImGuiDataType_U8, &treasureFlag); + if (treasureFlag > 0x1F) { + treasureFlag = 0x1F; + } + + u8 itemId = (params >> 5) & 0x7F; + ImGui::InputScalar("Item Id", ImGuiDataType_U8, &itemId); + if (itemId > 0x7F) { + itemId = 0x7F; + } + + static const char* items[] = { + "Big (Default)", + "Room Clear Big", + "Decorated Big", + "Switch Flag Fall Big", + "4", + "Small", + "6", + "Room Clear Small", + "Switch Flag Fall Small", + "9", + "10", + "Switch Flag Big" + }; + + int type = (params >> 12) & 0xF; + ImGui::Combo("Type", &type, items, IM_ARRAYSIZE(items)); + if (type > 0xF) { + type = 0xF; + } + + return (type << 12) + (itemId << 5) + treasureFlag; + }; + + actorSpecificData[ACTOR_EN_DOOR] = [](s16 params) -> s16 { + /** + * Actor Parameters + * + * | | | | + * | Transition Index | Type | Double Door | Switch Flag OR Text Id - 0x0200 + * |------------------|-------|-------------|--------------------------------- + * | 0 0 0 0 0 0 | 0 0 0 | 0 | 0 0 0 0 0 0 + * | 6 | 3 | 1 | 6 + * | + * + * Transition Index 1111110000000000 Set by the actor engine when the door is spawned + * Type 0000001110000000 + * Double Door 0000000001000000 + * Switch Flag 0000000000111111 For use with the `DOOR_LOCKED` type + * Text id - 0x0200 0000000000111111 For use with the `DOOR_CHECKABLE` type + * + */ + u8 transitionIndex = params >> 10; + ImGui::InputScalar("Transition Index", ImGuiDataType_U8, &transitionIndex); + if (transitionIndex > 0x3F) { + transitionIndex = 0x3F; + } + + static const char* items[] = { + "Room Load", // loads rooms + "Locked", // small key locked door + "Room Load (2)", // loads rooms + "Scene Exit", // doesn't load rooms, used for doors paired with scene transition polygons + "Ajar", // open slightly but slams shut if Link gets too close + "Checkable", // doors that display a textbox when interacting + "Evening", // unlocked between 18:00 and 21:00, Dampé's hut + "Room Load (7)" // loads rooms + }; + + int type = (params >> 7) & 7; + ImGui::Combo("Type", &type, items, IM_ARRAYSIZE(items)); + if (type > 7) { + type = 7; + } + + bool doubleDoor = ((params >> 6) & 1) != 0; + ImGui::Checkbox("Double Door", &doubleDoor); + + u8 lowerBits = params & 0x3F; + if (type == 1) { + ImGui::InputScalar("Switch Flag", ImGuiDataType_U8, &lowerBits); + if (lowerBits > 0x3F) { + lowerBits = 0x3F; + } + } else if (type == 5) { + ImGui::InputScalar("Text ID - 0x200", ImGuiDataType_U8, &lowerBits); + if (lowerBits > 0x3F) { + lowerBits = 0x3F; + } + } else { + lowerBits = 0; + } + + return (transitionIndex << 10) + (type << 7) + (doubleDoor << 6) + lowerBits; + }; + + actorSpecificData[ACTOR_EN_PO_DESERT] = [](s16 params) -> s16 { + u8 switchFlag = params >> 8; + + ImGui::InputScalar("Path", ImGuiDataType_U8, &switchFlag); + + return switchFlag << 8; + }; + + actorSpecificData[ACTOR_EN_KANBAN] = [](s16 params) -> s16 { + bool piece = params == (s16)0xFFDD; + bool fishingSign = params == 0x300; + if (ImGui::Checkbox("Piece", &piece)) { + fishingSign = false; + } + if (ImGui::Checkbox("Fishing Sign", &fishingSign)) { + piece = false; + } + + u8 textId = params; + if (!piece && !fishingSign) { + if (ImGui::InputScalar("Text ID", ImGuiDataType_U8, &textId)) { + textId |= 0x300; + } + } + + return piece ? (s16)0xFFDD : (fishingSign ? 0x300 : textId); + }; + + actorSpecificData[ACTOR_EN_KUSA] = [](s16 params) -> s16 { + static const char* items[] = { + "0", + "1", + "2" + }; + + int type = params & 3; + ImGui::Combo("Type", &type, items, IM_ARRAYSIZE(items)); + + bool bugs = ((params >> 4) & 1) != 0; + ImGui::Checkbox("Bugs", &bugs); + + u8 drop = (params >> 8) & 0xF; + if (type == 2) { + ImGui::InputScalar("Random Drop Params", ImGuiDataType_U8, &drop); + if (drop > 0xD) { + drop = 0xD; + } + } else { + drop = 0; + } + + return (drop << 8) + (bugs << 4) + type; + }; + + actorSpecificData[ActorDB::Instance->RetrieveId("En_Partner")] = [](s16 params) -> s16 { + static const char* items[] = { + "Port 1", + "Port 2", + "Port 3", + "Port 4" + }; + int selectedItem = params; + if (ImGui::Combo("Controller Port", &selectedItem, items, IM_ARRAYSIZE(items))) { + return selectedItem; + } + + return params; + }; +} + +std::vector GetActorsWithDescriptionContainingString(std::string s) { + std::locale loc; + for (size_t i = 0; i < s.length(); i += 1) { + s[i] = std::tolower(s[i], loc); + } + + std::vector actors; + for (int i = 0; i < ActorDB::Instance->GetEntryCount(); i += 1) { + ActorDB::Entry actorEntry = ActorDB::Instance->RetrieveEntry(i); + std::string desc = actorEntry.desc; + for (size_t j = 0; j < desc.length(); j += 1) { + desc[j] = std::tolower(desc[j], loc); + } + if (desc.find(s) != std::string::npos) { + actors.push_back((u16)i); + } + } + return actors; +} + void ActorViewer_AddTagForActor(Actor* actor) { int val = CVarGetInteger("gDebugActorViewerNameTags", ACTORVIEWER_NAMETAGS_NONE); auto entry = ActorDB::Instance->RetrieveEntry(actor->id); @@ -163,6 +942,9 @@ void ActorViewerWindow::DrawElement() { static std::string filler = "Please select"; static std::vector list; static u16 lastSceneId = 0; + static char searchString[64] = ""; + static s16 currentSelectedInDropdown; + static std::vector actors; if (gPlayState != nullptr) { needs_reset = lastSceneId != gPlayState->sceneNum; @@ -173,6 +955,11 @@ void ActorViewerWindow::DrawElement() { filler = "Please Select"; list.clear(); needs_reset = false; + for (size_t i = 0; i < ARRAY_COUNT(searchString); i += 1) { + searchString[i] = 0; + } + currentSelectedInDropdown = -1; + actors.clear(); } lastSceneId = gPlayState->sceneNum; if (ImGui::BeginCombo("Actor Type", acMapping[category])) { @@ -316,9 +1103,48 @@ void ActorViewerWindow::DrawElement() { if (ImGui::TreeNode("New...")) { ImGui::PushItemWidth(ImGui::GetFontSize() * 10); + if (ImGui::InputText("Search Actor", searchString, ARRAY_COUNT(searchString))) { + actors = GetActorsWithDescriptionContainingString(std::string(searchString)); + currentSelectedInDropdown = -1; + } + + if (searchString[0] != 0 && !actors.empty()) { + std::string preview = currentSelectedInDropdown == -1 ? "Please Select" : ActorDB::Instance->RetrieveEntry(actors[currentSelectedInDropdown]).desc; + if (ImGui::BeginCombo("Results", preview.c_str())) { + for (u8 i = 0; i < actors.size(); i++) { + if (ImGui::Selectable( + ActorDB::Instance->RetrieveEntry(actors[i]).desc.c_str(), + i == currentSelectedInDropdown + )) { + currentSelectedInDropdown = i; + newActor.id = actors[i]; + } + } + ImGui::EndCombo(); + } + } + ImGui::Text("%s", GetActorDescription(newActor.id).c_str()); - ImGui::InputScalar("ID", ImGuiDataType_S16, &newActor.id, &one); - ImGui::InputScalar("params", ImGuiDataType_S16, &newActor.params, &one); + if (ImGui::InputScalar("ID", ImGuiDataType_S16, &newActor.id, &one)) { + newActor.params = 0; + } + + UIWidgets::EnhancementCheckbox("Advanced mode", "gActorViewerAdvancedParams"); + UIWidgets::InsertHelpHoverText("Changes the actor specific param menus with a direct input"); + + if (CVarGetInteger("gActorViewerAdvancedParams", 0)) { + ImGui::InputScalar("params", ImGuiDataType_S16, &newActor.params, &one); + } else if (std::find(noParamsActors.begin(), noParamsActors.end(), newActor.id) == noParamsActors.end()) { + CreateActorSpecificData(); + if (actorSpecificData.find(newActor.id) == actorSpecificData.end()) { + ImGui::InputScalar("params", ImGuiDataType_S16, &newActor.params, &one); + } else { + DrawGroupWithBorder([&]() { + ImGui::Text("Actor Specific Data"); + newActor.params = actorSpecificData[newActor.id](newActor.params); + }); + } + } ImGui::PushItemWidth(ImGui::GetFontSize() * 6); @@ -401,6 +1227,11 @@ void ActorViewerWindow::DrawElement() { filler = "Please Select"; list.clear(); needs_reset = false; + for (size_t i = 0; i < ARRAY_COUNT(searchString); i += 1) { + searchString[i] = 0; + } + currentSelectedInDropdown = -1; + actors.clear(); } }