From 2c333b51d26d80c743525545ff11394788c27d33 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Sat, 29 Nov 2025 12:12:34 -0800 Subject: [PATCH] add $fastkill command --- README.md | 1 + src/ChatCommands.cc | 19 ++ src/Client.hh | 85 +++--- src/EnemyType.cc | 261 +++++++++--------- src/EnemyType.hh | 4 + src/HTTPServer.cc | 1 + src/Map.cc | 8 +- src/Menu.hh | 15 +- src/ProxyCommands.cc | 24 ++ src/ReceiveCommands.cc | 5 + src/ReceiveSubcommands.cc | 13 +- src/ServerState.cc | 1 + src/ServerState.hh | 1 + .../EnemyDamageSync.3___.patch.s | 2 +- .../EnemyDamageSync.4___.patch.s | 2 +- .../EnemyDamageSync.59NL.patch.s | 2 +- system/config.example.json | 1 + tests/config.json | 1 + 18 files changed, 259 insertions(+), 187 deletions(-) diff --git a/README.md b/README.md index e7d23c8e..0a0bb807 100644 --- a/README.md +++ b/README.md @@ -650,6 +650,7 @@ Some commands only work for clients not in proxy sessions. The chat commands are * `$cheat` (non-proxy only): Enable or disable cheat mode for the current game. All other cheat mode commands do nothing if cheat mode is disabled. By default, cheat mode is off in new games but can be enabled; there is an option in config.json that allows you to disable cheat mode entirely, or set it to on by default in new games. Cheat mode is always enabled on the proxy, unless cheat mode is disabled on the entire server. * `$infhp`: Enable or disable infinite HP mode. Applies to only you; does not affect other players. When enabled, one-hit KO attacks will still kill you, but on most versions of the game, the server will automatically revive you if you die. Infinite HP also automatically cures status ailments. * `$inftp`: Enable or disable infinite TP mode. Applies to only you; does not affect other players. Does not work on DCv1 or earlier versions. + * `$fastkill`: Enable or disable fast kills. Applies to only you; does not affect other players. When enabled, the server will kill any enemy after you hit it once. Bosses are not affected by fast kills. * `$warpme ` (or `$warp `): Warp yourself to the given floor. * `$warpall `: Warp everyone in the game to the given floor. You must be the leader to use this command, unless you're on the proxy. * `$next`: Warp yourself to the next floor. diff --git a/src/ChatCommands.cc b/src/ChatCommands.cc index f1e51804..78a3c50f 100644 --- a/src/ChatCommands.cc +++ b/src/ChatCommands.cc @@ -2196,6 +2196,25 @@ ChatCommandDefinition cc_quest( co_return; }); +ChatCommandDefinition cc_fastkill( + {"$fastkill"}, + +[](const Args& a) -> asio::awaitable { + if (!a.c->proxy_session) { + a.check_is_game(true); + } + + if (a.c->check_flag(Client::Flag::FAST_KILLS_ENABLED)) { + a.c->clear_flag(Client::Flag::FAST_KILLS_ENABLED); + send_text_message(a.c, "$C6Fast kills disabled"); + } else { + auto s = a.c->require_server_state(); + a.check_cheats_enabled_or_allowed(s->cheat_flags.fast_kills); + a.c->set_flag(Client::Flag::FAST_KILLS_ENABLED); + send_text_message(a.c, "$C6Fast kills enabled"); + } + co_return; + }); + ChatCommandDefinition cc_rand( {"$rand"}, +[](const Args& a) -> asio::awaitable { diff --git a/src/Client.hh b/src/Client.hh index eddb2047..985b8177 100644 --- a/src/Client.hh +++ b/src/Client.hh @@ -39,57 +39,58 @@ public: // clang-format off // Version-related flags - CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000002, - NO_D6_AFTER_LOBBY = 0x0000000000000100, - NO_D6 = 0x0000000000000200, - FORCE_ENGLISH_LANGUAGE_BB = 0x0000000000000400, + CHECKED_FOR_DC_V1_PROTOTYPE = 0x0000000000000001, + NO_D6_AFTER_LOBBY = 0x0000000000000002, + NO_D6 = 0x0000000000000004, + FORCE_ENGLISH_LANGUAGE_BB = 0x0000000000000008, // Flags describing the behavior for send_function_call - HAS_SEND_FUNCTION_CALL = 0x0000000000001000, - ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000002000, - SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE = 0x0000000000004000, - SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000008000, - CAN_RECEIVE_ENABLE_B2_QUEST = 0x0000000000020000, - AWAITING_ENABLE_B2_QUEST = 0x0000000000040000, + HAS_SEND_FUNCTION_CALL = 0x0000000000000010, + ENCRYPTED_SEND_FUNCTION_CALL = 0x0000000000000020, + SEND_FUNCTION_CALL_ACTUALLY_RUNS_CODE = 0x0000000000000040, + SEND_FUNCTION_CALL_NO_CACHE_PATCH = 0x0000000000000080, + CAN_RECEIVE_ENABLE_B2_QUEST = 0x0000000000000100, + AWAITING_ENABLE_B2_QUEST = 0x0000000000000200, // State flags - LOADING = 0x0000000000100000, - LOADING_QUEST = 0x0000000000200000, - LOADING_RUNNING_JOINABLE_QUEST = 0x0000000000400000, - LOADING_TOURNAMENT = 0x0000000000800000, - IN_INFORMATION_MENU = 0x0000000001000000, - AT_WELCOME_MESSAGE = 0x0000000002000000, - SAVE_ENABLED = 0x0000000004000000, - HAS_EP3_CARD_DEFS = 0x0000000008000000, - HAS_EP3_MEDIA_UPDATES = 0x0000000010000000, - HAS_AUTO_PATCHES = 0x0000004000000000, - AT_BANK_COUNTER = 0x0000000080000000, - SHOULD_SEND_ARTIFICIAL_ITEM_STATE = 0x0001000000000000, - SHOULD_SEND_ARTIFICIAL_ENEMY_AND_SET_STATE = 0x0040000000000000, - SHOULD_SEND_ARTIFICIAL_OBJECT_STATE = 0x0080000000000000, - SHOULD_SEND_ARTIFICIAL_FLAG_STATE = 0x0002000000000000, - SHOULD_SEND_ARTIFICIAL_PLAYER_STATES = 0x0200000000000000, - SHOULD_SEND_ENABLE_SAVE = 0x0004000000000000, - SWITCH_ASSIST_ENABLED = 0x0000000100000000, - IS_CLIENT_CUSTOMIZATION = 0x0100000000000000, - EP3_ALLOW_6xBC = 0x1000000000000000, + LOADING = 0x0000000000000400, + LOADING_QUEST = 0x0000000000000800, + LOADING_RUNNING_JOINABLE_QUEST = 0x0000000000001000, + LOADING_TOURNAMENT = 0x0000000000002000, + IN_INFORMATION_MENU = 0x0000000000004000, + AT_WELCOME_MESSAGE = 0x0000000000008000, + SAVE_ENABLED = 0x0000000000010000, + HAS_EP3_CARD_DEFS = 0x0000000000020000, + HAS_EP3_MEDIA_UPDATES = 0x0000000000040000, + HAS_AUTO_PATCHES = 0x0000000000080000, + AT_BANK_COUNTER = 0x0000000000100000, + SHOULD_SEND_ARTIFICIAL_ITEM_STATE = 0x0000000000200000, + SHOULD_SEND_ARTIFICIAL_ENEMY_AND_SET_STATE = 0x0000000000400000, + SHOULD_SEND_ARTIFICIAL_OBJECT_STATE = 0x0000000000800000, + SHOULD_SEND_ARTIFICIAL_FLAG_STATE = 0x0000000001000000, + SHOULD_SEND_ARTIFICIAL_PLAYER_STATES = 0x0000000002000000, + SHOULD_SEND_ENABLE_SAVE = 0x0000000004000000, + SWITCH_ASSIST_ENABLED = 0x0000000008000000, + IS_CLIENT_CUSTOMIZATION = 0x0000000010000000, + EP3_ALLOW_6xBC = 0x0000000020000000, // Cheat mode and option flags - INFINITE_HP_ENABLED = 0x0000000200000000, - INFINITE_TP_ENABLED = 0x0000000400000000, - DEBUG_ENABLED = 0x0000000800000000, - ITEM_DROP_NOTIFICATIONS_1 = 0x0010000000000000, - ITEM_DROP_NOTIFICATIONS_2 = 0x0020000000000000, - HAS_ENEMY_DAMAGE_SYNC_PATCH = 0x2000000000000000, // Must be same as in EnemyDamageSync*.s + INFINITE_HP_ENABLED = 0x0000000040000000, + INFINITE_TP_ENABLED = 0x0000000080000000, + FAST_KILLS_ENABLED = 0x0000000100000000, + DEBUG_ENABLED = 0x0000000200000000, + ITEM_DROP_NOTIFICATIONS_1 = 0x0000000400000000, + ITEM_DROP_NOTIFICATIONS_2 = 0x0000000800000000, + HAS_ENEMY_DAMAGE_SYNC_PATCH = 0x0000001000000000, // Must be same as in EnemyDamageSync*.s // Proxy option flags - PROXY_SAVE_FILES = 0x0000001000000000, - PROXY_CHAT_COMMANDS_ENABLED = 0x0000002000000000, + PROXY_SAVE_FILES = 0x0000002000000000, + PROXY_CHAT_COMMANDS_ENABLED = 0x0000004000000000, PROXY_PLAYER_NOTIFICATIONS_ENABLED = 0x0000008000000000, - PROXY_EP3_INFINITE_MESETA_ENABLED = 0x0000080000000000, - PROXY_EP3_INFINITE_TIME_ENABLED = 0x0000100000000000, - PROXY_BLOCK_FUNCTION_CALLS = 0x0000800000000000, - PROXY_EP3_UNMASK_WHISPERS = 0x0008000000000000, + PROXY_EP3_INFINITE_MESETA_ENABLED = 0x0000010000000000, + PROXY_EP3_INFINITE_TIME_ENABLED = 0x0000020000000000, + PROXY_BLOCK_FUNCTION_CALLS = 0x0000040000000000, + PROXY_EP3_UNMASK_WHISPERS = 0x0000080000000000, // clang-format on }; enum class ItemDropNotificationMode { diff --git a/src/EnemyType.cc b/src/EnemyType.cc index d2ef8cb2..d71a2de9 100644 --- a/src/EnemyType.cc +++ b/src/EnemyType.cc @@ -13,139 +13,140 @@ static constexpr uint8_t EP1 = EnemyTypeDefinition::Flag::VALID_EP1; static constexpr uint8_t EP2 = EnemyTypeDefinition::Flag::VALID_EP2; static constexpr uint8_t EP4 = EnemyTypeDefinition::Flag::VALID_EP4; static constexpr uint8_t RARE = EnemyTypeDefinition::Flag::IS_RARE; +static constexpr uint8_t BOSS = EnemyTypeDefinition::Flag::IS_BOSS; static const vector type_defs{ // clang-format off - // TYPE FLAGS RT BP ENUM NAME IN-GAME NAME ULTIMATE NAME - {EnemyType::UNKNOWN, 0, 0xFF, 0xFF, "UNKNOWN", "__UNKNOWN__", nullptr}, - {EnemyType::NONE, 0, 0xFF, 0xFF, "NONE", "__NONE__", nullptr}, - {EnemyType::NON_ENEMY_NPC, EP1 | EP2 | EP4, 0xFF, 0xFF, "NON_ENEMY_NPC", "__NPC__", nullptr}, - {EnemyType::AL_RAPPY, EP1 | RARE, 0x06, 0x19, "AL_RAPPY", "Al Rappy", "Pal Rappy"}, - {EnemyType::ASTARK, EP4, 0x41, 0x09, "ASTARK", "Astark", nullptr}, - {EnemyType::BA_BOOTA, EP4, 0x4F, 0x03, "BA_BOOTA", "Ba Boota", nullptr}, - {EnemyType::BARBA_RAY, EP2, 0x49, 0x0F, "BARBA_RAY", "Barba Ray", nullptr}, - {EnemyType::BARBAROUS_WOLF, EP1 | EP2, 0x08, 0x03, "BARBAROUS_WOLF", "Barbarous Wolf", "Gulgus-gue"}, - {EnemyType::BEE_L, EP1 | EP2, 0xFF, 0xFF, "BEE_L", "Bee L", "Gee L"}, - {EnemyType::BEE_R, EP1 | EP2, 0xFF, 0xFF, "BEE_R", "Bee R", "Gee R"}, - {EnemyType::BOOMA, EP1, 0x09, 0x4B, "BOOMA", "Booma", "Bartle"}, - {EnemyType::BOOTA, EP4, 0x4D, 0x00, "BOOTA", "Boota", nullptr}, - {EnemyType::BULCLAW, EP1, 0x28, 0x1F, "BULCLAW", "Bulclaw", nullptr}, - {EnemyType::BULK, EP1, 0x27, 0x1F, "BULK", "Bulk", nullptr}, - {EnemyType::CANADINE, EP1, 0x1C, 0x07, "CANADINE", "Canadine", "Canabin"}, - {EnemyType::CANADINE_GROUP, EP1, 0x1C, 0x08, "CANADINE_GROUP", "Canadine (group)", "Canabin (group)"}, - {EnemyType::CANANE, EP1, 0x1D, 0x09, "CANANE", "Canane", "Canune"}, - {EnemyType::CHAOS_BRINGER, EP1, 0x24, 0x0D, "CHAOS_BRINGER", "Chaos Bringer", "Dark Bringer"}, - {EnemyType::CHAOS_SORCERER, EP1 | EP2, 0x1F, 0x0A, "CHAOS_SORCERER", "Chaos Sorceror", "Gran Sorceror"}, - {EnemyType::CLAW, EP1, 0x26, 0x20, "CLAW", "Claw", nullptr}, - {EnemyType::DARK_BELRA, EP1 | EP2, 0x25, 0x0E, "DARK_BELRA", "Dark Belra", "Indi Belra"}, - {EnemyType::DARK_FALZ_1, EP1, 0xFF, 0x36, "DARK_FALZ_1", "Dark Falz (phase 1)", nullptr}, - {EnemyType::DARK_FALZ_2, EP1, 0x2F, 0x37, "DARK_FALZ_2", "Dark Falz (phase 2)", nullptr}, - {EnemyType::DARK_FALZ_3, EP1, 0x2F, 0x38, "DARK_FALZ_3", "Dark Falz (phase 3)", nullptr}, - {EnemyType::DARK_GUNNER, EP1, 0x22, 0x1E, "DARK_GUNNER", "Dark Gunner", nullptr}, - {EnemyType::DARK_GUNNER_CONTROL, EP1, 0xFF, 0xFF, "DARK_GUNNER_CONTROL", "Dark Gunner (control)", nullptr}, - {EnemyType::DARVANT, EP1, 0xFF, 0x35, "DARVANT", "Darvant", nullptr}, - {EnemyType::DARVANT_ULTIMATE, EP1, 0xFF, 0x39, "DARVANT_ULTIMATE", "Darvant (ultimate)", nullptr}, - {EnemyType::DE_ROL_LE, EP1, 0x2D, 0x0F, "DE_ROL_LE", "De Rol Le", "Dal Ral Lie"}, - {EnemyType::DE_ROL_LE_BODY, EP1, 0xFF, 0xFF, "DE_ROL_LE_BODY", "De Rol Le (body)", "Dal Ral Lie (body)"}, - {EnemyType::DE_ROL_LE_MINE, EP1, 0xFF, 0xFF, "DE_ROL_LE_MINE", "De Rol Le (mine)", "Dal Ral Lie (mine)"}, - {EnemyType::DEATH_GUNNER, EP1, 0x23, 0x1E, "DEATH_GUNNER", "Death Gunner", nullptr}, - {EnemyType::DEL_LILY, EP2, 0x53, 0x25, "DEL_LILY", "Del Lily", nullptr}, - {EnemyType::DEL_RAPPY_CRATER, EP4, 0x57, 0x06, "DEL_RAPPY_CRATER", "Del Rappy (crater)", nullptr}, - {EnemyType::DEL_RAPPY_DESERT, EP4, 0x58, 0x18, "DEL_RAPPY_DESERT", "Del Rappy (desert)", nullptr}, - {EnemyType::DELBITER, EP2, 0x48, 0x0D, "DELBITER", "Delbiter", nullptr}, - {EnemyType::DELDEPTH, EP2, 0x47, 0x30, "DELDEPTH", "Deldepth", nullptr}, - {EnemyType::DELSABER, EP1 | EP2, 0x1E, 0x52, "DELSABER", "Delsaber", nullptr}, - {EnemyType::DIMENIAN, EP1 | EP2, 0x29, 0x53, "DIMENIAN", "Dimenian", "Arlan"}, - {EnemyType::DOLMDARL, EP2, 0x41, 0x50, "DOLMDARL", "Dolmdarl", nullptr}, - {EnemyType::DOLMOLM, EP2, 0x40, 0x4F, "DOLMOLM", "Dolmolm", nullptr}, - {EnemyType::DORPHON, EP4, 0x50, 0x0F, "DORPHON", "Dorphon", nullptr}, - {EnemyType::DORPHON_ECLAIR, EP4 | RARE, 0x51, 0x10, "DORPHON_ECLAIR", "Dorphon Eclair", nullptr}, - {EnemyType::DRAGON, EP1, 0x2C, 0x12, "DRAGON", "Dragon", "Sil Dragon"}, - {EnemyType::DUBCHIC, EP1 | EP2, 0x18, 0x1B, "DUBCHIC", "Dubchic", "Dubchich"}, - {EnemyType::DUBWITCH, EP1 | EP2, 0xFF, 0xFF, "DUBWITCH", "Dubwitch", "Duvuik"}, - {EnemyType::EGG_RAPPY, EP2, 0x51, 0x19, "EGG_RAPPY", "Egg Rappy", nullptr}, - {EnemyType::EPSIGARD, EP2, 0xFF, 0xFF, "EPSIGARD", "Episgard", nullptr}, - {EnemyType::EPSILON, EP2, 0x54, 0x23, "EPSILON", "Epsilon", nullptr}, - {EnemyType::EVIL_SHARK, EP1, 0x10, 0x4F, "EVIL_SHARK", "Evil Shark", "Vulmer"}, - {EnemyType::GAEL_OR_GIEL, EP2, 0xFF, 0x2E, "GAEL", "Gael/Giel", nullptr}, - {EnemyType::GAL_GRYPHON, EP2, 0x4D, 0x1E, "GAL_GRYPHON", "Gal Gryphon", nullptr}, - {EnemyType::GARANZ, EP1 | EP2, 0x19, 0x1D, "GARANZ", "Garanz", "Baranz"}, - {EnemyType::GEE, EP2, 0x36, 0x07, "GEE", "Gee", nullptr}, - {EnemyType::GI_GUE, EP2, 0x37, 0x1A, "GI_GUE", "Gi Gue", nullptr}, - {EnemyType::GIBBLES, EP2, 0x3D, 0x3D, "GIBBLES", "Gibbles", nullptr}, - {EnemyType::GIGOBOOMA, EP1, 0x0B, 0x4D, "GIGOBOOMA", "Gigobooma", "Tollaw"}, - {EnemyType::GILLCHIC, EP1 | EP2, 0x32, 0x1C, "GILLCHIC", "Gillchic", "Gillchich"}, - {EnemyType::GIRTABLULU, EP4, 0x48, 0x1F, "GIRTABLULU", "Girtablulu", nullptr}, - {EnemyType::GOBOOMA, EP1, 0x0A, 0x4C, "GOBOOMA", "Gobooma", "Barble"}, - {EnemyType::GOL_DRAGON, EP2, 0x4C, 0x12, "GOL_DRAGON", "Gol Dragon", nullptr}, - {EnemyType::GORAN, EP4, 0x52, 0x11, "GORAN", "Goran", nullptr}, - {EnemyType::GORAN_DETONATOR, EP4, 0x53, 0x13, "GORAN_DETONATOR", "Goran Detonator", nullptr}, - {EnemyType::GRASS_ASSASSIN, EP1 | EP2, 0x0C, 0x4E, "GRASS_ASSASSIN", "Grass Assassin", "Crimson Assassin"}, - {EnemyType::GUIL_SHARK, EP1, 0x12, 0x51, "GUIL_SHARK", "Guil Shark", "Melqueek"}, - {EnemyType::HALLO_RAPPY, EP2, 0x50, 0x19, "HALLO_RAPPY", "Hallo Rappy", nullptr}, - {EnemyType::HIDOOM, EP1 | EP2, 0x17, 0x32, "HIDOOM", "Hidoom", nullptr}, - {EnemyType::HILDEBEAR, EP1 | EP2, 0x01, 0x49, "HILDEBEAR", "Hildebear", "Hildelt"}, - {EnemyType::HILDEBLUE, EP1 | EP2 | RARE, 0x02, 0x4A, "HILDEBLUE", "Hildeblue", "Hildetorr"}, - {EnemyType::ILL_GILL, EP2, 0x52, 0x26, "ILL_GILL", "Ill Gill", nullptr}, - {EnemyType::KONDRIEU, EP4 | RARE, 0x5B, 0x2A, "KONDRIEU", "Kondrieu", nullptr}, - {EnemyType::LA_DIMENIAN, EP1 | EP2, 0x2A, 0x54, "LA_DIMENIAN", "La Dimenian", "Merlan"}, - {EnemyType::LOVE_RAPPY, EP2, 0x33, 0x19, "LOVE_RAPPY", "Love Rappy", nullptr}, - {EnemyType::MERICARAND, EP2, 0x38, 0x3A, "MERICARAND", "Mericarand", nullptr}, - {EnemyType::MERICAROL, EP2, 0x38, 0x3A, "MERICAROL", "Mericarol", nullptr}, - {EnemyType::MERICUS, EP2 | RARE, 0x3A, 0x46, "MERICUS", "Mericus", nullptr}, - {EnemyType::MERIKLE, EP2 | RARE, 0x39, 0x45, "MERIKLE", "Merikle", nullptr}, - {EnemyType::MERILLIA, EP2, 0x34, 0x4B, "MERILLIA", "Merillia", nullptr}, - {EnemyType::MERILTAS, EP2, 0x35, 0x4C, "MERILTAS", "Meriltas", nullptr}, - {EnemyType::MERISSA_A, EP4, 0x46, 0x19, "MERISSA_A", "Merissa A", nullptr}, - {EnemyType::MERISSA_AA, EP4 | RARE, 0x47, 0x1A, "MERISSA_AA", "Merissa AA", nullptr}, - {EnemyType::MIGIUM, EP1 | EP2, 0x16, 0x33, "MIGIUM", "Migium", nullptr}, - {EnemyType::MONEST, EP1 | EP2, 0x04, 0x01, "MONEST", "Monest", "Mothvist"}, - {EnemyType::MORFOS, EP2, 0x42, 0x40, "MORFOS", "Morfos", nullptr}, - {EnemyType::MOTHMANT, EP1 | EP2, 0x03, 0x00, "MOTHMANT", "Mothmant", "Mothvert"}, - {EnemyType::NANO_DRAGON, EP1, 0x0F, 0x1A, "NANO_DRAGON", "Nano Dragon", nullptr}, - {EnemyType::NAR_LILY, EP1 | EP2 | RARE, 0x0E, 0x05, "NAR_LILY", "Nar Lily", "Mil Lily"}, - {EnemyType::OLGA_FLOW_1, EP2, 0xFF, 0x2B, "OLGA_FLOW_1", "Olga Flow (phase 1)", nullptr}, - {EnemyType::OLGA_FLOW_2, EP2, 0x4E, 0x2C, "OLGA_FLOW_2", "Olga Flow (phase 2)", nullptr}, - {EnemyType::PAL_SHARK, EP1, 0x11, 0x50, "PAL_SHARK", "Pal Shark", "Govulmer"}, - {EnemyType::PAN_ARMS, EP1 | EP2, 0x15, 0x31, "PAN_ARMS", "Pan Arms", nullptr}, - {EnemyType::PAZUZU_CRATER, EP4 | RARE, 0x4B, 0x08, "PAZUZU_CRATER", "Pazuzu (crater)", nullptr}, - {EnemyType::PAZUZU_DESERT, EP4 | RARE, 0x4C, 0x1C, "PAZUZU_DESERT", "Pazuzu (desert)", nullptr}, - {EnemyType::PIG_RAY, EP2, 0xFF, 0xFF, "PIG_RAY", "Pig Ray", nullptr}, - {EnemyType::POFUILLY_SLIME, EP1, 0x13, 0x30, "POFUILLY_SLIME", "Pofuilly Slime", nullptr}, - {EnemyType::POUILLY_SLIME, EP1 | RARE, 0x14, 0x2F, "POUILLY_SLIME", "Pouilly Slime", nullptr}, - {EnemyType::POISON_LILY, EP1 | EP2, 0x0D, 0x04, "POISON_LILY", "Poison Lily", "Ob Lily"}, - {EnemyType::PYRO_GORAN, EP4, 0x54, 0x12, "PYRO_GORAN", "Pyro Goran", nullptr}, - {EnemyType::RAG_RAPPY, EP1 | EP2, 0x05, 0x18, "RAG_RAPPY", "Rag Rappy", "El Rappy"}, - {EnemyType::RECOBOX, EP2, 0x43, 0x41, "RECOBOX", "Recobox", nullptr}, - {EnemyType::RECON, EP2, 0x44, 0x42, "RECON", "Recon", nullptr}, - {EnemyType::SAINT_MILION, EP4, 0x59, 0x22, "SAINT_MILION", "Saint-Milion", nullptr}, - {EnemyType::SAINT_RAPPY, EP2, 0x4F, 0x19, "SAINT_RAPPY", "Saint Rappy", nullptr}, - {EnemyType::SAND_RAPPY_CRATER, EP4, 0x55, 0x05, "SAND_RAPPY_CRATER", "Sand Rappy (crater)", nullptr}, - {EnemyType::SAND_RAPPY_DESERT, EP4, 0x56, 0x17, "SAND_RAPPY_DESERT", "Sand Rappy (desert)", nullptr}, - {EnemyType::SATELLITE_LIZARD_CRATER, EP4, 0x44, 0x0D, "SATELLITE_LIZARD_CRATER", "Satellite Lizard (crater)", nullptr}, - {EnemyType::SATELLITE_LIZARD_DESERT, EP4, 0x45, 0x1D, "SATELLITE_LIZARD_DESERT", "Satellite Lizard (desert)", nullptr}, - {EnemyType::SAVAGE_WOLF, EP1 | EP2, 0x07, 0x02, "SAVAGE_WOLF", "Savage Wolf", "Gulgus"}, - {EnemyType::SHAMBERTIN, EP4, 0x5A, 0x26, "SHAMBERTIN", "Shambertin", nullptr}, - {EnemyType::SINOW_BEAT, EP1, 0x1A, 0x06, "SINOW_BEAT", "Sinow Beat", "Sinow Blue"}, - {EnemyType::SINOW_BERILL, EP2, 0x3E, 0x06, "SINOW_BERILL", "Sinow Berill", nullptr}, - {EnemyType::SINOW_GOLD, EP1, 0x1B, 0x13, "SINOW_GOLD", "Sinow Gold", "Sinow Red"}, - {EnemyType::SINOW_SPIGELL, EP2, 0x3F, 0x13, "SINOW_SPIGELL", "Sinow Spigell", nullptr}, - {EnemyType::SINOW_ZELE, EP2, 0x46, 0x44, "SINOW_ZELE", "Sinow Zele", nullptr}, - {EnemyType::SINOW_ZOA, EP2, 0x45, 0x43, "SINOW_ZOA", "Sinow Zoa", nullptr}, - {EnemyType::SO_DIMENIAN, EP1 | EP2, 0x2B, 0x55, "SO_DIMENIAN", "So Dimenian", "Del-D"}, - {EnemyType::UL_GIBBON, EP2, 0x3B, 0x3B, "UL_GIBBON", "Ul Gibbon", nullptr}, - {EnemyType::VOL_OPT_1, EP1, 0xFF, 0xFF, "VOL_OPT_1", "Vol Opt (phase 1)", "Vol Opt ver.2 (phase 1)"}, - {EnemyType::VOL_OPT_2, EP1, 0x2E, 0x25, "VOL_OPT_2", "Vol Opt (phase 2)", "Vol Opt ver.2 (phase 2)"}, - {EnemyType::VOL_OPT_AMP, EP1, 0xFF, 0xFF, "VOL_OPT_AMP", "Vol Opt (amp)", "Vol Opt ver.2 (amp)"}, - {EnemyType::VOL_OPT_CORE, EP1, 0xFF, 0xFF, "VOL_OPT_CORE", "Vol Opt (core)", "Vol Opt ver.2 (core)"}, - {EnemyType::VOL_OPT_MONITOR, EP1, 0xFF, 0xFF, "VOL_OPT_MONITOR", "Vol Opt (monitor)", "Vol Opt ver.2 (monitor)"}, - {EnemyType::VOL_OPT_PILLAR, EP1, 0xFF, 0xFF, "VOL_OPT_PILLAR", "Vol Opt (pillar)", "Vol Opt ver.2 (pillar)"}, - {EnemyType::YOWIE_CRATER, EP4, 0x42, 0x0E, "YOWIE_CRATER", "Yowie (crater)", nullptr}, - {EnemyType::YOWIE_DESERT, EP4, 0x43, 0x1E, "YOWIE_DESERT", "Yowie (desert)", nullptr}, - {EnemyType::ZE_BOOTA, EP4, 0x4E, 0x01, "ZE_BOOTA", "Ze Boota", nullptr}, - {EnemyType::ZOL_GIBBON, EP2, 0x3C, 0x3C, "ZOL_GIBBON", "Zol Gibbon", nullptr}, - {EnemyType::ZU_CRATER, EP4, 0x49, 0x07, "ZU_CRATER", "Zu (crater)", nullptr}, - {EnemyType::ZU_DESERT, EP4, 0x4A, 0x1B, "ZU_DESERT", "Zu (desert)", nullptr}, + // TYPE FLAGS RT BP ENUM NAME IN-GAME NAME ULTIMATE NAME + {EnemyType::UNKNOWN, 0, 0xFF, 0xFF, "UNKNOWN", "__UNKNOWN__", nullptr}, + {EnemyType::NONE, 0, 0xFF, 0xFF, "NONE", "__NONE__", nullptr}, + {EnemyType::NON_ENEMY_NPC, EP1 | EP2 | EP4, 0xFF, 0xFF, "NON_ENEMY_NPC", "__NPC__", nullptr}, + {EnemyType::AL_RAPPY, EP1 | RARE, 0x06, 0x19, "AL_RAPPY", "Al Rappy", "Pal Rappy"}, + {EnemyType::ASTARK, EP4, 0x41, 0x09, "ASTARK", "Astark", nullptr}, + {EnemyType::BA_BOOTA, EP4, 0x4F, 0x03, "BA_BOOTA", "Ba Boota", nullptr}, + {EnemyType::BARBA_RAY, EP2 | BOSS, 0x49, 0x0F, "BARBA_RAY", "Barba Ray", nullptr}, + {EnemyType::BARBAROUS_WOLF, EP1 | EP2, 0x08, 0x03, "BARBAROUS_WOLF", "Barbarous Wolf", "Gulgus-gue"}, + {EnemyType::BEE_L, EP1 | EP2, 0xFF, 0xFF, "BEE_L", "Bee L", "Gee L"}, + {EnemyType::BEE_R, EP1 | EP2, 0xFF, 0xFF, "BEE_R", "Bee R", "Gee R"}, + {EnemyType::BOOMA, EP1, 0x09, 0x4B, "BOOMA", "Booma", "Bartle"}, + {EnemyType::BOOTA, EP4, 0x4D, 0x00, "BOOTA", "Boota", nullptr}, + {EnemyType::BULCLAW, EP1, 0x28, 0x1F, "BULCLAW", "Bulclaw", nullptr}, + {EnemyType::BULK, EP1, 0x27, 0x1F, "BULK", "Bulk", nullptr}, + {EnemyType::CANADINE, EP1, 0x1C, 0x07, "CANADINE", "Canadine", "Canabin"}, + {EnemyType::CANADINE_GROUP, EP1, 0x1C, 0x08, "CANADINE_GROUP", "Canadine (group)", "Canabin (group)"}, + {EnemyType::CANANE, EP1, 0x1D, 0x09, "CANANE", "Canane", "Canune"}, + {EnemyType::CHAOS_BRINGER, EP1, 0x24, 0x0D, "CHAOS_BRINGER", "Chaos Bringer", "Dark Bringer"}, + {EnemyType::CHAOS_SORCERER, EP1 | EP2, 0x1F, 0x0A, "CHAOS_SORCERER", "Chaos Sorceror", "Gran Sorceror"}, + {EnemyType::CLAW, EP1, 0x26, 0x20, "CLAW", "Claw", nullptr}, + {EnemyType::DARK_BELRA, EP1 | EP2, 0x25, 0x0E, "DARK_BELRA", "Dark Belra", "Indi Belra"}, + {EnemyType::DARK_FALZ_1, EP1 | BOSS, 0xFF, 0x36, "DARK_FALZ_1", "Dark Falz (phase 1)", nullptr}, + {EnemyType::DARK_FALZ_2, EP1 | BOSS, 0x2F, 0x37, "DARK_FALZ_2", "Dark Falz (phase 2)", nullptr}, + {EnemyType::DARK_FALZ_3, EP1 | BOSS, 0x2F, 0x38, "DARK_FALZ_3", "Dark Falz (phase 3)", nullptr}, + {EnemyType::DARK_GUNNER, EP1, 0x22, 0x1E, "DARK_GUNNER", "Dark Gunner", nullptr}, + {EnemyType::DARK_GUNNER_CONTROL, EP1, 0xFF, 0xFF, "DARK_GUNNER_CONTROL", "Dark Gunner (control)", nullptr}, + {EnemyType::DARVANT, EP1, 0xFF, 0x35, "DARVANT", "Darvant", nullptr}, + {EnemyType::DARVANT_ULTIMATE, EP1, 0xFF, 0x39, "DARVANT_ULTIMATE", "Darvant (ultimate)", nullptr}, + {EnemyType::DE_ROL_LE, EP1 | BOSS, 0x2D, 0x0F, "DE_ROL_LE", "De Rol Le", "Dal Ral Lie"}, + {EnemyType::DE_ROL_LE_BODY, EP1 | BOSS, 0xFF, 0xFF, "DE_ROL_LE_BODY", "De Rol Le (body)", "Dal Ral Lie (body)"}, + {EnemyType::DE_ROL_LE_MINE, EP1 | BOSS, 0xFF, 0xFF, "DE_ROL_LE_MINE", "De Rol Le (mine)", "Dal Ral Lie (mine)"}, + {EnemyType::DEATH_GUNNER, EP1, 0x23, 0x1E, "DEATH_GUNNER", "Death Gunner", nullptr}, + {EnemyType::DEL_LILY, EP2, 0x53, 0x25, "DEL_LILY", "Del Lily", nullptr}, + {EnemyType::DEL_RAPPY_CRATER, EP4, 0x57, 0x06, "DEL_RAPPY_CRATER", "Del Rappy (crater)", nullptr}, + {EnemyType::DEL_RAPPY_DESERT, EP4, 0x58, 0x18, "DEL_RAPPY_DESERT", "Del Rappy (desert)", nullptr}, + {EnemyType::DELBITER, EP2, 0x48, 0x0D, "DELBITER", "Delbiter", nullptr}, + {EnemyType::DELDEPTH, EP2, 0x47, 0x30, "DELDEPTH", "Deldepth", nullptr}, + {EnemyType::DELSABER, EP1 | EP2, 0x1E, 0x52, "DELSABER", "Delsaber", nullptr}, + {EnemyType::DIMENIAN, EP1 | EP2, 0x29, 0x53, "DIMENIAN", "Dimenian", "Arlan"}, + {EnemyType::DOLMDARL, EP2, 0x41, 0x50, "DOLMDARL", "Dolmdarl", nullptr}, + {EnemyType::DOLMOLM, EP2, 0x40, 0x4F, "DOLMOLM", "Dolmolm", nullptr}, + {EnemyType::DORPHON, EP4, 0x50, 0x0F, "DORPHON", "Dorphon", nullptr}, + {EnemyType::DORPHON_ECLAIR, EP4 | RARE, 0x51, 0x10, "DORPHON_ECLAIR", "Dorphon Eclair", nullptr}, + {EnemyType::DRAGON, EP1 | BOSS, 0x2C, 0x12, "DRAGON", "Dragon", "Sil Dragon"}, + {EnemyType::DUBCHIC, EP1 | EP2, 0x18, 0x1B, "DUBCHIC", "Dubchic", "Dubchich"}, + {EnemyType::DUBWITCH, EP1 | EP2, 0xFF, 0xFF, "DUBWITCH", "Dubwitch", "Duvuik"}, + {EnemyType::EGG_RAPPY, EP2, 0x51, 0x19, "EGG_RAPPY", "Egg Rappy", nullptr}, + {EnemyType::EPSIGARD, EP2, 0xFF, 0xFF, "EPSIGARD", "Episgard", nullptr}, + {EnemyType::EPSILON, EP2, 0x54, 0x23, "EPSILON", "Epsilon", nullptr}, + {EnemyType::EVIL_SHARK, EP1, 0x10, 0x4F, "EVIL_SHARK", "Evil Shark", "Vulmer"}, + {EnemyType::GAEL_OR_GIEL, EP2, 0xFF, 0x2E, "GAEL", "Gael/Giel", nullptr}, + {EnemyType::GAL_GRYPHON, EP2 | BOSS, 0x4D, 0x1E, "GAL_GRYPHON", "Gal Gryphon", nullptr}, + {EnemyType::GARANZ, EP1 | EP2, 0x19, 0x1D, "GARANZ", "Garanz", "Baranz"}, + {EnemyType::GEE, EP2, 0x36, 0x07, "GEE", "Gee", nullptr}, + {EnemyType::GI_GUE, EP2, 0x37, 0x1A, "GI_GUE", "Gi Gue", nullptr}, + {EnemyType::GIBBLES, EP2, 0x3D, 0x3D, "GIBBLES", "Gibbles", nullptr}, + {EnemyType::GIGOBOOMA, EP1, 0x0B, 0x4D, "GIGOBOOMA", "Gigobooma", "Tollaw"}, + {EnemyType::GILLCHIC, EP1 | EP2, 0x32, 0x1C, "GILLCHIC", "Gillchic", "Gillchich"}, + {EnemyType::GIRTABLULU, EP4, 0x48, 0x1F, "GIRTABLULU", "Girtablulu", nullptr}, + {EnemyType::GOBOOMA, EP1, 0x0A, 0x4C, "GOBOOMA", "Gobooma", "Barble"}, + {EnemyType::GOL_DRAGON, EP2 | BOSS, 0x4C, 0x12, "GOL_DRAGON", "Gol Dragon", nullptr}, + {EnemyType::GORAN, EP4, 0x52, 0x11, "GORAN", "Goran", nullptr}, + {EnemyType::GORAN_DETONATOR, EP4, 0x53, 0x13, "GORAN_DETONATOR", "Goran Detonator", nullptr}, + {EnemyType::GRASS_ASSASSIN, EP1 | EP2, 0x0C, 0x4E, "GRASS_ASSASSIN", "Grass Assassin", "Crimson Assassin"}, + {EnemyType::GUIL_SHARK, EP1, 0x12, 0x51, "GUIL_SHARK", "Guil Shark", "Melqueek"}, + {EnemyType::HALLO_RAPPY, EP2, 0x50, 0x19, "HALLO_RAPPY", "Hallo Rappy", nullptr}, + {EnemyType::HIDOOM, EP1 | EP2, 0x17, 0x32, "HIDOOM", "Hidoom", nullptr}, + {EnemyType::HILDEBEAR, EP1 | EP2, 0x01, 0x49, "HILDEBEAR", "Hildebear", "Hildelt"}, + {EnemyType::HILDEBLUE, EP1 | EP2 | RARE, 0x02, 0x4A, "HILDEBLUE", "Hildeblue", "Hildetorr"}, + {EnemyType::ILL_GILL, EP2, 0x52, 0x26, "ILL_GILL", "Ill Gill", nullptr}, + {EnemyType::KONDRIEU, EP4 | RARE | BOSS, 0x5B, 0x2A, "KONDRIEU", "Kondrieu", nullptr}, + {EnemyType::LA_DIMENIAN, EP1 | EP2, 0x2A, 0x54, "LA_DIMENIAN", "La Dimenian", "Merlan"}, + {EnemyType::LOVE_RAPPY, EP2, 0x33, 0x19, "LOVE_RAPPY", "Love Rappy", nullptr}, + {EnemyType::MERICARAND, EP2, 0x38, 0x3A, "MERICARAND", "Mericarand", nullptr}, + {EnemyType::MERICAROL, EP2, 0x38, 0x3A, "MERICAROL", "Mericarol", nullptr}, + {EnemyType::MERICUS, EP2 | RARE, 0x3A, 0x46, "MERICUS", "Mericus", nullptr}, + {EnemyType::MERIKLE, EP2 | RARE, 0x39, 0x45, "MERIKLE", "Merikle", nullptr}, + {EnemyType::MERILLIA, EP2, 0x34, 0x4B, "MERILLIA", "Merillia", nullptr}, + {EnemyType::MERILTAS, EP2, 0x35, 0x4C, "MERILTAS", "Meriltas", nullptr}, + {EnemyType::MERISSA_A, EP4, 0x46, 0x19, "MERISSA_A", "Merissa A", nullptr}, + {EnemyType::MERISSA_AA, EP4 | RARE, 0x47, 0x1A, "MERISSA_AA", "Merissa AA", nullptr}, + {EnemyType::MIGIUM, EP1 | EP2, 0x16, 0x33, "MIGIUM", "Migium", nullptr}, + {EnemyType::MONEST, EP1 | EP2, 0x04, 0x01, "MONEST", "Monest", "Mothvist"}, + {EnemyType::MORFOS, EP2, 0x42, 0x40, "MORFOS", "Morfos", nullptr}, + {EnemyType::MOTHMANT, EP1 | EP2, 0x03, 0x00, "MOTHMANT", "Mothmant", "Mothvert"}, + {EnemyType::NANO_DRAGON, EP1, 0x0F, 0x1A, "NANO_DRAGON", "Nano Dragon", nullptr}, + {EnemyType::NAR_LILY, EP1 | EP2 | RARE, 0x0E, 0x05, "NAR_LILY", "Nar Lily", "Mil Lily"}, + {EnemyType::OLGA_FLOW_1, EP2 | BOSS, 0xFF, 0x2B, "OLGA_FLOW_1", "Olga Flow (phase 1)", nullptr}, + {EnemyType::OLGA_FLOW_2, EP2 | BOSS, 0x4E, 0x2C, "OLGA_FLOW_2", "Olga Flow (phase 2)", nullptr}, + {EnemyType::PAL_SHARK, EP1, 0x11, 0x50, "PAL_SHARK", "Pal Shark", "Govulmer"}, + {EnemyType::PAN_ARMS, EP1 | EP2, 0x15, 0x31, "PAN_ARMS", "Pan Arms", nullptr}, + {EnemyType::PAZUZU_CRATER, EP4 | RARE, 0x4B, 0x08, "PAZUZU_CRATER", "Pazuzu (crater)", nullptr}, + {EnemyType::PAZUZU_DESERT, EP4 | RARE, 0x4C, 0x1C, "PAZUZU_DESERT", "Pazuzu (desert)", nullptr}, + {EnemyType::PIG_RAY, EP2, 0xFF, 0xFF, "PIG_RAY", "Pig Ray", nullptr}, + {EnemyType::POFUILLY_SLIME, EP1, 0x13, 0x30, "POFUILLY_SLIME", "Pofuilly Slime", nullptr}, + {EnemyType::POUILLY_SLIME, EP1 | RARE, 0x14, 0x2F, "POUILLY_SLIME", "Pouilly Slime", nullptr}, + {EnemyType::POISON_LILY, EP1 | EP2, 0x0D, 0x04, "POISON_LILY", "Poison Lily", "Ob Lily"}, + {EnemyType::PYRO_GORAN, EP4, 0x54, 0x12, "PYRO_GORAN", "Pyro Goran", nullptr}, + {EnemyType::RAG_RAPPY, EP1 | EP2, 0x05, 0x18, "RAG_RAPPY", "Rag Rappy", "El Rappy"}, + {EnemyType::RECOBOX, EP2, 0x43, 0x41, "RECOBOX", "Recobox", nullptr}, + {EnemyType::RECON, EP2, 0x44, 0x42, "RECON", "Recon", nullptr}, + {EnemyType::SAINT_MILION, EP4 | BOSS, 0x59, 0x22, "SAINT_MILION", "Saint-Milion", nullptr}, + {EnemyType::SAINT_RAPPY, EP2, 0x4F, 0x19, "SAINT_RAPPY", "Saint Rappy", nullptr}, + {EnemyType::SAND_RAPPY_CRATER, EP4, 0x55, 0x05, "SAND_RAPPY_CRATER", "Sand Rappy (crater)", nullptr}, + {EnemyType::SAND_RAPPY_DESERT, EP4, 0x56, 0x17, "SAND_RAPPY_DESERT", "Sand Rappy (desert)", nullptr}, + {EnemyType::SATELLITE_LIZARD_CRATER, EP4, 0x44, 0x0D, "SATELLITE_LIZARD_CRATER", "Satellite Lizard (crater)", nullptr}, + {EnemyType::SATELLITE_LIZARD_DESERT, EP4, 0x45, 0x1D, "SATELLITE_LIZARD_DESERT", "Satellite Lizard (desert)", nullptr}, + {EnemyType::SAVAGE_WOLF, EP1 | EP2, 0x07, 0x02, "SAVAGE_WOLF", "Savage Wolf", "Gulgus"}, + {EnemyType::SHAMBERTIN, EP4 | BOSS, 0x5A, 0x26, "SHAMBERTIN", "Shambertin", nullptr}, + {EnemyType::SINOW_BEAT, EP1, 0x1A, 0x06, "SINOW_BEAT", "Sinow Beat", "Sinow Blue"}, + {EnemyType::SINOW_BERILL, EP2, 0x3E, 0x06, "SINOW_BERILL", "Sinow Berill", nullptr}, + {EnemyType::SINOW_GOLD, EP1, 0x1B, 0x13, "SINOW_GOLD", "Sinow Gold", "Sinow Red"}, + {EnemyType::SINOW_SPIGELL, EP2, 0x3F, 0x13, "SINOW_SPIGELL", "Sinow Spigell", nullptr}, + {EnemyType::SINOW_ZELE, EP2, 0x46, 0x44, "SINOW_ZELE", "Sinow Zele", nullptr}, + {EnemyType::SINOW_ZOA, EP2, 0x45, 0x43, "SINOW_ZOA", "Sinow Zoa", nullptr}, + {EnemyType::SO_DIMENIAN, EP1 | EP2, 0x2B, 0x55, "SO_DIMENIAN", "So Dimenian", "Del-D"}, + {EnemyType::UL_GIBBON, EP2, 0x3B, 0x3B, "UL_GIBBON", "Ul Gibbon", nullptr}, + {EnemyType::VOL_OPT_1, EP1 | BOSS, 0xFF, 0xFF, "VOL_OPT_1", "Vol Opt (phase 1)", "Vol Opt ver.2 (phase 1)"}, + {EnemyType::VOL_OPT_2, EP1 | BOSS, 0x2E, 0x25, "VOL_OPT_2", "Vol Opt (phase 2)", "Vol Opt ver.2 (phase 2)"}, + {EnemyType::VOL_OPT_AMP, EP1 | BOSS, 0xFF, 0xFF, "VOL_OPT_AMP", "Vol Opt (amp)", "Vol Opt ver.2 (amp)"}, + {EnemyType::VOL_OPT_CORE, EP1 | BOSS, 0xFF, 0xFF, "VOL_OPT_CORE", "Vol Opt (core)", "Vol Opt ver.2 (core)"}, + {EnemyType::VOL_OPT_MONITOR, EP1 | BOSS, 0xFF, 0xFF, "VOL_OPT_MONITOR", "Vol Opt (monitor)", "Vol Opt ver.2 (monitor)"}, + {EnemyType::VOL_OPT_PILLAR, EP1 | BOSS, 0xFF, 0xFF, "VOL_OPT_PILLAR", "Vol Opt (pillar)", "Vol Opt ver.2 (pillar)"}, + {EnemyType::YOWIE_CRATER, EP4, 0x42, 0x0E, "YOWIE_CRATER", "Yowie (crater)", nullptr}, + {EnemyType::YOWIE_DESERT, EP4, 0x43, 0x1E, "YOWIE_DESERT", "Yowie (desert)", nullptr}, + {EnemyType::ZE_BOOTA, EP4, 0x4E, 0x01, "ZE_BOOTA", "Ze Boota", nullptr}, + {EnemyType::ZOL_GIBBON, EP2, 0x3C, 0x3C, "ZOL_GIBBON", "Zol Gibbon", nullptr}, + {EnemyType::ZU_CRATER, EP4, 0x49, 0x07, "ZU_CRATER", "Zu (crater)", nullptr}, + {EnemyType::ZU_DESERT, EP4, 0x4A, 0x1B, "ZU_DESERT", "Zu (desert)", nullptr}, // clang-format on }; diff --git a/src/EnemyType.hh b/src/EnemyType.hh index bf824d52..96400b07 100644 --- a/src/EnemyType.hh +++ b/src/EnemyType.hh @@ -146,6 +146,7 @@ struct EnemyTypeDefinition { VALID_EP2 = 0x02, VALID_EP4 = 0x04, IS_RARE = 0x08, + IS_BOSS = 0x10, }; EnemyType type; uint8_t flags; @@ -170,6 +171,9 @@ struct EnemyTypeDefinition { inline bool is_rare() const { return (this->flags & Flag::IS_RARE); } + inline bool is_boss() const { + return (this->flags & Flag::IS_BOSS); + } EnemyType rare_type(Episode episode, uint8_t event, uint8_t floor) const; }; diff --git a/src/HTTPServer.cc b/src/HTTPServer.cc index f6d4aa5f..6a91cd1e 100644 --- a/src/HTTPServer.cc +++ b/src/HTTPServer.cc @@ -88,6 +88,7 @@ HTTPServer::HTTPServer(shared_ptr state) {"SwitchAssistEnabled", (c->check_flag(Client::Flag::SWITCH_ASSIST_ENABLED) ? true : false)}, {"InfiniteHPEnabled", (c->check_flag(Client::Flag::INFINITE_HP_ENABLED) ? true : false)}, {"InfiniteTPEnabled", (c->check_flag(Client::Flag::INFINITE_TP_ENABLED) ? true : false)}, + {"FastKillsEnabled", (c->check_flag(Client::Flag::FAST_KILLS_ENABLED) ? true : false)}, {"DropNotificationMode", drop_notifications_mode}, {"DebugEnabled", (c->check_flag(Client::Flag::DEBUG_ENABLED) ? true : false)}, {"ProxySaveFilesEnabled", (c->check_flag(Client::Flag::PROXY_SAVE_FILES) ? true : false)}, diff --git a/src/Map.cc b/src/Map.cc index d5dc1dda..022133af 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -3069,10 +3069,10 @@ static const vector dat_enemy_definitions({ // stands still instead) // param6 = type (0 = Booma, 1 = Gobooma, 2 = Gigobooma) // param7 = group ID (if nonzero, it looks like this is used to cause - // groups of enemies to band together and all attack the same player, - // chosen by the highest-ranking enemy (by param6) in the group; - // TODO: this explanation is unverified and param7 was never used by - // Sega; see client code at 3OE1:800F6F3C) + // groups of enemies in the same room to band together and all attack + // the same player, chosen by the highest-ranking enemy (by param6) in + // the group; TODO: this explanation is unverified and param7 was never + // used by Sega; see client code at 3OE1:800F6F3C) {0x0044, F_V0_V4, 0x0000000000000006, "TObjEneBeast"}, // Grass Assassin. Params: diff --git a/src/Menu.hh b/src/Menu.hh index 716a0c02..65e3338f 100644 --- a/src/Menu.hh +++ b/src/Menu.hh @@ -75,13 +75,14 @@ constexpr uint32_t PLAYER_NOTIFICATIONS = 0xAA0202AA; constexpr uint32_t DROP_NOTIFICATIONS = 0xAA0303AA; constexpr uint32_t INFINITE_HP = 0xAA0404AA; constexpr uint32_t INFINITE_TP = 0xAA0505AA; -constexpr uint32_t SWITCH_ASSIST = 0xAA0606AA; -constexpr uint32_t BLOCK_EVENTS = 0xAA0707AA; -constexpr uint32_t BLOCK_PATCHES = 0xAA0808AA; -constexpr uint32_t SAVE_FILES = 0xAA0909AA; -constexpr uint32_t EP3_INFINITE_MESETA = 0xAA0A0AAA; -constexpr uint32_t EP3_INFINITE_TIME = 0xAA0B0BAA; -constexpr uint32_t EP3_UNMASK_WHISPERS = 0xAA0C0CAA; +constexpr uint32_t FAST_KILLS = 0xAA0606AA; +constexpr uint32_t SWITCH_ASSIST = 0xAA0707AA; +constexpr uint32_t BLOCK_EVENTS = 0xAA0808AA; +constexpr uint32_t BLOCK_PATCHES = 0xAA0909AA; +constexpr uint32_t SAVE_FILES = 0xAA0A0AAA; +constexpr uint32_t EP3_INFINITE_MESETA = 0xAA0B0BAA; +constexpr uint32_t EP3_INFINITE_TIME = 0xAA0C0CAA; +constexpr uint32_t EP3_UNMASK_WHISPERS = 0xAA0D0DAA; } // namespace ProxyOptionsMenuItemID namespace TeamRewardMenuItemID { diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index 48ac6dae..b6e444ee 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -1998,6 +1998,30 @@ asio::awaitable C_6x(shared_ptr c, Channel::Message& msg) } break; + case 0x0A: { + if (c->check_flag(Client::Flag::FAST_KILLS_ENABLED)) { + auto handle_6x0A = [&]() -> void { + auto& cmd = msg.check_size_t>(); + bool is_boss = false; + if (c->proxy_session->map_state) { + auto ene_st = c->proxy_session->map_state->enemy_state_for_index(c->version(), cmd.enemy_index); + is_boss = type_definition_for_enemy(ene_st->super_ene->type).is_boss(); + } + if (!is_boss && !(cmd.game_flags & 0x00000800)) { + cmd.game_flags |= 0x00000800; + c->channel->send(0x60, 0x00, &cmd, sizeof(cmd)); + modified = true; + } + }; + if (is_gc(c->version())) { + handle_6x0A.template operator()(); + } else { + handle_6x0A.template operator()(); + } + } + break; + } + case 0x0C: if (c->check_flag(Client::Flag::INFINITE_HP_ENABLED)) { co_await send_remove_negative_conditions(c); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index 065effba..b2b1d22d 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -212,6 +212,8 @@ static shared_ptr proxy_options_menu_for_client(shared_ptr on_10_proxy_options(shared_ptr c, uint32_t case ProxyOptionsMenuItemID::INFINITE_TP: c->toggle_flag(Client::Flag::INFINITE_TP_ENABLED); break; + case ProxyOptionsMenuItemID::FAST_KILLS: + c->toggle_flag(Client::Flag::FAST_KILLS_ENABLED); + break; case ProxyOptionsMenuItemID::SWITCH_ASSIST: c->toggle_flag(Client::Flag::SWITCH_ASSIST_ENABLED); break; diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 8022b78b..59f26cff 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -3466,8 +3466,19 @@ static asio::awaitable on_update_enemy_state(shared_ptr c, Subcomm ene_st->alias_target_ene_st->e_id, ene_st->alias_target_ene_st->total_damage, ene_st->alias_target_ene_st->game_flags); } + // TODO: It'd be nice if this worked on bosses too, but it seems we have to + // use each boss' specific state-syncing command, or the cutscenes misbehave. + // Just setting flag 0x800 does work on Vol Opt (and the various parts), but + // doesn't work on other Episode 1 bosses. Other episodes are not yet tested. + bool is_fast_kill = c->check_flag(Client::Flag::FAST_KILLS_ENABLED) && + !type_definition_for_enemy(ene_st->super_ene->type).is_boss() && + !(ene_st->game_flags & 0x00000800); + if (is_fast_kill) { + ene_st->game_flags |= 0x00000800; + } + for (auto lc : l->clients) { - if (lc && (lc != c)) { + if (lc && (is_fast_kill || (lc != c))) { cmd.enemy_index = l->map_state->index_for_enemy_state(lc->version(), ene_st); if (cmd.enemy_index != 0xFFFF) { cmd.header.entity_id = 0x1000 | cmd.enemy_index; diff --git a/src/ServerState.cc b/src/ServerState.cc index a2afb656..b28a2b3e 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -40,6 +40,7 @@ CheatFlags::CheatFlags(const phosg::JSON& json) : CheatFlags() { this->ep3_replace_assist = enabled_keys.count("Ep3ReplaceAssist"); this->ep3_unset_field_character = enabled_keys.count("Ep3UnsetFieldCharacter"); this->infinite_hp_tp = enabled_keys.count("InfiniteHPTP"); + this->fast_kills = enabled_keys.count("FastKills"); this->insufficient_minimum_level = enabled_keys.count("InsufficientMinimumLevel"); this->override_random_seed = enabled_keys.count("OverrideRandomSeed"); this->override_section_id = enabled_keys.count("OverrideSectionID"); diff --git a/src/ServerState.hh b/src/ServerState.hh index d7599d9f..ec6383d5 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -52,6 +52,7 @@ struct CheatFlags { bool ep3_replace_assist = true; bool ep3_unset_field_character = true; bool infinite_hp_tp = true; + bool fast_kills = true; bool insufficient_minimum_level = true; bool override_random_seed = true; bool override_section_id = true; diff --git a/system/client-functions/EnemyDamageSync/EnemyDamageSync.3___.patch.s b/system/client-functions/EnemyDamageSync/EnemyDamageSync.3___.patch.s index 15739687..686a75d2 100644 --- a/system/client-functions/EnemyDamageSync/EnemyDamageSync.3___.patch.s +++ b/system/client-functions/EnemyDamageSync/EnemyDamageSync.3___.patch.s @@ -1,6 +1,6 @@ .meta name="DMC" .meta description="Mitigates effects\nof enemy health\ndesync" -.meta client_flag="0x2000000000000000" +.meta client_flag="0x0000001000000000" .versions 3OJ2 3OJ3 3OJ4 3OJ5 3OE0 3OE1 3OE2 3OP0 diff --git a/system/client-functions/EnemyDamageSync/EnemyDamageSync.4___.patch.s b/system/client-functions/EnemyDamageSync/EnemyDamageSync.4___.patch.s index ee2c4f33..fab791d3 100644 --- a/system/client-functions/EnemyDamageSync/EnemyDamageSync.4___.patch.s +++ b/system/client-functions/EnemyDamageSync/EnemyDamageSync.4___.patch.s @@ -1,6 +1,6 @@ .meta name="DMC" .meta description="Mitigates effects\nof enemy health\ndesync" -.meta client_flag="0x2000000000000000" +.meta client_flag="0x0000001000000000" .versions 4OJB 4OJD 4OJU 4OED 4OEU 4OPD 4OPU diff --git a/system/client-functions/EnemyDamageSync/EnemyDamageSync.59NL.patch.s b/system/client-functions/EnemyDamageSync/EnemyDamageSync.59NL.patch.s index 90e66651..8ad0ee0d 100644 --- a/system/client-functions/EnemyDamageSync/EnemyDamageSync.59NL.patch.s +++ b/system/client-functions/EnemyDamageSync/EnemyDamageSync.59NL.patch.s @@ -1,6 +1,6 @@ .meta name="DMC" .meta description="Mitigates effects\nof enemy health\ndesync" -.meta client_flag="0x2000000000000000" +.meta client_flag="0x0000001000000000" entry_ptr: reloc0: diff --git a/system/config.example.json b/system/config.example.json index 126ff726..998082e6 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -1003,6 +1003,7 @@ "Ep3ReplaceAssist", // Use of $setassist "Ep3UnsetFieldCharacter", // Use of $unset "InfiniteHPTP", // Use of $infhp and $inftp + "FastKills", // Use of $fastkill "InsufficientMinimumLevel", // Setting a $minlevel below the default "OverrideRandomSeed", // Use of $rand "OverrideSectionID", // Use of $secid diff --git a/tests/config.json b/tests/config.json index b3942dd2..d1f4a0e9 100644 --- a/tests/config.json +++ b/tests/config.json @@ -37,6 +37,7 @@ "Ep3ReplaceAssist", "Ep3UnsetFieldCharacter", "InfiniteHPTP", + "FastKills", "InsufficientMinimumLevel", // "OverrideRandomSeed", "OverrideSectionID",