diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 4dafdff7..91cc9ae4 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -3129,6 +3129,35 @@ static void on_charge_attack_bb(shared_ptr c, uint8_t command, uint8_t f } } +static void send_max_level_notification_if_needed(shared_ptr c) { + auto s = c->require_server_state(); + if (!s->notify_server_for_max_level_achieved) { + return; + } + + uint32_t max_level; + if (is_v1(c->version())) { + max_level = 99; + } else if (!is_ep3(c->version())) { + max_level = 199; + } else { + max_level = 998; + } + + auto p = c->character(); + if (p->disp.stats.level == max_level) { + string name = p->disp.name.decode(c->language()); + size_t level_for_str = max_level + 1; + string message = string_printf("$CG%s$C6\nGC: %" PRIu32 "\nhas reached Level $CG%zu", + name.c_str(), c->license->serial_number, level_for_str); + for (auto& it : s->channel_to_client) { + if ((it.second != c) && it.second->license && !is_patch(it.second->version()) && it.second->lobby.lock()) { + send_text_message(it.second, message); + } + } + } +} + static void on_level_up(shared_ptr c, uint8_t command, uint8_t flag, void* data, size_t size) { auto l = c->require_lobby(); if (!l->is_game()) { @@ -3161,6 +3190,7 @@ static void on_level_up(shared_ptr c, uint8_t command, uint8_t flag, voi p->disp.stats.level = cmd.level.load(); } + send_max_level_notification_if_needed(c); forward_subcommand(c, command, flag, data, size); } @@ -3183,7 +3213,9 @@ static void add_player_exp(shared_ptr c, uint32_t exp) { break; } } while (p->disp.stats.level < 199); + if (leveled_up) { + send_max_level_notification_if_needed(c); send_level_up(c); } } diff --git a/src/ServerState.cc b/src/ServerState.cc index 65706332..ff2e6852 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -690,6 +690,7 @@ void ServerState::load_config_early() { this->allow_unregistered_users = this->config_json->get_bool("AllowUnregisteredUsers", false); this->allow_pc_nte = this->config_json->get_bool("AllowPCNTE", false); this->use_temp_licenses_for_prototypes = this->config_json->get_bool("UseTemporaryLicensesForPrototypes", true); + this->notify_server_for_max_level_achieved = this->config_json->get_bool("NotifyServerForMaxLevelAchieved", false); this->allowed_drop_modes_v1_v2_normal = this->config_json->get_int("AllowedDropModesV1V2Normal", 0x1F); this->allowed_drop_modes_v1_v2_battle = this->config_json->get_int("AllowedDropModesV1V2Battle", 0x07); this->allowed_drop_modes_v1_v2_challenge = this->config_json->get_int("AllowedDropModesV1V2Challenge", 0x07); diff --git a/src/ServerState.hh b/src/ServerState.hh index 0a154c97..13d475f1 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -131,6 +131,7 @@ struct ServerState : public std::enable_shared_from_this { bool default_rare_notifs_enabled_v3_v4 = false; std::unordered_set notify_game_for_item_primary_identifiers; std::unordered_set notify_server_for_item_primary_identifiers; + bool notify_server_for_max_level_achieved = false; std::vector> bb_private_keys; std::shared_ptr function_code_index; std::shared_ptr pc_patch_file_index; diff --git a/system/config.example.json b/system/config.example.json index b1ccd09d..e167b690 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -995,6 +995,10 @@ "NotifyGameForItemPrimaryIdentifiers": [], "NotifyServerForItemPrimaryIdentifiers": [], + // Whether to notify the entire server when a player reaches the maximum level + // (100 on v1, 200 on other versions, or 999 on Episode 3). + "NotifyServerForMaxLevelAchieved": false, + // Whether to enable patches on Episode 3 USA. This functionality depends on // exploiting a bug in Episode 3, and while it seems to work reliably on // Dolphin, it hasn't been tested on a real GameCube. So, newserv doesn't diff --git a/tests/config.json b/tests/config.json index fd2e5b38..e1956e35 100644 --- a/tests/config.json +++ b/tests/config.json @@ -33,6 +33,7 @@ "RareNotificationsEnabledByDefault": false, "NotifyGameForItemPrimaryIdentifiers": [], "NotifyServerForItemPrimaryIdentifiers": [], + "NotifyServerForMaxLevelAchieved": false, "LocalAddress": "en0", "ExternalAddress": "en0",