add $fastkill command

This commit is contained in:
Martin Michelsen
2025-11-29 12:12:34 -08:00
parent 80f8ee1b09
commit 2c333b51d2
18 changed files with 259 additions and 187 deletions
+1
View File
@@ -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 <floor-id>` (or `$warp <floor-id>`): Warp yourself to the given floor.
* `$warpall <floor-id>`: 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.
+19
View File
@@ -2196,6 +2196,25 @@ ChatCommandDefinition cc_quest(
co_return;
});
ChatCommandDefinition cc_fastkill(
{"$fastkill"},
+[](const Args& a) -> asio::awaitable<void> {
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<void> {
+43 -42
View File
@@ -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 {
+22 -21
View File
@@ -13,6 +13,7 @@ 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<EnemyTypeDefinition> type_defs{
// clang-format off
@@ -23,7 +24,7 @@ static const vector<EnemyTypeDefinition> type_defs{
{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::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"},
@@ -38,16 +39,16 @@ static const vector<EnemyTypeDefinition> type_defs{
{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_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, 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::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},
@@ -60,7 +61,7 @@ static const vector<EnemyTypeDefinition> type_defs{
{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::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},
@@ -68,7 +69,7 @@ static const vector<EnemyTypeDefinition> type_defs{
{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::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},
@@ -77,7 +78,7 @@ static const vector<EnemyTypeDefinition> type_defs{
{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::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"},
@@ -87,7 +88,7 @@ static const vector<EnemyTypeDefinition> type_defs{
{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::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},
@@ -104,8 +105,8 @@ static const vector<EnemyTypeDefinition> type_defs{
{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::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},
@@ -118,14 +119,14 @@ static const vector<EnemyTypeDefinition> type_defs{
{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_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, 0x5A, 0x26, "SHAMBERTIN", "Shambertin", nullptr},
{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"},
@@ -134,12 +135,12 @@ static const vector<EnemyTypeDefinition> type_defs{
{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::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},
+4
View File
@@ -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;
};
+1
View File
@@ -88,6 +88,7 @@ HTTPServer::HTTPServer(shared_ptr<ServerState> 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)},
+4 -4
View File
@@ -3069,10 +3069,10 @@ static const vector<DATEntityDefinition> 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:
+8 -7
View File
@@ -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 {
+24
View File
@@ -1998,6 +1998,30 @@ asio::awaitable<HandlerResult> C_6x(shared_ptr<Client> c, Channel::Message& msg)
}
break;
case 0x0A: {
if (c->check_flag(Client::Flag::FAST_KILLS_ENABLED)) {
auto handle_6x0A = [&]<bool BE>() -> void {
auto& cmd = msg.check_size_t<G_UpdateEnemyStateT_6x0A<BE>>();
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()<true>();
} else {
handle_6x0A.template operator()<false>();
}
}
break;
}
case 0x0C:
if (c->check_flag(Client::Flag::INFINITE_HP_ENABLED)) {
co_await send_remove_negative_conditions(c);
+5
View File
@@ -212,6 +212,8 @@ static shared_ptr<const Menu> proxy_options_menu_for_client(shared_ptr<const Cli
"Infinite HP", "Enable automatic HP\nrestoration when\nyou are hit by an\nenemy or trap\n\nCannot revive you\nfrom one-hit kills");
add_flag_option(ProxyOptionsMenuItemID::INFINITE_TP, Client::Flag::INFINITE_TP_ENABLED,
"Infinite TP", "Enable automatic TP\nrestoration when\nyou cast any\ntechnique");
add_flag_option(ProxyOptionsMenuItemID::FAST_KILLS, Client::Flag::FAST_KILLS_ENABLED,
"Fast kills", "Enable one-hit kills\nfor all enemies\nexcept bosses");
} else {
add_flag_option(ProxyOptionsMenuItemID::EP3_INFINITE_MESETA, Client::Flag::PROXY_EP3_INFINITE_MESETA_ENABLED,
"Inf Meseta", "Fix Meseta value\nat 1,000,000");
@@ -2678,6 +2680,9 @@ static asio::awaitable<void> on_10_proxy_options(shared_ptr<Client> 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;
+12 -1
View File
@@ -3466,8 +3466,19 @@ static asio::awaitable<void> on_update_enemy_state(shared_ptr<Client> 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;
+1
View File
@@ -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");
+1
View File
@@ -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;
@@ -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
@@ -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
@@ -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:
+1
View File
@@ -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
+1
View File
@@ -37,6 +37,7 @@
"Ep3ReplaceAssist",
"Ep3UnsetFieldCharacter",
"InfiniteHPTP",
"FastKills",
"InsufficientMinimumLevel",
// "OverrideRandomSeed",
"OverrideSectionID",