From e13b220be977375376d419d3378d690dcade7c39 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Wed, 3 Jan 2024 23:27:06 -0800 Subject: [PATCH] support non-BB rare enemy generation logic --- TODO.md | 1 + src/Lobby.cc | 2 +- src/Map.cc | 26 ++++++++++++++++++++++---- src/Map.hh | 3 ++- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index 0f9eaa4e..424c522c 100644 --- a/TODO.md +++ b/TODO.md @@ -5,6 +5,7 @@ - Make UI strings localizable (e.g. entries in menus, welcome message, etc.) - Add an idle connection timeout for proxy sessions - Look into JP heart symbol bug on Linux +- Check server's rare enemy logic against GC's logic ## Episode 3 diff --git a/src/Lobby.cc b/src/Lobby.cc index 334f7070..8a2857d3 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -262,7 +262,7 @@ void Lobby::create_item_creator() { } void Lobby::load_maps() { - this->map = make_shared(this->lobby_id, this->random_crypt); + this->map = make_shared(this->base_version, this->lobby_id, this->random_crypt); auto rare_rates = ((this->base_version == Version::BB_V4) && this->rare_enemy_rates) ? this->rare_enemy_rates diff --git a/src/Map.cc b/src/Map.cc index 11e63608..a7347805 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -683,8 +683,9 @@ string Map::Object::str() const { this->item_drop_checked ? "true" : "false"); } -Map::Map(uint32_t lobby_id, std::shared_ptr random_crypt) +Map::Map(Version version, uint32_t lobby_id, std::shared_ptr random_crypt) : log(string_printf("[Lobby:%08" PRIX32 ":map] ", lobby_id), lobby_log.min_level), + version(version), random_crypt(random_crypt) {} void Map::clear() { @@ -722,10 +723,27 @@ bool Map::check_and_log_rare_enemy(bool default_is_rare, uint32_t rare_rate) { if (default_is_rare) { return true; } - if ((this->rare_enemy_indexes.size() < 0x10) && (this->random_crypt->next() < rare_rate)) { - this->rare_enemy_indexes.emplace_back(this->enemies.size()); - return true; + + // On BB, rare enemy indexes are generated by the server and sent to the + // client, so we can use any method we want to choose rares. On other + // versions, we must match the client's logic, even though it's more + // computationally expensive. + if (this->version == Version::BB_V4) { + if ((this->rare_enemy_indexes.size() < 0x10) && (this->random_crypt->next() < rare_rate)) { + this->rare_enemy_indexes.emplace_back(this->enemies.size()); + return true; + } + + } else { + // TODO: We only need the first value from this crypt, so it's unfortunate + // that we have to initialize the entire thing. Find a way to make this + // faster. + PSOV2Encryption crypt(this->random_crypt->seed()); + if ((static_cast((crypt.next() >> 16) & 0xFFFF) / 65536.0f) < 0.002f) { + return true; + } } + return false; } diff --git a/src/Map.hh b/src/Map.hh index e92081e4..28b44782 100644 --- a/src/Map.hh +++ b/src/Map.hh @@ -256,7 +256,7 @@ struct Map { void generate_shuffled_location_table(const Map::RandomEnemyLocationsHeader& header, StringReader r, uint16_t section); }; - Map(uint32_t lobby_id, std::shared_ptr random_crypt); + Map(Version version, uint32_t lobby_id, std::shared_ptr random_crypt); ~Map() = default; void clear(); @@ -315,6 +315,7 @@ struct Map { static std::string disassemble_quest_data(const void* data, size_t size); PrefixedLogger log; + Version version; std::shared_ptr random_crypt; std::vector objects; std::vector enemies;