fix mericarol type logic; closes #607
This commit is contained in:
@@ -158,6 +158,8 @@ const char* phosg::name_for_enum<EnemyType>(EnemyType type) {
|
||||
return "LOVE_RAPPY";
|
||||
case EnemyType::MERICAROL:
|
||||
return "MERICAROL";
|
||||
case EnemyType::MERICARAND:
|
||||
return "MERICARAND";
|
||||
case EnemyType::MERICUS:
|
||||
return "MERICUS";
|
||||
case EnemyType::MERIKLE:
|
||||
@@ -348,6 +350,7 @@ EnemyType phosg::enum_for_name<EnemyType>(const char* name) {
|
||||
{"KONDRIEU", EnemyType::KONDRIEU},
|
||||
{"LA_DIMENIAN", EnemyType::LA_DIMENIAN},
|
||||
{"LOVE_RAPPY", EnemyType::LOVE_RAPPY},
|
||||
{"MERICARAND", EnemyType::MERICARAND},
|
||||
{"MERICAROL", EnemyType::MERICAROL},
|
||||
{"MERICUS", EnemyType::MERICUS},
|
||||
{"MERIKLE", EnemyType::MERIKLE},
|
||||
@@ -500,6 +503,7 @@ bool enemy_type_valid_for_episode(Episode episode, EnemyType enemy_type) {
|
||||
case EnemyType::LA_DIMENIAN:
|
||||
case EnemyType::LOVE_RAPPY:
|
||||
case EnemyType::MERICAROL:
|
||||
case EnemyType::MERICARAND:
|
||||
case EnemyType::MERICUS:
|
||||
case EnemyType::MERIKLE:
|
||||
case EnemyType::MERILLIA:
|
||||
@@ -744,6 +748,7 @@ uint8_t battle_param_index_for_enemy_type(Episode episode, EnemyType enemy_type)
|
||||
case EnemyType::MIGIUM:
|
||||
return 0x33;
|
||||
case EnemyType::MERICAROL:
|
||||
case EnemyType::MERICARAND:
|
||||
return 0x3A;
|
||||
case EnemyType::UL_GIBBON:
|
||||
return 0x3B;
|
||||
@@ -979,6 +984,7 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
|
||||
case EnemyType::LOVE_RAPPY:
|
||||
return 0x33;
|
||||
case EnemyType::MERICAROL:
|
||||
case EnemyType::MERICARAND:
|
||||
return 0x38;
|
||||
case EnemyType::MERICUS:
|
||||
return 0x3A;
|
||||
@@ -1116,6 +1122,8 @@ bool enemy_type_is_rare(EnemyType type) {
|
||||
(type == EnemyType::AL_RAPPY) ||
|
||||
(type == EnemyType::NAR_LILY) ||
|
||||
(type == EnemyType::POUILLY_SLIME) ||
|
||||
(type == EnemyType::MERICUS) ||
|
||||
(type == EnemyType::MERIKLE) ||
|
||||
(type == EnemyType::MERISSA_AA) ||
|
||||
(type == EnemyType::PAZUZU_ALT) ||
|
||||
(type == EnemyType::DORPHON_ECLAIR) ||
|
||||
|
||||
@@ -80,6 +80,7 @@ enum class EnemyType {
|
||||
KONDRIEU,
|
||||
LA_DIMENIAN,
|
||||
LOVE_RAPPY,
|
||||
MERICARAND,
|
||||
MERICAROL,
|
||||
MERICUS,
|
||||
MERIKLE,
|
||||
|
||||
+63
-28
@@ -2066,12 +2066,9 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
break;
|
||||
}
|
||||
case 0x0064: { // TObjEneSlime
|
||||
// TODO: It appears BB doesn't have a way to force slimes to be rare via
|
||||
// constructor args. Is this true?
|
||||
// Unlike all other versions, BB doesn't have a way to force slimes to be
|
||||
// rare via constructor args
|
||||
bool is_rare_v123 = (set_entry->uparam2 & 1);
|
||||
if ((set_entry->num_children != 0) && (set_entry->num_children != 4)) {
|
||||
this->log.warning("POFUILLY_SLIME has an unusual num_children (0x%hX)", set_entry->num_children.load());
|
||||
}
|
||||
default_num_children = -1; // Skip adding children later (because we do it here)
|
||||
size_t num_children = set_entry->num_children ? set_entry->num_children.load() : 4;
|
||||
for (size_t z = 0; z < num_children + 1; z++) {
|
||||
@@ -2224,15 +2221,24 @@ shared_ptr<SuperMap::Enemy> SuperMap::add_enemy_and_children(
|
||||
default_num_children = 4;
|
||||
break;
|
||||
case 0x00D5: // TObjEneMerillLia
|
||||
add((set_entry->uparam1 & 0x01) ? EnemyType::MERILTAS : EnemyType::MERILLIA);
|
||||
add((set_entry->uparam1 & 1) ? EnemyType::MERILTAS : EnemyType::MERILLIA);
|
||||
break;
|
||||
case 0x00D6: // TObjEneBm9Mericarol
|
||||
if (set_entry->uparam1 == 0) {
|
||||
add(EnemyType::MERICAROL);
|
||||
} else {
|
||||
add(((set_entry->uparam1 % 3) == 2) ? EnemyType::MERICUS : EnemyType::MERIKLE);
|
||||
case 0x00D6: { // TObjEneBm9Mericarol
|
||||
switch (set_entry->uparam1) {
|
||||
case 0:
|
||||
add(EnemyType::MERICAROL);
|
||||
break;
|
||||
case 1:
|
||||
add(EnemyType::MERIKLE);
|
||||
break;
|
||||
case 2:
|
||||
add(EnemyType::MERICUS);
|
||||
break;
|
||||
default:
|
||||
add(EnemyType::MERICARAND);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x00D7: // TObjEneBm5GibonU
|
||||
add((set_entry->uparam1 & 0x01) ? EnemyType::ZOL_GIBBON : EnemyType::UL_GIBBON);
|
||||
break;
|
||||
@@ -3218,29 +3224,31 @@ void SuperMap::print(FILE* stream) const {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Map state
|
||||
|
||||
MapState::RareEnemyRates::RareEnemyRates(uint32_t enemy_rate, uint32_t boss_rate)
|
||||
MapState::RareEnemyRates::RareEnemyRates(uint32_t enemy_rate, uint32_t mericarand_rate, uint32_t boss_rate)
|
||||
: hildeblue(enemy_rate),
|
||||
rappy(enemy_rate),
|
||||
nar_lily(enemy_rate),
|
||||
pouilly_slime(enemy_rate),
|
||||
mericarand(mericarand_rate),
|
||||
merissa_aa(enemy_rate),
|
||||
pazuzu(enemy_rate),
|
||||
dorphon_eclair(enemy_rate),
|
||||
kondrieu(boss_rate) {}
|
||||
|
||||
MapState::RareEnemyRates::RareEnemyRates(const phosg::JSON& json)
|
||||
: hildeblue(json.get_int("Hildeblue")),
|
||||
rappy(json.get_int("Rappy")),
|
||||
nar_lily(json.get_int("NarLily")),
|
||||
pouilly_slime(json.get_int("PouillySlime")),
|
||||
merissa_aa(json.get_int("MerissaAA")),
|
||||
pazuzu(json.get_int("Pazuzu")),
|
||||
dorphon_eclair(json.get_int("DorphonEclair")),
|
||||
kondrieu(json.get_int("Kondrieu")) {}
|
||||
: hildeblue(json.get_int("Hildeblue", DEFAULT_RARE_ENEMY_RATE_V3)),
|
||||
rappy(json.get_int("Rappy", DEFAULT_RARE_ENEMY_RATE_V3)),
|
||||
nar_lily(json.get_int("NarLily", DEFAULT_RARE_ENEMY_RATE_V3)),
|
||||
pouilly_slime(json.get_int("PouillySlime", DEFAULT_RARE_ENEMY_RATE_V3)),
|
||||
mericarand(json.get_int("Mericarand", DEFAULT_MERICARAND_RATE_V3)),
|
||||
merissa_aa(json.get_int("MerissaAA", DEFAULT_RARE_ENEMY_RATE_V3)),
|
||||
pazuzu(json.get_int("Pazuzu", DEFAULT_RARE_ENEMY_RATE_V3)),
|
||||
dorphon_eclair(json.get_int("DorphonEclair", DEFAULT_RARE_ENEMY_RATE_V3)),
|
||||
kondrieu(json.get_int("Kondrieu", DEFAULT_RARE_BOSS_RATE_V4)) {}
|
||||
|
||||
string MapState::RareEnemyRates::str() const {
|
||||
return phosg::string_printf("RareEnemyRates(hildeblue=%08" PRIX32 ", rappy=%08" PRIX32 ", nar_lily=%08" PRIX32 ", pouilly_slime=%08" PRIX32 ", merissa_aa=%08" PRIX32 ", pazuzu=%08" PRIX32 ", dorphon_eclair=%08" PRIX32 ", kondrieu=%08" PRIX32 ")",
|
||||
this->hildeblue, this->rappy, this->nar_lily, this->pouilly_slime,
|
||||
return phosg::string_printf("RareEnemyRates(hildeblue=%08" PRIX32 ", rappy=%08" PRIX32 ", nar_lily=%08" PRIX32 ", pouilly_slime=%08" PRIX32 ", mericarand=%08" PRIX32 ", merissa_aa=%08" PRIX32 ", pazuzu=%08" PRIX32 ", dorphon_eclair=%08" PRIX32 ", kondrieu=%08" PRIX32 ")",
|
||||
this->hildeblue, this->rappy, this->nar_lily, this->pouilly_slime, this->mericarand,
|
||||
this->merissa_aa, this->pazuzu, this->dorphon_eclair, this->kondrieu);
|
||||
}
|
||||
|
||||
@@ -3250,6 +3258,7 @@ phosg::JSON MapState::RareEnemyRates::json() const {
|
||||
{"Rappy", this->rappy},
|
||||
{"NarLily", this->nar_lily},
|
||||
{"PouillySlime", this->pouilly_slime},
|
||||
{"Mericarand", this->mericarand},
|
||||
{"MerissaAA", this->merissa_aa},
|
||||
{"Pazuzu", this->pazuzu},
|
||||
{"DorphonEclair", this->dorphon_eclair},
|
||||
@@ -3269,6 +3278,8 @@ uint32_t MapState::RareEnemyRates::for_enemy_type(EnemyType type) const {
|
||||
return this->nar_lily;
|
||||
case EnemyType::POFUILLY_SLIME:
|
||||
return this->pouilly_slime;
|
||||
case EnemyType::MERICARAND:
|
||||
return this->mericarand;
|
||||
case EnemyType::MERISSA_A:
|
||||
return this->merissa_aa;
|
||||
case EnemyType::ZU:
|
||||
@@ -3284,8 +3295,12 @@ uint32_t MapState::RareEnemyRates::for_enemy_type(EnemyType type) const {
|
||||
}
|
||||
}
|
||||
|
||||
const shared_ptr<const MapState::RareEnemyRates> MapState::NO_RARE_ENEMIES = make_shared<MapState::RareEnemyRates>(0, 0);
|
||||
const shared_ptr<const MapState::RareEnemyRates> MapState::DEFAULT_RARE_ENEMIES = make_shared<MapState::RareEnemyRates>(0x0083126E, 0x1999999A);
|
||||
const shared_ptr<const MapState::RareEnemyRates> MapState::NO_RARE_ENEMIES = make_shared<MapState::RareEnemyRates>(
|
||||
0, 0, 0);
|
||||
const shared_ptr<const MapState::RareEnemyRates> MapState::DEFAULT_RARE_ENEMIES = make_shared<MapState::RareEnemyRates>(
|
||||
MapState::RareEnemyRates::DEFAULT_RARE_ENEMY_RATE_V3,
|
||||
MapState::RareEnemyRates::DEFAULT_MERICARAND_RATE_V3,
|
||||
MapState::RareEnemyRates::DEFAULT_RARE_BOSS_RATE_V4);
|
||||
|
||||
uint32_t MapState::EnemyState::convert_game_flags(uint32_t game_flags, bool to_v3) {
|
||||
// The format of game_flags was changed significantly between v2 and v3, and
|
||||
@@ -3522,7 +3537,7 @@ void MapState::index_super_map(const FloorConfig& fc, shared_ptr<PSOLFGEncryptio
|
||||
}
|
||||
|
||||
auto rare_type = rare_type_for_enemy_type(type, fc.super_map->get_episode(), this->event, ene->floor);
|
||||
if (rare_type != type) {
|
||||
if ((type == EnemyType::MERICARAND) || (rare_type != type)) {
|
||||
unordered_map<uint32_t, float> det_cache;
|
||||
uint32_t bb_rare_rate = this->bb_rare_rates->for_enemy_type(type);
|
||||
for (Version v : ALL_ARPG_SEMANTIC_VERSIONS) {
|
||||
@@ -3556,15 +3571,35 @@ void MapState::index_super_map(const FloorConfig& fc, shared_ptr<PSOLFGEncryptio
|
||||
det_cache.emplace(seed, det);
|
||||
}
|
||||
|
||||
// On v1 and v2 (and GC NTE), the rare rate is 0.1% instead of 0.2%.
|
||||
if (det < (is_v1_or_v2(v) ? 0.001f : 0.002f)) {
|
||||
ene_st->set_rare(v);
|
||||
if (type == EnemyType::MERICARAND) {
|
||||
// On v3, Mericarols that have uparam1 > 2 are randomized to be
|
||||
// Mericus, Merikle, or Mericarol, but the former two are not
|
||||
// considered rare. (We use rare_flags anyway to distinguish them
|
||||
// from Mericarol.)
|
||||
if (det > 0.9) { // Merikle
|
||||
ene_st->set_rare(v);
|
||||
ene_st->set_mericarand_variant_flag(v);
|
||||
} else if (det > 0.8) { // Mericus
|
||||
ene_st->set_rare(v);
|
||||
} else {
|
||||
// Mericarol (no flags to set)
|
||||
}
|
||||
|
||||
} else {
|
||||
// On v1 and v2 (and GC NTE), the rare rate is 0.1% instead of 0.2%.
|
||||
if (det < (is_v1_or_v2(v) ? 0.001f : 0.002f)) {
|
||||
ene_st->set_rare(v);
|
||||
}
|
||||
}
|
||||
|
||||
} else if ((bb_rare_rate > 0) &&
|
||||
(this->bb_rare_enemy_indexes.size() < 0x10) &&
|
||||
(random_from_optional_crypt(opt_rand_crypt) < bb_rare_rate)) {
|
||||
this->bb_rare_enemy_indexes.emplace_back(enemy_index);
|
||||
ene_st->set_rare(v);
|
||||
if ((type == EnemyType::MERICARAND) && (enemy_index & 1)) {
|
||||
ene_st->set_mericarand_variant_flag(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-6
@@ -628,16 +628,22 @@ protected:
|
||||
class MapState {
|
||||
public:
|
||||
struct RareEnemyRates {
|
||||
static constexpr uint32_t DEFAULT_RARE_ENEMY_RATE_V1_V2 = 0x00418937; // 1/1000
|
||||
static constexpr uint32_t DEFAULT_RARE_ENEMY_RATE_V3 = 0x0083126E; // 1/500
|
||||
static constexpr uint32_t DEFAULT_MERICARAND_RATE_V3 = 0x33333333; // 1/5
|
||||
static constexpr uint32_t DEFAULT_RARE_BOSS_RATE_V4 = 0x1999999A; // 1/10
|
||||
|
||||
uint32_t hildeblue; // HILDEBEAR -> HILDEBLUE
|
||||
uint32_t rappy; // RAG_RAPPY -> {AL_RAPPY or seasonal rappies}; SAND_RAPPY -> DEL_RAPPY
|
||||
uint32_t nar_lily; // POISON_LILY -> NAR_LILY
|
||||
uint32_t pouilly_slime; // POFUILLY_SLIME -> POUILLY_SLIME
|
||||
uint32_t mericarand; // MERICARAND -> MERIKLE or MERICUS (only for those with subtype > 2)
|
||||
uint32_t merissa_aa; // MERISSA_A -> MERISSA_AA
|
||||
uint32_t pazuzu; // ZU -> PAZUZU (and _ALT variants)
|
||||
uint32_t dorphon_eclair; // DORPHON -> DORPHON_ECLAIR
|
||||
uint32_t kondrieu; // {SAINT_MILLION, SHAMBERTIN} -> KONDRIEU
|
||||
|
||||
RareEnemyRates(uint32_t enemy_rate, uint32_t boss_rate);
|
||||
RareEnemyRates(uint32_t enemy_rate, uint32_t mericarand_rate, uint32_t boss_rate);
|
||||
explicit RareEnemyRates(const phosg::JSON& json);
|
||||
|
||||
uint32_t for_enemy_type(EnemyType type) const;
|
||||
@@ -687,9 +693,10 @@ public:
|
||||
};
|
||||
size_t e_id = 0;
|
||||
size_t set_id = 0;
|
||||
uint32_t game_flags = 0; // From 6x0A
|
||||
uint16_t total_damage = 0;
|
||||
uint16_t rare_flags = 0;
|
||||
uint32_t game_flags = 0; // From 6x0A
|
||||
uint16_t mericarand_variant_flags = 0;
|
||||
uint16_t set_flags = 0; // Only used if super_ene->child_index == 0
|
||||
uint16_t server_flags = 0;
|
||||
|
||||
@@ -721,16 +728,29 @@ public:
|
||||
}
|
||||
|
||||
inline bool is_rare(Version version) const {
|
||||
return (((rare_flags >> static_cast<size_t>(version)) & 1) ||
|
||||
return (((this->rare_flags >> static_cast<size_t>(version)) & 1) ||
|
||||
((version == Version::BB_V4) ? this->super_ene->is_default_rare_bb : this->super_ene->is_default_rare_v123));
|
||||
}
|
||||
inline void set_rare(Version version) {
|
||||
this->rare_flags |= (1 << static_cast<size_t>(version));
|
||||
}
|
||||
inline void set_mericarand_variant_flag(Version version) {
|
||||
this->mericarand_variant_flags |= (1 << static_cast<size_t>(version));
|
||||
}
|
||||
inline EnemyType type(Version version, Episode episode, uint8_t event) const {
|
||||
return this->is_rare(version)
|
||||
? rare_type_for_enemy_type(this->super_ene->type, episode, event, this->super_ene->floor)
|
||||
: this->super_ene->type;
|
||||
if (this->super_ene->type == EnemyType::MERICARAND) {
|
||||
if (this->is_rare(version)) {
|
||||
return ((this->mericarand_variant_flags >> static_cast<size_t>(version)) & 1)
|
||||
? EnemyType::MERIKLE
|
||||
: EnemyType::MERICUS;
|
||||
} else {
|
||||
return EnemyType::MERICAROL;
|
||||
}
|
||||
} else {
|
||||
return this->is_rare(version)
|
||||
? rare_type_for_enemy_type(this->super_ene->type, episode, event, this->super_ene->floor)
|
||||
: this->super_ene->type;
|
||||
}
|
||||
}
|
||||
inline bool ever_hit_by_client_id(uint8_t client_id) const {
|
||||
return this->server_flags & (Flag::ALL_HITS_MASK_FIRST << client_id);
|
||||
|
||||
@@ -1205,18 +1205,24 @@
|
||||
// all difficulties use the same rates.) If the Challenge set is not
|
||||
// specified, the default rates are used for Challenge mode, which is 1/500
|
||||
// for all enemies.
|
||||
// The Mericarand rate applies to Mericarol enemies whose arguments specify
|
||||
// that they may be replaced with Mericus or Merikle. (Specifically, if
|
||||
// uparam1 > 2, this is the case.) When a Mericarol is replaced, the enemy
|
||||
// index is used to determine whether it will be a Mericus (even enemy index)
|
||||
// or Merikle (odd enemy index).
|
||||
"RareEnemyRates-Normal": {
|
||||
// These are probabilities out of 0xFFFFFFFF - so 0 means that rare enemy
|
||||
// will never appear, and 0xFFFFFFFF means it will always appear (until 16
|
||||
// rare enemies have been assigned).
|
||||
"Hildeblue": 0x0083126E,
|
||||
"Rappy": 0x0083126E,
|
||||
"NarLily": 0x0083126E,
|
||||
"PouillySlime": 0x0083126E,
|
||||
"MerissaAA": 0x0083126E,
|
||||
"Pazuzu": 0x0083126E,
|
||||
"DorphonEclair": 0x0083126E,
|
||||
"Kondrieu": 0x1999999A,
|
||||
"Hildeblue": 0x0083126E, // 1/500
|
||||
"Rappy": 0x0083126E, // 1/500
|
||||
"NarLily": 0x0083126E, // 1/500
|
||||
"PouillySlime": 0x0083126E, // 1/500
|
||||
"Mericarand": 0x33333333, // 1/5
|
||||
"MerissaAA": 0x0083126E, // 1/500
|
||||
"Pazuzu": 0x0083126E, // 1/500
|
||||
"DorphonEclair": 0x0083126E, // 1/500
|
||||
"Kondrieu": 0x1999999A, // 1/10
|
||||
},
|
||||
// "RareEnemyRates-Hard": {...},
|
||||
// "RareEnemyRates-VeryHard": {...},
|
||||
|
||||
Reference in New Issue
Block a user