diff --git a/TODO.md b/TODO.md index a125aa6c..9273a3e2 100644 --- a/TODO.md +++ b/TODO.md @@ -4,7 +4,6 @@ - Implement decrypt/encrypt actions for VMS files - Make UI strings localizable (e.g. entries in menus, welcome message, etc.) - Add an idle connection timeout for proxy sessions -- Check server's rare enemy logic against GC's logic ## Episode 3 diff --git a/src/EnemyType.cc b/src/EnemyType.cc index 9418aacc..9ff16860 100644 --- a/src/EnemyType.cc +++ b/src/EnemyType.cc @@ -1094,3 +1094,14 @@ const vector& enemy_types_for_rare_table_index(Episode episode, uint8 return empty_vec; } } + +bool enemy_type_is_rare(EnemyType type) { + return ((type == EnemyType::HILDEBLUE) || + (type == EnemyType::AL_RAPPY) || + (type == EnemyType::NAR_LILY) || + (type == EnemyType::POUILLY_SLIME) || + (type == EnemyType::MERISSA_AA) || + (type == EnemyType::PAZUZU_ALT) || + (type == EnemyType::DORPHON_ECLAIR) || + (type == EnemyType::KONDRIEU)); +} diff --git a/src/EnemyType.hh b/src/EnemyType.hh index e142d238..5e7d2b5c 100644 --- a/src/EnemyType.hh +++ b/src/EnemyType.hh @@ -145,3 +145,4 @@ bool enemy_type_valid_for_episode(Episode episode, EnemyType enemy_type); uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type); uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type); const std::vector& enemy_types_for_rare_table_index(Episode episode, uint8_t rt_index); +bool enemy_type_is_rare(EnemyType type); diff --git a/src/Map.cc b/src/Map.cc index 8636bce6..5f4ee6e0 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -665,8 +665,14 @@ Map::Enemy::Enemy(uint16_t enemy_id, size_t source_index, uint8_t floor, EnemyTy } string Map::Enemy::str() const { - return string_printf("[Map::Enemy E-%hX source %zX %s floor=%02hhX flags=%02hhX last_hit_by_client_id=%hu]", - this->enemy_id, this->source_index, name_for_enum(this->type), this->floor, this->state_flags, this->last_hit_by_client_id); + return string_printf("[Map::Enemy E-%hX source %zX %s%s floor=%02hhX flags=%02hhX last_hit_by_client_id=%hu]", + this->enemy_id, + this->source_index, + name_for_enum(this->type), + enemy_type_is_rare(this->type) ? " RARE" : "", + this->floor, + this->state_flags, + this->last_hit_by_client_id); } string Map::Object::str() const { @@ -738,8 +744,9 @@ bool Map::check_and_log_rare_enemy(bool default_is_rare, uint32_t rare_rate) { // 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()); + PSOV2Encryption crypt(this->random_crypt->seed() + 0x1000 + this->enemies.size()); if ((static_cast((crypt.next() >> 16) & 0xFFFF) / 65536.0f) < 0.002f) { + this->rare_enemy_indexes.emplace_back(this->enemies.size()); return true; } } @@ -828,15 +835,15 @@ void Map::add_enemy( add(EnemyType::NON_ENEMY_NPC); break; - case 0x0040: // TObjEneMoja - add(this->check_and_log_rare_enemy(e.uparam1 != 0, rare_rates->hildeblue) + case 0x0040: { // TObjEneMoja + bool default_is_rare = (this->version == Version::BB_V4) ? (e.uparam1 & 1) : (e.uparam1 != 0); + add(this->check_and_log_rare_enemy(default_is_rare, rare_rates->hildeblue) ? EnemyType::HILDEBLUE : EnemyType::HILDEBEAR); break; + } case 0x0041: { // TObjEneLappy - // On BB, the set rare condition is uparam1 & 1; on other versions it's - // simply uparam1 != 0. - bool default_is_rare = rare_rates ? (e.uparam1 & 1) : (e.uparam1 != 0); + bool default_is_rare = (this->version == Version::BB_V4) ? (e.uparam1 & 1) : (e.uparam1 != 0); bool is_rare = this->check_and_log_rare_enemy(default_is_rare, rare_rates->rappy); switch (episode) { case Episode::EP1: @@ -893,7 +900,7 @@ void Map::add_enemy( if ((episode == Episode::EP2) && (e.floor == 0x11)) { add(EnemyType::DEL_LILY); } else { - add(this->check_and_log_rare_enemy(e.uparam1 != 0, rare_rates->nar_lily) + add(this->check_and_log_rare_enemy((this->version == Version::BB_V4) && (e.uparam1 & 1), rare_rates->nar_lily) ? EnemyType::NAR_LILY : EnemyType::POISON_LILY); } @@ -907,7 +914,7 @@ void Map::add_enemy( break; } case 0x0064: // TObjEneSlime - add(this->check_and_log_rare_enemy(e.uparam1 != 0, rare_rates->pouilly_slime) + add(this->check_and_log_rare_enemy((this->version == Version::BB_V4) && (e.uparam1 & 1), rare_rates->pouilly_slime) ? EnemyType::POUILLY_SLIME : EnemyType::POFUILLY_SLIME); default_num_children = 4; diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index c2e06f4e..bdf14d88 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -4394,7 +4394,8 @@ static void on_6F(shared_ptr c, uint16_t command, uint32_t, string& data for (size_t z = 0; z < l->variations.size(); z++) { variations_str += string_printf("%" PRIX32, l->variations[z].load()); } - send_text_message_printf(c, "Rare seed: %08" PRIX32 "\nVariations: %s", l->random_seed, variations_str.c_str()); + send_text_message_printf(c, "Rare seed: %08" PRIX32 "\nRare enemies: %zu\nVariations: %s\n", + l->random_seed, l->map->rare_enemy_indexes.size(), variations_str.c_str()); } bool should_resume_game = true;