reorganize game tables

This commit is contained in:
Martin Michelsen
2026-05-10 09:54:51 -07:00
parent 554bef0de4
commit fc70919c94
62 changed files with 115955 additions and 228 deletions
+223 -11
View File
@@ -9,6 +9,191 @@
using namespace std;
BattleParamsIndex::AttackData BattleParamsIndex::AttackData::from_json(const phosg::JSON& json) {
return AttackData{
json.get_int("MinATP"),
json.get_int("MaxATP"),
json.get_int("MinATA"),
json.get_int("MaxATA"),
json.get_float("DistanceX"),
json.get_int("Angle"),
json.get_float("DistanceY"),
json.get_int("UnknownA8"),
json.get_int("UnknownA9"),
json.get_int("UnknownA10"),
json.get_int("UnknownA11"),
json.get_int("UnknownA12"),
json.get_int("UnknownA13"),
json.get_int("UnknownA14"),
json.get_int("UnknownA15"),
json.get_int("UnknownA16"),
};
}
phosg::JSON BattleParamsIndex::AttackData::json() const {
return phosg::JSON::dict({
{"MinATP", this->min_atp.load()},
{"MaxATP", this->max_atp.load()},
{"MinATA", this->min_ata.load()},
{"MaxATA", this->max_ata.load()},
{"DistanceX", this->distance_x.load()},
{"Angle", this->angle.load()},
{"DistanceY", this->distance_y.load()},
{"UnknownA8", this->unknown_a8.load()},
{"UnknownA9", this->unknown_a9.load()},
{"UnknownA10", this->unknown_a10.load()},
{"UnknownA11", this->unknown_a11.load()},
{"UnknownA12", this->unknown_a12.load()},
{"UnknownA13", this->unknown_a13.load()},
{"UnknownA14", this->unknown_a14.load()},
{"UnknownA15", this->unknown_a15.load()},
{"UnknownA16", this->unknown_a16.load()},
});
}
BattleParamsIndex::ResistData BattleParamsIndex::ResistData::from_json(const phosg::JSON& json) {
return BattleParamsIndex::ResistData{
json.get_int("EVPBonus"),
json.get_int("EFR"),
json.get_int("EIC"),
json.get_int("ETH"),
json.get_int("ELT"),
json.get_int("EDK"),
json.get_int("UnknownA6"),
json.get_int("UnknownA7"),
json.get_int("UnknownA8"),
json.get_int("UnknownA9"),
json.get_int("DFPBonus"),
};
}
phosg::JSON BattleParamsIndex::ResistData::json() const {
return phosg::JSON::dict({
{"EVPBonus", this->evp_bonus.load()},
{"EFR", this->efr.load()},
{"EIC", this->eic.load()},
{"ETH", this->eth.load()},
{"ELT", this->elt.load()},
{"EDK", this->edk.load()},
{"UnknownA6", this->unknown_a6.load()},
{"UnknownA7", this->unknown_a7.load()},
{"UnknownA8", this->unknown_a8.load()},
{"UnknownA9", this->unknown_a9.load()},
{"DFPBonus", this->dfp_bonus.load()},
});
}
BattleParamsIndex::MovementData BattleParamsIndex::MovementData::from_json(const phosg::JSON& json) {
const auto& fparams_json = json.at("FParams").as_list();
const auto& iparams_json = json.at("IParams").as_list();
return BattleParamsIndex::MovementData{
fparams_json.at(0)->as_float(),
fparams_json.at(1)->as_float(),
fparams_json.at(2)->as_float(),
fparams_json.at(3)->as_float(),
fparams_json.at(4)->as_float(),
fparams_json.at(5)->as_float(),
iparams_json.at(0)->as_float(),
iparams_json.at(1)->as_float(),
iparams_json.at(2)->as_float(),
iparams_json.at(3)->as_float(),
iparams_json.at(4)->as_float(),
iparams_json.at(5)->as_float(),
};
}
phosg::JSON BattleParamsIndex::MovementData::json() const {
auto fparams_list = phosg::JSON::list({
this->fparam1.load(),
this->fparam2.load(),
this->fparam3.load(),
this->fparam4.load(),
this->fparam5.load(),
this->fparam6.load(),
});
auto iparams_list = phosg::JSON::list({
this->iparam1.load(),
this->iparam2.load(),
this->iparam3.load(),
this->iparam4.load(),
this->iparam5.load(),
this->iparam6.load(),
});
return phosg::JSON::dict({{"FParams", std::move(fparams_list)}, {"IParams", std::move(iparams_list)}});
}
BattleParamsIndex::Table BattleParamsIndex::Table::from_json(const phosg::JSON& json) {
BattleParamsIndex::Table ret;
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
const auto& diff_json = json.at(name_for_difficulty(difficulty));
for (size_t z = 0; z < 0x60; z++) {
const auto& entry_json = diff_json.at(z);
ret.stats[static_cast<size_t>(difficulty)][z] = PlayerStats::from_json(entry_json.at("Stats"));
ret.attack_data[static_cast<size_t>(difficulty)][z] = AttackData::from_json(entry_json.at("AttackData"));
ret.resist_data[static_cast<size_t>(difficulty)][z] = ResistData::from_json(entry_json.at("ResistData"));
ret.movement_data[static_cast<size_t>(difficulty)][z] = MovementData::from_json(entry_json.at("MovementData"));
}
}
return ret;
}
phosg::JSON BattleParamsIndex::Table::json() const {
auto ret = phosg::JSON::dict();
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
auto diff_ret = phosg::JSON::list();
for (size_t z = 0; z < 0x60; z++) {
auto stats_json = this->stats_for_index(difficulty, z).json();
auto attack_data_json = this->attack_data_for_index(difficulty, z).json();
auto resist_data_json = this->resist_data_for_index(difficulty, z).json();
auto movement_data_json = this->movement_data_for_index(difficulty, z).json();
std::set<EnemyType> stats_names;
std::set<EnemyType> attack_data_names;
std::set<EnemyType> resist_data_names;
std::set<EnemyType> movement_data_names;
for (Episode episode : ALL_EPISODES_V4) {
for (const auto& enemy_type : enemy_types_for_battle_param_stats_index(episode, z)) {
stats_names.emplace(enemy_type);
}
for (const auto& enemy_type : enemy_types_for_battle_param_attack_data_index(episode, z)) {
attack_data_names.emplace(enemy_type);
}
for (const auto& enemy_type : enemy_types_for_battle_param_resist_data_index(episode, z)) {
resist_data_names.emplace(enemy_type);
}
for (const auto& enemy_type : enemy_types_for_battle_param_movement_data_index(episode, z)) {
movement_data_names.emplace(enemy_type);
}
}
auto stats_names_json = phosg::JSON::list();
for (EnemyType enemy_type : stats_names) {
stats_names_json.emplace_back(phosg::name_for_enum(enemy_type));
}
auto attack_data_names_json = phosg::JSON::list();
for (EnemyType enemy_type : attack_data_names) {
attack_data_names_json.emplace_back(phosg::name_for_enum(enemy_type));
}
auto resist_data_names_json = phosg::JSON::list();
for (EnemyType enemy_type : resist_data_names) {
resist_data_names_json.emplace_back(phosg::name_for_enum(enemy_type));
}
auto movement_data_names_json = phosg::JSON::list();
for (EnemyType enemy_type : movement_data_names) {
movement_data_names_json.emplace_back(phosg::name_for_enum(enemy_type));
}
stats_json.emplace("Enemies", std::move(stats_names_json));
attack_data_json.emplace("Enemies", std::move(attack_data_names_json));
resist_data_json.emplace("Enemies", std::move(resist_data_names_json));
movement_data_json.emplace("Enemies", std::move(movement_data_names_json));
diff_ret.emplace_back(phosg::JSON::dict({
{"BPIndex", z},
{"Stats", std::move(stats_json)},
{"AttackData", std::move(attack_data_json)},
{"ResistData", std::move(resist_data_json)},
{"MovementData", std::move(movement_data_json)},
}));
}
ret.emplace(name_for_difficulty(difficulty), std::move(diff_ret));
}
return ret;
}
void BattleParamsIndex::Table::print(FILE* stream, Episode episode) const {
phosg::fwrite_fmt(stream, "========== STATS\n");
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
@@ -76,7 +261,40 @@ void BattleParamsIndex::Table::print(FILE* stream, Episode episode) const {
}
}
BattleParamsIndex::BattleParamsIndex(
phosg::JSON BattleParamsIndex::json() const {
return phosg::JSON::dict({
{"Episode1-Online", this->get_table(false, Episode::EP1).json()},
{"Episode2-Online", this->get_table(false, Episode::EP2).json()},
{"Episode4-Online", this->get_table(false, Episode::EP4).json()},
{"Episode1-Solo", this->get_table(true, Episode::EP1).json()},
{"Episode2-Solo", this->get_table(true, Episode::EP2).json()},
{"Episode4-Solo", this->get_table(true, Episode::EP4).json()},
});
}
JSONBattleParamsIndex::JSONBattleParamsIndex(const phosg::JSON& json) {
this->tables[0][0] = Table::from_json(json.at("Episode1-Online"));
this->tables[0][1] = Table::from_json(json.at("Episode2-Online"));
this->tables[0][2] = Table::from_json(json.at("Episode4-Online"));
this->tables[1][0] = Table::from_json(json.at("Episode1-Solo"));
this->tables[1][1] = Table::from_json(json.at("Episode2-Solo"));
this->tables[1][2] = Table::from_json(json.at("Episode4-Solo"));
}
const BattleParamsIndex::Table& JSONBattleParamsIndex::get_table(bool solo, Episode episode) const {
switch (episode) {
case Episode::EP1:
return this->tables[!!solo][0];
case Episode::EP2:
return this->tables[!!solo][1];
case Episode::EP4:
return this->tables[!!solo][2];
default:
throw invalid_argument("invalid episode");
}
}
BinaryBattleParamsIndex::BinaryBattleParamsIndex(
shared_ptr<const string> data_on_ep1,
shared_ptr<const string> data_on_ep2,
shared_ptr<const string> data_on_ep4,
@@ -103,21 +321,15 @@ BattleParamsIndex::BattleParamsIndex(
}
}
const BattleParamsIndex::Table& BattleParamsIndex::get_table(bool solo, Episode episode) const {
uint8_t ep_index;
const BattleParamsIndex::Table& BinaryBattleParamsIndex::get_table(bool solo, Episode episode) const {
switch (episode) {
case Episode::EP1:
ep_index = 0;
break;
return *this->files[!!solo][0].table;
case Episode::EP2:
ep_index = 1;
break;
return *this->files[!!solo][1].table;
case Episode::EP4:
ep_index = 2;
break;
return *this->files[!!solo][2].table;
default:
throw invalid_argument("invalid episode");
}
return *this->files[!!solo][ep_index].table;
}
+38 -10
View File
@@ -35,7 +35,8 @@ public:
/* 24 */ le_uint32_t unknown_a14;
/* 28 */ le_uint32_t unknown_a15;
/* 2C */ le_uint32_t unknown_a16;
/* 30 */
static AttackData from_json(const phosg::JSON& json);
phosg::JSON json() const;
} __packed_ws__(AttackData, 0x30);
struct ResistData {
@@ -50,7 +51,8 @@ public:
/* 14 */ le_uint32_t unknown_a8;
/* 18 */ le_uint32_t unknown_a9;
/* 1C */ le_int32_t dfp_bonus;
/* 20 */
static ResistData from_json(const phosg::JSON& json);
phosg::JSON json() const;
} __packed_ws__(ResistData, 0x20);
struct MovementData {
@@ -66,7 +68,8 @@ public:
/* 24 */ le_uint32_t iparam4;
/* 28 */ le_uint32_t iparam5;
/* 2C */ le_uint32_t iparam6;
/* 30 */
static MovementData from_json(const phosg::JSON& json);
phosg::JSON json() const;
} __packed_ws__(MovementData, 0x30);
struct Table {
@@ -76,23 +79,48 @@ public:
/* AE00 */ parray<parray<MovementData, 0x60>, 4> movement_data; // [difficulty][bp_index]
/* F600 */
const PlayerStats& stats_for_index(Difficulty difficulty, uint8_t index) const {
inline const PlayerStats& stats_for_index(Difficulty difficulty, uint8_t index) const {
return this->stats.at(static_cast<size_t>(difficulty)).at(index);
}
const AttackData& attack_data_for_index(Difficulty difficulty, uint8_t index) const {
inline const AttackData& attack_data_for_index(Difficulty difficulty, uint8_t index) const {
return this->attack_data.at(static_cast<size_t>(difficulty)).at(index);
}
const ResistData& resist_data_for_index(Difficulty difficulty, uint8_t index) const {
inline const ResistData& resist_data_for_index(Difficulty difficulty, uint8_t index) const {
return this->resist_data.at(static_cast<size_t>(difficulty)).at(index);
}
const MovementData& movement_data_for_index(Difficulty difficulty, uint8_t index) const {
inline const MovementData& movement_data_for_index(Difficulty difficulty, uint8_t index) const {
return this->movement_data.at(static_cast<size_t>(difficulty)).at(index);
}
static Table from_json(const phosg::JSON& json);
phosg::JSON json() const;
void print(FILE* stream, Episode episode) const;
} __packed_ws__(Table, 0xF600);
BattleParamsIndex(
virtual ~BattleParamsIndex() = default;
virtual const Table& get_table(bool solo, Episode episode) const = 0;
phosg::JSON json() const;
protected:
BattleParamsIndex() = default;
};
class JSONBattleParamsIndex : public BattleParamsIndex {
public:
explicit JSONBattleParamsIndex(const phosg::JSON& json);
virtual const Table& get_table(bool solo, Episode episode) const;
protected:
// Indexed as [online/offline][episode]
std::array<std::array<Table, 3>, 2> tables;
};
class BinaryBattleParamsIndex : public BattleParamsIndex {
public:
BinaryBattleParamsIndex(
std::shared_ptr<const std::string> data_on_ep1, // BattleParamEntry_on.dat
std::shared_ptr<const std::string> data_on_ep2, // BattleParamEntry_lab_on.dat
std::shared_ptr<const std::string> data_on_ep4, // BattleParamEntry_ep4_on.dat
@@ -100,9 +128,9 @@ public:
std::shared_ptr<const std::string> data_off_ep2, // BattleParamEntry_lab.dat
std::shared_ptr<const std::string> data_off_ep4); // BattleParamEntry_ep4.dat
const Table& get_table(bool solo, Episode episode) const;
virtual const Table& get_table(bool solo, Episode episode) const;
private:
protected:
struct File {
std::shared_ptr<const std::string> data;
const Table* table = nullptr;
+207 -147
View File
@@ -2,6 +2,7 @@
#include <phosg/Filesystem.hh>
#include <phosg/Strings.hh>
#include <set>
#include "Loggers.hh"
#include "PSOEncryption.hh"
@@ -18,140 +19,140 @@ static constexpr uint8_t BOSS = EnemyTypeDefinition::Flag::IS_BOSS;
static constexpr uint8_t NONE = 0xFF;
static const vector<EnemyTypeDefinition> type_defs{
// clang-format off
// TYPE FLAGS RT OLDRT BP-STATS BP-ATTACK BP-RESIST ENUM NAME IN-GAME NAME ULTIMATE NAME
{EnemyType::UNKNOWN, 0, NONE, NONE, {}, {}, {}, "UNKNOWN", "__UNKNOWN__", nullptr},
{EnemyType::NONE, 0, NONE, NONE, {}, {}, {}, "NONE", "__NONE__", nullptr},
{EnemyType::NON_ENEMY_NPC, EP1 | EP2 | EP4, NONE, NONE, {}, {}, {}, "NON_ENEMY_NPC", "__NPC__", nullptr},
{EnemyType::AL_RAPPY, EP1 | RARE, 0x06, 0x06, {0x19}, {0x19}, {0x19}, "AL_RAPPY", "Al Rappy", "Pal Rappy"},
{EnemyType::ASTARK, EP4, 0x58, 0x41, {0x09}, {0x0B, 0x0A, 0x0C}, {0x09}, "ASTARK", "Astark", nullptr},
{EnemyType::BA_BOOTA, EP4, 0x62, 0x4F, {0x03}, {0x03, 0x02, 0x04}, {0x03}, "BA_BOOTA", "Ba Boota", nullptr},
{EnemyType::BARBA_RAY, EP2 | BOSS, 0x49, 0x49, {0x0F}, {0x0F}, {0x0F}, "BARBA_RAY", "Barba Ray", nullptr},
{EnemyType::BARBA_RAY_JOINT, EP2 | BOSS, 0x49, 0x49, {0x10}, {0x0F}, {0x10}, "BARBA_RAY_JOINT", "Barba Ray (joint)", nullptr},
{EnemyType::BARBAROUS_WOLF, EP1 | EP2, 0x08, 0x08, {0x03}, {0x03}, {0x03}, "BARBAROUS_WOLF", "Barbarous Wolf", "Gulgus-gue"},
{EnemyType::BEE_L, EP1 | EP2, NONE, NONE, {0x0C}, {0x0C}, {0x0C}, "BEE_L", "Bee L", "Gee L"},
{EnemyType::BEE_R, EP1 | EP2, NONE, NONE, {0x0B}, {0x0B}, {0x0B}, "BEE_R", "Bee R", "Gee R"},
{EnemyType::BOOMA, EP1, 0x09, 0x09, {0x4B}, {0x4E}, {0x4A}, "BOOMA", "Booma", "Bartle"},
{EnemyType::BOOTA, EP4, 0x60, 0x4D, {0x00}, {0x00, 0x02, 0x04}, {0x00}, "BOOTA", "Boota", nullptr},
{EnemyType::BULCLAW, EP1, 0x28, 0x28, {0x1F}, {0x1F}, {0x1F}, "BULCLAW", "Bulclaw", nullptr},
{EnemyType::BULK, EP1, 0x27, 0x27, {0x1F}, {0x1F}, {0x1F}, "BULK", "Bulk", nullptr},
{EnemyType::CANADINE, EP1, 0x1C, 0x1C, {0x07}, {0x07}, {0x07}, "CANADINE", "Canadine", "Canabin"},
{EnemyType::CANADINE_GROUP, EP1, 0x1C, 0x1C, {0x08}, {0x08}, {0x08}, "CANADINE_GROUP", "Canadine (group)", "Canabin (group)"},
{EnemyType::CANANE, EP1, 0x1D, 0x1D, {0x09}, {0x09}, {0x09}, "CANANE", "Canane", "Canune"},
{EnemyType::CHAOS_BRINGER, EP1, 0x24, 0x24, {0x0D}, {0x0D}, {0x0D}, "CHAOS_BRINGER", "Chaos Bringer", "Dark Bringer"},
{EnemyType::CHAOS_SORCERER, EP1 | EP2, 0x1F, 0x1F, {0x0A}, {0x0A}, {0x0A}, "CHAOS_SORCERER", "Chaos Sorceror", "Gran Sorceror"},
{EnemyType::CLAW, EP1, 0x26, 0x26, {0x20}, {0x20}, {0x20}, "CLAW", "Claw", nullptr},
{EnemyType::DARK_BELRA, EP1 | EP2, 0x25, 0x25, {0x0E}, {0x0E, 0x13}, {0x0E}, "DARK_BELRA", "Dark Belra", "Indi Belra"},
{EnemyType::DARK_FALZ_1, EP1 | BOSS, NONE, NONE, {0x36}, {0x36}, {0x36}, "DARK_FALZ_1", "Dark Falz (phase 1)", nullptr},
{EnemyType::DARK_FALZ_2, EP1 | BOSS, 0x2F, 0x2F, {0x37}, {0x37}, {0x37}, "DARK_FALZ_2", "Dark Falz (phase 2)", nullptr},
{EnemyType::DARK_FALZ_3, EP1 | BOSS, 0x2F, 0x2F, {0x38}, {0x38}, {0x38}, "DARK_FALZ_3", "Dark Falz (phase 3)", nullptr},
{EnemyType::DARK_GUNNER, EP1, 0x22, 0x22, {0x1E}, {0x1E}, {0x1E}, "DARK_GUNNER", "Dark Gunner", nullptr},
{EnemyType::DARK_GUNNER_CONTROL, EP1, NONE, NONE, {}, {}, {}, "DARK_GUNNER_CONTROL", "Dark Gunner (control)", nullptr},
{EnemyType::DARVANT, EP1, NONE, NONE, {0x35}, {0x35}, {0x35}, "DARVANT", "Darvant", nullptr},
{EnemyType::DE_ROL_LE, EP1 | BOSS, 0x2D, 0x2D, {0x0F}, {0x0F}, {0x0F}, "DE_ROL_LE", "De Rol Le", "Dal Ral Lie"},
{EnemyType::DE_ROL_LE_BODY, EP1 | BOSS, NONE, NONE, {0x10}, {0x0F}, {0x10}, "DE_ROL_LE_BODY", "De Rol Le (body)", "Dal Ral Lie (body)"},
{EnemyType::DE_ROL_LE_MINE, EP1 | BOSS, NONE, NONE, {0x11}, {0x0F}, {0x11}, "DE_ROL_LE_MINE", "De Rol Le (mine)", "Dal Ral Lie (mine)"},
{EnemyType::DEATH_GUNNER, EP1, 0x23, 0x23, {0x1E}, {0x1E}, {0x1E}, "DEATH_GUNNER", "Death Gunner", nullptr},
{EnemyType::DEL_LILY, EP2, 0x53, 0x53, {0x25}, {0x25}, {0x25}, "DEL_LILY", "Del Lily", nullptr},
{EnemyType::DEL_RAPPY_CRATER, EP4, 0x69, 0x57, {0x06}, {0x06}, {0x06}, "DEL_RAPPY_CRATER", "Del Rappy (crater)", nullptr},
{EnemyType::DEL_RAPPY_DESERT, EP4, 0x69, 0x58, {0x18}, {0x18}, {0x18}, "DEL_RAPPY_DESERT", "Del Rappy (desert)", nullptr},
{EnemyType::DELBITER, EP2, 0x48, 0x48, {0x0D}, {0x0D}, {0x0D}, "DELBITER", "Delbiter", nullptr},
{EnemyType::DELDEPTH, EP2, 0x47, 0x47, {0x30}, {0x30}, {0x30}, "DELDEPTH", "Deldepth", nullptr},
{EnemyType::DELSABER, EP1 | EP2, 0x1E, 0x1E, {0x52}, {0x57, 0x58, 0x59}, {0x51}, "DELSABER", "Delsaber", nullptr},
{EnemyType::DIMENIAN, EP1 | EP2, 0x29, 0x29, {0x53}, {0x5A}, {0x52}, "DIMENIAN", "Dimenian", "Arlan"},
{EnemyType::DOLMDARL, EP2, 0x41, 0x41, {0x50}, {0x55}, {0x4F}, "DOLMDARL", "Dolmdarl", nullptr},
{EnemyType::DOLMOLM, EP2, 0x40, 0x40, {0x4F}, {0x54}, {0x4E}, "DOLMOLM", "Dolmolm", nullptr},
{EnemyType::DORPHON, EP4, 0x63, 0x50, {0x0F}, {0x0F}, {0x0F}, "DORPHON", "Dorphon", nullptr},
{EnemyType::DORPHON_ECLAIR, EP4 | RARE, 0x64, 0x51, {0x10}, {0x10}, {0x10}, "DORPHON_ECLAIR", "Dorphon Eclair", nullptr},
{EnemyType::DRAGON, EP1 | BOSS, 0x2C, 0x2C, {0x12}, {0x12, 0x14, 0x15, 0x16, 0x17}, {0x12}, "DRAGON", "Dragon", "Sil Dragon"},
{EnemyType::DUBCHIC, EP1 | EP2, 0x18, 0x18, {0x1B}, {0x1B}, {0x1B}, "DUBCHIC", "Dubchic", "Dubchich"},
{EnemyType::DUBWITCH, EP1 | EP2, NONE, NONE, {}, {}, {}, "DUBWITCH", "Dubwitch", "Duvuik"},
{EnemyType::EGG_RAPPY, EP2, 0x51, 0x51, {0x19}, {0x19}, {0x19}, "EGG_RAPPY", "Egg Rappy", nullptr},
{EnemyType::EPSIGARD, EP2, NONE, NONE, {0x24}, {0x24}, {0x24}, "EPSIGARD", "Episgard", nullptr},
{EnemyType::EPSILON, EP2, 0x54, 0x54, {0x23}, {0x23}, {0x23}, "EPSILON", "Epsilon", nullptr},
{EnemyType::EVIL_SHARK, EP1, 0x10, 0x10, {0x4F}, {0x54}, {0x4E}, "EVIL_SHARK", "Evil Shark", "Vulmer"},
{EnemyType::GAEL_OR_GIEL, EP2, NONE, NONE, {0x2E}, {0x2E}, {0x2E}, "GAEL_OR_GIEL", "Gael/Giel", nullptr},
{EnemyType::GAL_GRYPHON, EP2 | BOSS, 0x4D, 0x4D, {0x1E}, {0x1E, 0x1F, 0x20, 0x21, 0x22}, {0x1E}, "GAL_GRYPHON", "Gal Gryphon", nullptr},
{EnemyType::GARANZ, EP1 | EP2, 0x19, 0x19, {0x1D}, {0x1D}, {0x1D}, "GARANZ", "Garanz", "Baranz"},
{EnemyType::GEE, EP2, 0x36, 0x36, {0x07}, {0x07}, {0x07}, "GEE", "Gee", nullptr},
{EnemyType::GI_GUE, EP2, 0x37, 0x37, {0x1A}, {0x1A}, {0x1A}, "GI_GUE", "Gi Gue", nullptr},
{EnemyType::GIBBLES, EP2, 0x3D, 0x3D, {0x3D}, {0x3D, 0x3E, 0x3F}, {0x3D}, "GIBBLES", "Gibbles", nullptr},
{EnemyType::GIGOBOOMA, EP1, 0x0B, 0x0B, {0x4D}, {0x50}, {0x4C}, "GIGOBOOMA", "Gigobooma", "Tollaw"},
{EnemyType::GILLCHIC, EP1 | EP2, 0x32, 0x32, {0x1C}, {0x1C}, {0x1C}, "GILLCHIC", "Gillchic", "Gillchich"},
{EnemyType::GIRTABLULU, EP4, 0x5D, 0x48, {0x1F}, {0x1F}, {0x1F}, "GIRTABLULU", "Girtablulu", nullptr},
{EnemyType::GOBOOMA, EP1, 0x0A, 0x0A, {0x4C}, {0x4F}, {0x4B}, "GOBOOMA", "Gobooma", "Barble"},
{EnemyType::GOL_DRAGON, EP2 | BOSS, 0x4C, 0x4C, {0x12}, {0x12, 0x14, 0x15, 0x16, 0x17}, {0x12}, "GOL_DRAGON", "Gol Dragon", nullptr},
{EnemyType::GORAN, EP4, 0x65, 0x52, {0x11}, {0x11, 0x14}, {0x11}, "GORAN", "Goran", nullptr},
{EnemyType::GORAN_DETONATOR, EP4, 0x66, 0x53, {0x13}, {0x13, 0x16}, {0x13}, "GORAN_DETONATOR", "Goran Detonator", nullptr},
{EnemyType::GRASS_ASSASSIN, EP1 | EP2, 0x0C, 0x0C, {0x4E}, {0x51, 0x52, 0x53}, {0x4D}, "GRASS_ASSASSIN", "Grass Assassin", "Crimson Assassin"},
{EnemyType::GUIL_SHARK, EP1, 0x12, 0x12, {0x51}, {0x56}, {0x50}, "GUIL_SHARK", "Guil Shark", "Melqueek"},
{EnemyType::HALLO_RAPPY, EP2, 0x50, 0x50, {0x19}, {0x19}, {0x19}, "HALLO_RAPPY", "Hallo Rappy", nullptr},
{EnemyType::HIDOOM, EP1 | EP2, 0x17, 0x17, {0x32}, {0x32}, {0x32}, "HIDOOM", "Hidoom", nullptr},
{EnemyType::HILDEBEAR, EP1 | EP2, 0x01, 0x01, {0x49}, {0x48, 0x49, 0x4A}, {0x48}, "HILDEBEAR", "Hildebear", "Hildelt"},
{EnemyType::HILDEBLUE, EP1 | EP2 | RARE, 0x02, 0x02, {0x4A}, {0x4B, 0x4C, 0x4D}, {0x49}, "HILDEBLUE", "Hildeblue", "Hildetorr"},
{EnemyType::ILL_GILL, EP2, 0x52, 0x52, {0x26}, {0x26, 0x27, 0x28, 0x29}, {0x26}, "ILL_GILL", "Ill Gill", nullptr},
{EnemyType::KONDRIEU, EP4 | RARE | BOSS, 0x6C, 0x5B, {0x28, 0x2A}, {0x28, 0x2A}, {0x28, 0x2A}, "KONDRIEU", "Kondrieu", nullptr},
{EnemyType::KONDRIEU_SPINNER, EP4 | RARE | BOSS, 0x6C, 0x5B, {0x29, 0x2B}, {0x29, 0x2B}, {0x29, 0x2B}, "KONDRIEU_SPINNER", "Kondrieu (spinner)", nullptr},
{EnemyType::LA_DIMENIAN, EP1 | EP2, 0x2A, 0x2A, {0x54}, {0x5B}, {0x53}, "LA_DIMENIAN", "La Dimenian", "Merlan"},
{EnemyType::LOVE_RAPPY, EP2, 0x33, 0x33, {0x19}, {0x19}, {0x19}, "LOVE_RAPPY", "Love Rappy", nullptr},
{EnemyType::MERICARAND, EP2, 0x38, 0x38, {0x3A}, {0x3A}, {0x3A}, "MERICARAND", "Mericarand", nullptr},
{EnemyType::MERICAROL, EP2, 0x38, 0x38, {0x3A}, {0x3A}, {0x3A}, "MERICAROL", "Mericarol", nullptr},
{EnemyType::MERICUS, EP2 | RARE, 0x3A, 0x3A, {0x46}, {0x46}, {0x46}, "MERICUS", "Mericus", nullptr},
{EnemyType::MERIKLE, EP2 | RARE, 0x39, 0x39, {0x45}, {0x45}, {0x45}, "MERIKLE", "Merikle", nullptr},
{EnemyType::MERILLIA, EP2, 0x34, 0x34, {0x4B}, {0x4E}, {0x4A}, "MERILLIA", "Merillia", nullptr},
{EnemyType::MERILTAS, EP2, 0x35, 0x35, {0x4C}, {0x4F}, {0x4B}, "MERILTAS", "Meriltas", nullptr},
{EnemyType::MERISSA_A, EP4, 0x5B, 0x46, {0x19}, {0x19}, {0x19}, "MERISSA_A", "Merissa A", nullptr},
{EnemyType::MERISSA_AA, EP4 | RARE, 0x5C, 0x47, {0x1A}, {0x1A}, {0x1A}, "MERISSA_AA", "Merissa AA", nullptr},
{EnemyType::MIGIUM, EP1 | EP2, 0x16, 0x16, {0x33}, {0x33}, {0x33}, "MIGIUM", "Migium", nullptr},
{EnemyType::MONEST, EP1 | EP2, 0x04, 0x04, {0x01}, {0x01}, {0x01}, "MONEST", "Monest", "Mothvist"},
{EnemyType::MORFOS, EP2, 0x42, 0x42, {0x40}, {0x40, 0x50}, {0x40}, "MORFOS", "Morfos", nullptr},
{EnemyType::MOTHMANT, EP1 | EP2, 0x03, 0x03, {0x00}, {0x00}, {0x00}, "MOTHMANT", "Mothmant", "Mothvert"},
{EnemyType::NANO_DRAGON, EP1, 0x0F, 0x0F, {0x1A}, {0x1A}, {0x1A}, "NANO_DRAGON", "Nano Dragon", nullptr},
{EnemyType::NAR_LILY, EP1 | EP2 | RARE, 0x0E, 0x0E, {0x05}, {0x05}, {0x05}, "NAR_LILY", "Nar Lily", "Mil Lily"},
{EnemyType::OLGA_FLOW_1, EP2 | BOSS, NONE, NONE, {0x2B}, {0x2B}, {0x2B}, "OLGA_FLOW_1", "Olga Flow (phase 1)", nullptr},
{EnemyType::OLGA_FLOW_2, EP2 | BOSS, 0x4E, 0x4E, {0x2C}, {0x2C}, {0x2C}, "OLGA_FLOW_2", "Olga Flow (phase 2)", nullptr},
{EnemyType::PAL_SHARK, EP1, 0x11, 0x11, {0x50}, {0x55}, {0x4F}, "PAL_SHARK", "Pal Shark", "Govulmer"},
{EnemyType::PAN_ARMS, EP1 | EP2, 0x15, 0x15, {0x31}, {0x31}, {0x31}, "PAN_ARMS", "Pan Arms", nullptr},
{EnemyType::PAZUZU_CRATER, EP4 | RARE, 0x5F, 0x4B, {0x08}, {0x08}, {0x08}, "PAZUZU_CRATER", "Pazuzu (crater)", nullptr},
{EnemyType::PAZUZU_DESERT, EP4 | RARE, 0x5F, 0x4C, {0x1C}, {0x1C}, {0x1C}, "PAZUZU_DESERT", "Pazuzu (desert)", nullptr},
{EnemyType::PIG_RAY, EP2, 0x4A, NONE, {0x08}, {0x08}, {0x08}, "PIG_RAY", "Pig Ray", nullptr},
{EnemyType::POFUILLY_SLIME, EP1, 0x13, 0x13, {0x30}, {0x30}, {0x30}, "POFUILLY_SLIME", "Pofuilly Slime", nullptr},
{EnemyType::POUILLY_SLIME, EP1 | RARE, 0x14, 0x14, {0x34}, {0x34}, {0x34}, "POUILLY_SLIME", "Pouilly Slime", nullptr},
{EnemyType::POISON_LILY, EP1 | EP2, 0x0D, 0x0D, {0x04}, {0x04}, {0x04}, "POISON_LILY", "Poison Lily", "Ob Lily"},
{EnemyType::PYRO_GORAN, EP4, 0x67, 0x54, {0x12}, {0x12, 0x15}, {0x12}, "PYRO_GORAN", "Pyro Goran", nullptr},
{EnemyType::RAG_RAPPY, EP1 | EP2, 0x05, 0x05, {0x18}, {0x18}, {0x18}, "RAG_RAPPY", "Rag Rappy", "El Rappy"},
{EnemyType::RECOBOX, EP2, 0x43, 0x43, {0x41}, {0x41}, {0x41}, "RECOBOX", "Recobox", nullptr},
{EnemyType::RECON, EP2, 0x44, 0x44, {0x42}, {0x42}, {0x42}, "RECON", "Recon", nullptr},
{EnemyType::SAINT_MILION, EP4 | BOSS, 0x6A, 0x59, {0x20, 0x22}, {0x20, 0x22}, {0x20, 0x22}, "SAINT_MILION", "Saint-Milion", nullptr},
{EnemyType::SAINT_MILION_SPINNER, EP4 | BOSS, 0x6A, 0x59, {0x21, 0x23}, {0x21, 0x23}, {0x21, 0x23}, "SAINT_MILION_SPINNER", "Saint-Milion (spinner)", nullptr},
{EnemyType::SAINT_RAPPY, EP2, 0x4F, 0x4F, {0x19}, {0x19}, {0x19}, "SAINT_RAPPY", "Saint Rappy", nullptr},
{EnemyType::SAND_RAPPY_CRATER, EP4, 0x68, 0x55, {0x05}, {0x05}, {0x05}, "SAND_RAPPY_CRATER", "Sand Rappy (crater)", nullptr},
{EnemyType::SAND_RAPPY_DESERT, EP4, 0x68, 0x56, {0x17}, {0x17}, {0x17}, "SAND_RAPPY_DESERT", "Sand Rappy (desert)", nullptr},
{EnemyType::SATELLITE_LIZARD_CRATER, EP4, 0x5A, 0x44, {0x0D}, {0x0D}, {0x0D}, "SATELLITE_LIZARD_CRATER", "Satellite Lizard (crater)", nullptr},
{EnemyType::SATELLITE_LIZARD_DESERT, EP4, 0x5A, 0x45, {0x1D}, {0x1D}, {0x1D}, "SATELLITE_LIZARD_DESERT", "Satellite Lizard (desert)", nullptr},
{EnemyType::SAVAGE_WOLF, EP1 | EP2, 0x07, 0x07, {0x02}, {0x02}, {0x02}, "SAVAGE_WOLF", "Savage Wolf", "Gulgus"},
{EnemyType::SHAMBERTIN, EP4 | BOSS, 0x6B, 0x5A, {0x24, 0x26}, {0x24, 0x26}, {0x24, 0x26}, "SHAMBERTIN", "Shambertin", nullptr},
{EnemyType::SHAMBERTIN_SPINNER, EP4 | BOSS, 0x6B, 0x5A, {0x25, 0x27}, {0x25, 0x27}, {0x25, 0x27}, "SHAMBERTIN_SPINNER", "Shambertin (spinner)", nullptr},
{EnemyType::SINOW_BEAT, EP1, 0x1A, 0x1A, {0x06}, {0x06}, {0x06}, "SINOW_BEAT", "Sinow Beat", "Sinow Blue"},
{EnemyType::SINOW_BERILL, EP2, 0x3E, 0x3E, {0x06}, {0x06}, {0x06}, "SINOW_BERILL", "Sinow Berill", nullptr},
{EnemyType::SINOW_GOLD, EP1, 0x1B, 0x1B, {0x13}, {0x47}, {0x13}, "SINOW_GOLD", "Sinow Gold", "Sinow Red"},
{EnemyType::SINOW_SPIGELL, EP2, 0x3F, 0x3F, {0x13}, {0x47}, {0x13}, "SINOW_SPIGELL", "Sinow Spigell", nullptr},
{EnemyType::SINOW_ZELE, EP2, 0x46, 0x46, {0x44}, {0x44}, {0x44}, "SINOW_ZELE", "Sinow Zele", nullptr},
{EnemyType::SINOW_ZOA, EP2, 0x45, 0x45, {0x43}, {0x43}, {0x43}, "SINOW_ZOA", "Sinow Zoa", nullptr},
{EnemyType::SO_DIMENIAN, EP1 | EP2, 0x2B, 0x2B, {0x55}, {0x5C}, {0x54}, "SO_DIMENIAN", "So Dimenian", "Del-D"},
{EnemyType::UL_GIBBON, EP2, 0x3B, 0x3B, {0x3B}, {0x3B}, {0x3B}, "UL_GIBBON", "Ul Gibbon", nullptr},
{EnemyType::UL_RAY, EP2, 0x4B, NONE, {0x09}, {0x09}, {0x09}, "UL_RAY", "Ul Ray", nullptr},
{EnemyType::VOL_OPT_1, EP1 | BOSS, NONE, NONE, {0x21}, {0x21}, {0x21}, "VOL_OPT_1", "Vol Opt (phase 1)", "Vol Opt ver.2 (phase 1)"},
{EnemyType::VOL_OPT_2, EP1 | BOSS, 0x2E, 0x2E, {0x25}, {0x25}, {0x25}, "VOL_OPT_2", "Vol Opt (phase 2)", "Vol Opt ver.2 (phase 2)"},
{EnemyType::VOL_OPT_AMP, EP1 | BOSS, NONE, NONE, {0x24}, {0x24}, {0x24}, "VOL_OPT_AMP", "Vol Opt (amp)", "Vol Opt ver.2 (amp)"},
{EnemyType::VOL_OPT_CORE, EP1 | BOSS, NONE, NONE, {0x26}, {0x26}, {0x26}, "VOL_OPT_CORE", "Vol Opt (core)", "Vol Opt ver.2 (core)"},
{EnemyType::VOL_OPT_MONITOR, EP1 | BOSS, NONE, NONE, {0x23}, {0x23}, {0x23}, "VOL_OPT_MONITOR", "Vol Opt (monitor)", "Vol Opt ver.2 (monitor)"},
{EnemyType::VOL_OPT_PILLAR, EP1 | BOSS, NONE, NONE, {0x22}, {0x22}, {0x22}, "VOL_OPT_PILLAR", "Vol Opt (pillar)", "Vol Opt ver.2 (pillar)"},
{EnemyType::YOWIE_CRATER, EP4, 0x59, 0x42, {0x0E}, {0x0E}, {0x0E}, "YOWIE_CRATER", "Yowie (crater)", nullptr},
{EnemyType::YOWIE_DESERT, EP4, 0x59, 0x43, {0x1E}, {0x1E}, {0x1E}, "YOWIE_DESERT", "Yowie (desert)", nullptr},
{EnemyType::ZE_BOOTA, EP4, 0x61, 0x4E, {0x01}, {0x01, 0x02, 0x04}, {0x01}, "ZE_BOOTA", "Ze Boota", nullptr},
{EnemyType::ZOL_GIBBON, EP2, 0x3C, 0x3C, {0x3C}, {0x3C}, {0x3C}, "ZOL_GIBBON", "Zol Gibbon", nullptr},
{EnemyType::ZU_CRATER, EP4, 0x5E, 0x49, {0x07}, {0x07}, {0x07}, "ZU_CRATER", "Zu (crater)", nullptr},
{EnemyType::ZU_DESERT, EP4, 0x5E, 0x4A, {0x1B}, {0x1B}, {0x1B}, "ZU_DESERT", "Zu (desert)", nullptr},
// TYPE FLAGS RT OLDRT BP-STATS BP-ATTACK BP-RESIST BP-MOVEMENT ENUM NAME IN-GAME NAME ULTIMATE NAME
{EnemyType::UNKNOWN, 0, NONE, NONE, {}, {}, {}, {}, "UNKNOWN", "__UNKNOWN__", nullptr},
{EnemyType::NONE, 0, NONE, NONE, {}, {}, {}, {}, "NONE", "__NONE__", nullptr},
{EnemyType::NON_ENEMY_NPC, EP1 | EP2 | EP4, NONE, NONE, {}, {}, {}, {}, "NON_ENEMY_NPC", "__NPC__", nullptr},
{EnemyType::AL_RAPPY, EP1 | RARE, 0x06, 0x06, {0x19}, {0x19}, {0x19}, {0x19}, "AL_RAPPY", "Al Rappy", "Pal Rappy"},
{EnemyType::ASTARK, EP4, 0x58, 0x41, {0x09}, {0x0B, 0x0A, 0x0C}, {0x09}, {0x09}, "ASTARK", "Astark", nullptr},
{EnemyType::BA_BOOTA, EP4, 0x62, 0x4F, {0x03}, {0x03, 0x02, 0x04}, {0x03}, {0x03}, "BA_BOOTA", "Ba Boota", nullptr},
{EnemyType::BARBA_RAY, EP2 | BOSS, 0x49, 0x49, {0x0F}, {0x0F}, {0x0F}, {0x0F}, "BARBA_RAY", "Barba Ray", nullptr},
{EnemyType::BARBA_RAY_JOINT, EP2 | BOSS, 0x49, 0x49, {0x10}, {0x0F}, {0x10}, {}, "BARBA_RAY_JOINT", "Barba Ray (joint)", nullptr},
{EnemyType::BARBAROUS_WOLF, EP1 | EP2, 0x08, 0x08, {0x03}, {0x03}, {0x03}, {0x03}, "BARBAROUS_WOLF", "Barbarous Wolf", "Gulgus-gue"},
{EnemyType::BEE_L, EP1 | EP2, NONE, NONE, {0x0C}, {0x0C}, {0x0C}, {0x0C}, "BEE_L", "Bee L", "Gee L"},
{EnemyType::BEE_R, EP1 | EP2, NONE, NONE, {0x0B}, {0x0B}, {0x0B}, {0x0B}, "BEE_R", "Bee R", "Gee R"},
{EnemyType::BOOMA, EP1, 0x09, 0x09, {0x4B}, {0x4E}, {0x4A}, {0x4A}, "BOOMA", "Booma", "Bartle"},
{EnemyType::BOOTA, EP4, 0x60, 0x4D, {0x00}, {0x00, 0x02, 0x04}, {0x00}, {0x00}, "BOOTA", "Boota", nullptr},
{EnemyType::BULCLAW, EP1, 0x28, 0x28, {0x1F}, {0x1F}, {0x1F}, {0x1F, 0x20}, "BULCLAW", "Bulclaw", nullptr},
{EnemyType::BULK, EP1, 0x27, 0x27, {0x1F}, {0x1F}, {0x1F}, {0x1F, 0x20}, "BULK", "Bulk", nullptr},
{EnemyType::CANADINE, EP1, 0x1C, 0x1C, {0x07}, {0x07}, {0x07}, {0x07}, "CANADINE", "Canadine", "Canabin"},
{EnemyType::CANADINE_GROUP, EP1, 0x1C, 0x1C, {0x08}, {0x08}, {0x08}, {0x08}, "CANADINE_GROUP", "Canadine (group)", "Canabin (group)"},
{EnemyType::CANANE, EP1, 0x1D, 0x1D, {0x09}, {0x09}, {0x09}, {0x09}, "CANANE", "Canane", "Canune"},
{EnemyType::CHAOS_BRINGER, EP1, 0x24, 0x24, {0x0D}, {0x0D}, {0x0D}, {0x0A, 0x0D}, "CHAOS_BRINGER", "Chaos Bringer", "Dark Bringer"},
{EnemyType::CHAOS_SORCERER, EP1 | EP2, 0x1F, 0x1F, {0x0A}, {0x0A}, {0x0A}, {}, "CHAOS_SORCERER", "Chaos Sorceror", "Gran Sorceror"},
{EnemyType::CLAW, EP1, 0x26, 0x26, {0x20}, {0x20}, {0x20}, {}, "CLAW", "Claw", nullptr},
{EnemyType::DARK_BELRA, EP1 | EP2, 0x25, 0x25, {0x0E}, {0x0E, 0x13}, {0x0E}, {0x0E}, "DARK_BELRA", "Dark Belra", "Indi Belra"},
{EnemyType::DARK_FALZ_1, EP1 | BOSS, NONE, NONE, {0x36}, {0x36}, {0x36}, {0x36, 0x39}, "DARK_FALZ_1", "Dark Falz (phase 1)", nullptr},
{EnemyType::DARK_FALZ_2, EP1 | BOSS, 0x2F, 0x2F, {0x37}, {0x37}, {0x37}, {0x37}, "DARK_FALZ_2", "Dark Falz (phase 2)", nullptr},
{EnemyType::DARK_FALZ_3, EP1 | BOSS, 0x2F, 0x2F, {0x38}, {0x38}, {0x38}, {0x38}, "DARK_FALZ_3", "Dark Falz (phase 3)", nullptr},
{EnemyType::DARK_GUNNER, EP1, 0x22, 0x22, {0x1E}, {0x1E}, {0x1E}, {0x1E}, "DARK_GUNNER", "Dark Gunner", nullptr},
{EnemyType::DARK_GUNNER_CONTROL, EP1, NONE, NONE, {}, {}, {}, {}, "DARK_GUNNER_CONTROL", "Dark Gunner (control)", nullptr},
{EnemyType::DARVANT, EP1, NONE, NONE, {0x35}, {0x35}, {0x35}, {0x35, 0x39}, "DARVANT", "Darvant", nullptr},
{EnemyType::DE_ROL_LE, EP1 | BOSS, 0x2D, 0x2D, {0x0F}, {0x0F}, {0x0F}, {0x0F}, "DE_ROL_LE", "De Rol Le", "Dal Ral Lie"},
{EnemyType::DE_ROL_LE_BODY, EP1 | BOSS, NONE, NONE, {0x10}, {0x0F}, {0x10}, {0x0F}, "DE_ROL_LE_BODY", "De Rol Le (body)", "Dal Ral Lie (body)"},
{EnemyType::DE_ROL_LE_MINE, EP1 | BOSS, NONE, NONE, {0x11}, {0x0F}, {0x11}, {0x0F}, "DE_ROL_LE_MINE", "De Rol Le (mine)", "Dal Ral Lie (mine)"},
{EnemyType::DEATH_GUNNER, EP1, 0x23, 0x23, {0x1E}, {0x1E}, {0x1E}, {0x1E}, "DEATH_GUNNER", "Death Gunner", nullptr},
{EnemyType::DEL_LILY, EP2, 0x53, 0x53, {0x25}, {0x25}, {0x25}, {0x25}, "DEL_LILY", "Del Lily", nullptr},
{EnemyType::DEL_RAPPY_CRATER, EP4, 0x69, 0x57, {0x06}, {0x06}, {0x06}, {0x06}, "DEL_RAPPY_CRATER", "Del Rappy (crater)", nullptr},
{EnemyType::DEL_RAPPY_DESERT, EP4, 0x69, 0x58, {0x18}, {0x18}, {0x18}, {0x18}, "DEL_RAPPY_DESERT", "Del Rappy (desert)", nullptr},
{EnemyType::DELBITER, EP2, 0x48, 0x48, {0x0D}, {0x0D}, {0x0D}, {0x0D}, "DELBITER", "Delbiter", nullptr},
{EnemyType::DELDEPTH, EP2, 0x47, 0x47, {0x30}, {0x30}, {0x30}, {0x30}, "DELDEPTH", "Deldepth", nullptr},
{EnemyType::DELSABER, EP1 | EP2, 0x1E, 0x1E, {0x52}, {0x57, 0x58, 0x59}, {0x51}, {0x51}, "DELSABER", "Delsaber", nullptr},
{EnemyType::DIMENIAN, EP1 | EP2, 0x29, 0x29, {0x53}, {0x5A}, {0x52}, {0x52}, "DIMENIAN", "Dimenian", "Arlan"},
{EnemyType::DOLMDARL, EP2, 0x41, 0x41, {0x50}, {0x55}, {0x4F}, {0x4F}, "DOLMDARL", "Dolmdarl", nullptr},
{EnemyType::DOLMOLM, EP2, 0x40, 0x40, {0x4F}, {0x54}, {0x4E}, {0x4E}, "DOLMOLM", "Dolmolm", nullptr},
{EnemyType::DORPHON, EP4, 0x63, 0x50, {0x0F}, {0x0F}, {0x0F}, {0x0F}, "DORPHON", "Dorphon", nullptr},
{EnemyType::DORPHON_ECLAIR, EP4 | RARE, 0x64, 0x51, {0x10}, {0x10}, {0x10}, {0x10}, "DORPHON_ECLAIR", "Dorphon Eclair", nullptr},
{EnemyType::DRAGON, EP1 | BOSS, 0x2C, 0x2C, {0x12}, {0x12, 0x14, 0x15, 0x16, 0x17}, {0x12}, {0x11}, "DRAGON", "Dragon", "Sil Dragon"},
{EnemyType::DUBCHIC, EP1 | EP2, 0x18, 0x18, {0x1B}, {0x1B}, {0x1B}, {0x1B}, "DUBCHIC", "Dubchic", "Dubchich"},
{EnemyType::DUBWITCH, EP1 | EP2, NONE, NONE, {}, {}, {}, {}, "DUBWITCH", "Dubwitch", "Duvuik"},
{EnemyType::EGG_RAPPY, EP2, 0x51, 0x51, {0x19}, {0x19}, {0x19}, {0x19}, "EGG_RAPPY", "Egg Rappy", nullptr},
{EnemyType::EPSIGARD, EP2, NONE, NONE, {0x24}, {0x24}, {0x24}, {0x24}, "EPSIGARD", "Episgard", nullptr},
{EnemyType::EPSILON, EP2, 0x54, 0x54, {0x23}, {0x23}, {0x23}, {0x23}, "EPSILON", "Epsilon", nullptr},
{EnemyType::EVIL_SHARK, EP1, 0x10, 0x10, {0x4F}, {0x54}, {0x4E}, {0x4E}, "EVIL_SHARK", "Evil Shark", "Vulmer"},
{EnemyType::GAEL_OR_GIEL, EP2, NONE, NONE, {0x2E}, {0x2E}, {0x2E}, {}, "GAEL_OR_GIEL", "Gael/Giel", nullptr},
{EnemyType::GAL_GRYPHON, EP2 | BOSS, 0x4D, 0x4D, {0x1E}, {0x1E, 0x1F, 0x20, 0x21, 0x22}, {0x1E}, {0x1E, 0x1F, 0x20}, "GAL_GRYPHON", "Gal Gryphon", nullptr},
{EnemyType::GARANZ, EP1 | EP2, 0x19, 0x19, {0x1D}, {0x1D}, {0x1D}, {0x1D}, "GARANZ", "Garanz", "Baranz"},
{EnemyType::GEE, EP2, 0x36, 0x36, {0x07}, {0x07}, {0x07}, {0x07}, "GEE", "Gee", nullptr},
{EnemyType::GI_GUE, EP2, 0x37, 0x37, {0x1A}, {0x1A}, {0x1A}, {0x1A}, "GI_GUE", "Gi Gue", nullptr},
{EnemyType::GIBBLES, EP2, 0x3D, 0x3D, {0x3D}, {0x3D, 0x3E, 0x3F}, {0x3D}, {0x3D}, "GIBBLES", "Gibbles", nullptr},
{EnemyType::GIGOBOOMA, EP1, 0x0B, 0x0B, {0x4D}, {0x50}, {0x4C}, {0x4C}, "GIGOBOOMA", "Gigobooma", "Tollaw"},
{EnemyType::GILLCHIC, EP1 | EP2, 0x32, 0x32, {0x1C}, {0x1C}, {0x1C}, {0x1C}, "GILLCHIC", "Gillchic", "Gillchich"},
{EnemyType::GIRTABLULU, EP4, 0x5D, 0x48, {0x1F}, {0x1F}, {0x1F}, {0x1F}, "GIRTABLULU", "Girtablulu", nullptr},
{EnemyType::GOBOOMA, EP1, 0x0A, 0x0A, {0x4C}, {0x4F}, {0x4B}, {0x4B}, "GOBOOMA", "Gobooma", "Barble"},
{EnemyType::GOL_DRAGON, EP2 | BOSS, 0x4C, 0x4C, {0x12}, {0x12, 0x14, 0x15, 0x16, 0x17}, {0x12}, {0x11, 0x12, 0x13}, "GOL_DRAGON", "Gol Dragon", nullptr},
{EnemyType::GORAN, EP4, 0x65, 0x52, {0x11}, {0x11, 0x14}, {0x11}, {0x11}, "GORAN", "Goran", nullptr},
{EnemyType::GORAN_DETONATOR, EP4, 0x66, 0x53, {0x13}, {0x13, 0x16}, {0x13}, {0x13}, "GORAN_DETONATOR", "Goran Detonator", nullptr},
{EnemyType::GRASS_ASSASSIN, EP1 | EP2, 0x0C, 0x0C, {0x4E}, {0x51, 0x52, 0x53}, {0x4D}, {0x4D}, "GRASS_ASSASSIN", "Grass Assassin", "Crimson Assassin"},
{EnemyType::GUIL_SHARK, EP1, 0x12, 0x12, {0x51}, {0x56}, {0x50}, {0x50}, "GUIL_SHARK", "Guil Shark", "Melqueek"},
{EnemyType::HALLO_RAPPY, EP2, 0x50, 0x50, {0x19}, {0x19}, {0x19}, {0x19}, "HALLO_RAPPY", "Hallo Rappy", nullptr},
{EnemyType::HIDOOM, EP1 | EP2, 0x17, 0x17, {0x32}, {0x32}, {0x32}, {0x32}, "HIDOOM", "Hidoom", nullptr},
{EnemyType::HILDEBEAR, EP1 | EP2, 0x01, 0x01, {0x49}, {0x48, 0x49, 0x4A}, {0x48}, {0x48}, "HILDEBEAR", "Hildebear", "Hildelt"},
{EnemyType::HILDEBLUE, EP1 | EP2 | RARE, 0x02, 0x02, {0x4A}, {0x4B, 0x4C, 0x4D}, {0x49}, {0x49}, "HILDEBLUE", "Hildeblue", "Hildetorr"},
{EnemyType::ILL_GILL, EP2, 0x52, 0x52, {0x26}, {0x26, 0x27, 0x28, 0x29}, {0x26}, {0x26}, "ILL_GILL", "Ill Gill", nullptr},
{EnemyType::KONDRIEU, EP4 | RARE | BOSS, 0x6C, 0x5B, {0x28, 0x2A}, {0x28, 0x2A}, {0x28, 0x2A}, {0x28, 0x2A}, "KONDRIEU", "Kondrieu", nullptr},
{EnemyType::KONDRIEU_SPINNER, EP4 | RARE | BOSS, 0x6C, 0x5B, {0x29, 0x2B}, {0x29, 0x2B}, {0x29, 0x2B}, {0x29, 0x2B}, "KONDRIEU_SPINNER", "Kondrieu (spinner)", nullptr},
{EnemyType::LA_DIMENIAN, EP1 | EP2, 0x2A, 0x2A, {0x54}, {0x5B}, {0x53}, {0x53}, "LA_DIMENIAN", "La Dimenian", "Merlan"},
{EnemyType::LOVE_RAPPY, EP2, 0x33, 0x33, {0x19}, {0x19}, {0x19}, {0x19}, "LOVE_RAPPY", "Love Rappy", nullptr},
{EnemyType::MERICARAND, EP2, 0x38, 0x38, {0x3A}, {0x3A}, {0x3A}, {0x3A}, "MERICARAND", "Mericarand", nullptr},
{EnemyType::MERICAROL, EP2, 0x38, 0x38, {0x3A}, {0x3A}, {0x3A}, {0x3A}, "MERICAROL", "Mericarol", nullptr},
{EnemyType::MERICUS, EP2 | RARE, 0x3A, 0x3A, {0x46}, {0x46}, {0x46}, {0x46}, "MERICUS", "Mericus", nullptr},
{EnemyType::MERIKLE, EP2 | RARE, 0x39, 0x39, {0x45}, {0x45}, {0x45}, {0x45}, "MERIKLE", "Merikle", nullptr},
{EnemyType::MERILLIA, EP2, 0x34, 0x34, {0x4B}, {0x4E}, {0x4A}, {0x4A}, "MERILLIA", "Merillia", nullptr},
{EnemyType::MERILTAS, EP2, 0x35, 0x35, {0x4C}, {0x4F}, {0x4B}, {0x4B}, "MERILTAS", "Meriltas", nullptr},
{EnemyType::MERISSA_A, EP4, 0x5B, 0x46, {0x19}, {0x19}, {0x19}, {0x19}, "MERISSA_A", "Merissa A", nullptr},
{EnemyType::MERISSA_AA, EP4 | RARE, 0x5C, 0x47, {0x1A}, {0x1A}, {0x1A}, {0x1A}, "MERISSA_AA", "Merissa AA", nullptr},
{EnemyType::MIGIUM, EP1 | EP2, 0x16, 0x16, {0x33}, {0x33}, {0x33}, {0x33}, "MIGIUM", "Migium", nullptr},
{EnemyType::MONEST, EP1 | EP2, 0x04, 0x04, {0x01}, {0x01}, {0x01}, {0x01}, "MONEST", "Monest", "Mothvist"},
{EnemyType::MORFOS, EP2, 0x42, 0x42, {0x40}, {0x40, 0x50}, {0x40}, {0x40}, "MORFOS", "Morfos", nullptr},
{EnemyType::MOTHMANT, EP1 | EP2, 0x03, 0x03, {0x00}, {0x00}, {0x00}, {0x00}, "MOTHMANT", "Mothmant", "Mothvert"},
{EnemyType::NANO_DRAGON, EP1, 0x0F, 0x0F, {0x1A}, {0x1A}, {0x1A}, {0x1A}, "NANO_DRAGON", "Nano Dragon", nullptr},
{EnemyType::NAR_LILY, EP1 | EP2 | RARE, 0x0E, 0x0E, {0x05}, {0x05}, {0x05}, {0x05}, "NAR_LILY", "Nar Lily", "Mil Lily"},
{EnemyType::OLGA_FLOW_1, EP2 | BOSS, NONE, NONE, {0x2B}, {0x2B}, {0x2B}, {0x2B, 0x2D, 0x2F}, "OLGA_FLOW_1", "Olga Flow (phase 1)", nullptr},
{EnemyType::OLGA_FLOW_2, EP2 | BOSS, 0x4E, 0x4E, {0x2C}, {0x2C}, {0x2C}, {0x2C, 0x2D, 0x3E, 0x2F}, "OLGA_FLOW_2", "Olga Flow (phase 2)", nullptr},
{EnemyType::PAL_SHARK, EP1, 0x11, 0x11, {0x50}, {0x55}, {0x4F}, {0x4F}, "PAL_SHARK", "Pal Shark", "Govulmer"},
{EnemyType::PAN_ARMS, EP1 | EP2, 0x15, 0x15, {0x31}, {0x31}, {0x31}, {0x31}, "PAN_ARMS", "Pan Arms", nullptr},
{EnemyType::PAZUZU_CRATER, EP4 | RARE, 0x5F, 0x4B, {0x08}, {0x08}, {0x08}, {0x08}, "PAZUZU_CRATER", "Pazuzu (crater)", nullptr},
{EnemyType::PAZUZU_DESERT, EP4 | RARE, 0x5F, 0x4C, {0x1C}, {0x1C}, {0x1C}, {0x1C}, "PAZUZU_DESERT", "Pazuzu (desert)", nullptr},
{EnemyType::PIG_RAY, EP2, 0x4A, NONE, {0x08}, {0x08}, {0x08}, {0x08}, "PIG_RAY", "Pig Ray", nullptr},
{EnemyType::POFUILLY_SLIME, EP1, 0x13, 0x13, {0x30}, {0x30}, {0x30}, {0x30}, "POFUILLY_SLIME", "Pofuilly Slime", nullptr},
{EnemyType::POISON_LILY, EP1 | EP2, 0x0D, 0x0D, {0x04}, {0x04}, {0x04}, {0x04}, "POISON_LILY", "Poison Lily", "Ob Lily"},
{EnemyType::POUILLY_SLIME, EP1 | RARE, 0x14, 0x14, {0x34}, {0x34}, {0x34}, {0x34}, "POUILLY_SLIME", "Pouilly Slime", nullptr},
{EnemyType::PYRO_GORAN, EP4, 0x67, 0x54, {0x12}, {0x12, 0x15}, {0x12}, {0x12}, "PYRO_GORAN", "Pyro Goran", nullptr},
{EnemyType::RAG_RAPPY, EP1 | EP2, 0x05, 0x05, {0x18}, {0x18}, {0x18}, {0x18}, "RAG_RAPPY", "Rag Rappy", "El Rappy"},
{EnemyType::RECOBOX, EP2, 0x43, 0x43, {0x41}, {0x41}, {0x41}, {0x41}, "RECOBOX", "Recobox", nullptr},
{EnemyType::RECON, EP2, 0x44, 0x44, {0x42}, {0x42}, {0x42}, {0x42}, "RECON", "Recon", nullptr},
{EnemyType::SAINT_MILION, EP4 | BOSS, 0x6A, 0x59, {0x20, 0x22}, {0x20, 0x22}, {0x20, 0x22}, {0x20, 0x22}, "SAINT_MILION", "Saint-Milion", nullptr},
{EnemyType::SAINT_MILION_SPINNER, EP4 | BOSS, 0x6A, 0x59, {0x21, 0x23}, {0x21, 0x23}, {0x21, 0x23}, {0x21, 0x23}, "SAINT_MILION_SPINNER", "Saint-Milion (spinner)", nullptr},
{EnemyType::SAINT_RAPPY, EP2, 0x4F, 0x4F, {0x19}, {0x19}, {0x19}, {0x19}, "SAINT_RAPPY", "Saint Rappy", nullptr},
{EnemyType::SAND_RAPPY_CRATER, EP4, 0x68, 0x55, {0x05}, {0x05}, {0x05}, {0x05}, "SAND_RAPPY_CRATER", "Sand Rappy (crater)", nullptr},
{EnemyType::SAND_RAPPY_DESERT, EP4, 0x68, 0x56, {0x17}, {0x17}, {0x17}, {0x17}, "SAND_RAPPY_DESERT", "Sand Rappy (desert)", nullptr},
{EnemyType::SATELLITE_LIZARD_CRATER, EP4, 0x5A, 0x44, {0x0D}, {0x0D}, {0x0D}, {0x0D}, "SATELLITE_LIZARD_CRATER", "Satellite Lizard (crater)", nullptr},
{EnemyType::SATELLITE_LIZARD_DESERT, EP4, 0x5A, 0x45, {0x1D}, {0x1D}, {0x1D}, {0x1D}, "SATELLITE_LIZARD_DESERT", "Satellite Lizard (desert)", nullptr},
{EnemyType::SAVAGE_WOLF, EP1 | EP2, 0x07, 0x07, {0x02}, {0x02}, {0x02}, {0x02}, "SAVAGE_WOLF", "Savage Wolf", "Gulgus"},
{EnemyType::SHAMBERTIN, EP4 | BOSS, 0x6B, 0x5A, {0x24, 0x26}, {0x24, 0x26}, {0x24, 0x26}, {0x24, 0x26}, "SHAMBERTIN", "Shambertin", nullptr},
{EnemyType::SHAMBERTIN_SPINNER, EP4 | BOSS, 0x6B, 0x5A, {0x25, 0x27}, {0x25, 0x27}, {0x25, 0x27}, {0x25, 0x27}, "SHAMBERTIN_SPINNER", "Shambertin (spinner)", nullptr},
{EnemyType::SINOW_BEAT, EP1, 0x1A, 0x1A, {0x06}, {0x06}, {0x06}, {0x06}, "SINOW_BEAT", "Sinow Beat", "Sinow Blue"},
{EnemyType::SINOW_BERILL, EP2, 0x3E, 0x3E, {0x06}, {0x06}, {0x06}, {0x06}, "SINOW_BERILL", "Sinow Berill", nullptr},
{EnemyType::SINOW_GOLD, EP1, 0x1B, 0x1B, {0x13}, {0x47}, {0x13}, {0x10}, "SINOW_GOLD", "Sinow Gold", "Sinow Red"},
{EnemyType::SINOW_SPIGELL, EP2, 0x3F, 0x3F, {0x13}, {0x47}, {0x13}, {0x10}, "SINOW_SPIGELL", "Sinow Spigell", nullptr},
{EnemyType::SINOW_ZELE, EP2, 0x46, 0x46, {0x44}, {0x44}, {0x44}, {0x44}, "SINOW_ZELE", "Sinow Zele", nullptr},
{EnemyType::SINOW_ZOA, EP2, 0x45, 0x45, {0x43}, {0x43}, {0x43}, {0x43}, "SINOW_ZOA", "Sinow Zoa", nullptr},
{EnemyType::SO_DIMENIAN, EP1 | EP2, 0x2B, 0x2B, {0x55}, {0x5C}, {0x54}, {0x54}, "SO_DIMENIAN", "So Dimenian", "Del-D"},
{EnemyType::UL_GIBBON, EP2, 0x3B, 0x3B, {0x3B}, {0x3B}, {0x3B}, {0x3B}, "UL_GIBBON", "Ul Gibbon", nullptr},
{EnemyType::UL_RAY, EP2, 0x4B, NONE, {0x09}, {0x09}, {0x09}, {0x09}, "UL_RAY", "Ul Ray", nullptr},
{EnemyType::VOL_OPT_1, EP1 | BOSS, NONE, NONE, {0x21}, {0x21}, {0x21}, {0x21, 0x22, 0x23}, "VOL_OPT_1", "Vol Opt (phase 1)", "Vol Opt ver.2 (phase 1)"},
{EnemyType::VOL_OPT_2, EP1 | BOSS, 0x2E, 0x2E, {0x25}, {0x25}, {0x25}, {0x25, 0x26, 0x28, 0x29, 0x2A}, "VOL_OPT_2", "Vol Opt (phase 2)", "Vol Opt ver.2 (phase 2)"},
{EnemyType::VOL_OPT_AMP, EP1 | BOSS, NONE, NONE, {0x24}, {0x24}, {0x24}, {}, "VOL_OPT_AMP", "Vol Opt (amp)", "Vol Opt ver.2 (amp)"},
{EnemyType::VOL_OPT_CORE, EP1 | BOSS, NONE, NONE, {0x26}, {0x26}, {0x26}, {}, "VOL_OPT_CORE", "Vol Opt (core)", "Vol Opt ver.2 (core)"},
{EnemyType::VOL_OPT_MONITOR, EP1 | BOSS, NONE, NONE, {0x23}, {0x23}, {0x23}, {}, "VOL_OPT_MONITOR", "Vol Opt (monitor)", "Vol Opt ver.2 (monitor)"},
{EnemyType::VOL_OPT_PILLAR, EP1 | BOSS, NONE, NONE, {0x22}, {0x22}, {0x22}, {}, "VOL_OPT_PILLAR", "Vol Opt (pillar)", "Vol Opt ver.2 (pillar)"},
{EnemyType::YOWIE_CRATER, EP4, 0x59, 0x42, {0x0E}, {0x0E}, {0x0E}, {0x0E}, "YOWIE_CRATER", "Yowie (crater)", nullptr},
{EnemyType::YOWIE_DESERT, EP4, 0x59, 0x43, {0x1E}, {0x1E}, {0x1E}, {0x1E}, "YOWIE_DESERT", "Yowie (desert)", nullptr},
{EnemyType::ZE_BOOTA, EP4, 0x61, 0x4E, {0x01}, {0x01, 0x02, 0x04}, {0x01}, {0x01}, "ZE_BOOTA", "Ze Boota", nullptr},
{EnemyType::ZOL_GIBBON, EP2, 0x3C, 0x3C, {0x3C}, {0x3C}, {0x3C}, {0x3C}, "ZOL_GIBBON", "Zol Gibbon", nullptr},
{EnemyType::ZU_CRATER, EP4, 0x5E, 0x49, {0x07}, {0x07}, {0x07}, {0x07}, "ZU_CRATER", "Zu (crater)", nullptr},
{EnemyType::ZU_DESERT, EP4, 0x5E, 0x4A, {0x1B}, {0x1B}, {0x1B}, {0x1B}, "ZU_DESERT", "Zu (desert)", nullptr},
// clang-format on
};
@@ -201,26 +202,85 @@ const vector<EnemyType>& enemy_types_for_rare_table_index(Episode episode, uint8
}
}
const vector<EnemyType>& enemy_types_for_battle_param_stats_index(Episode episode, uint8_t bp_index) {
static array<vector<vector<EnemyType>>, 5> data;
auto& ret = data.at(static_cast<size_t>(episode));
if (ret.empty()) {
struct BPIndexCacheEntry {
std::set<EnemyType> stats;
std::set<EnemyType> attack_data;
std::set<EnemyType> resist_data;
std::set<EnemyType> movement_data;
};
static const BPIndexCacheEntry& get_bp_index_cache_entry(Episode episode, uint8_t bp_index) {
static bool cache_populated = false;
static array<vector<BPIndexCacheEntry>, 5> data;
if (!cache_populated) {
cache_populated = true;
for (const auto& def : type_defs) {
if (def.valid_in_episode(episode) && !def.bp_stats_indexes.empty()) {
// Some enemies (e.g. Ep4 bosses) have multiple stats structures; we use the last one, since that's the only
// one used when giving EXP
size_t bp_index = def.bp_stats_indexes.back();
if (bp_index >= ret.size()) {
ret.resize(bp_index + 1);
for (const auto& episode : ALL_EPISODES_V4) {
if (!def.valid_in_episode(episode)) {
continue;
}
auto& ep_index = data[static_cast<size_t>(episode)];
for (const auto& bp_index : def.bp_stats_indexes) {
if (bp_index >= ep_index.size()) {
ep_index.resize(bp_index + 1);
}
ep_index[bp_index].stats.emplace(def.type);
}
for (const auto& bp_index : def.bp_attack_data_indexes) {
if (bp_index >= ep_index.size()) {
ep_index.resize(bp_index + 1);
}
ep_index[bp_index].attack_data.emplace(def.type);
}
for (const auto& bp_index : def.bp_resist_data_indexes) {
if (bp_index >= ep_index.size()) {
ep_index.resize(bp_index + 1);
}
ep_index[bp_index].resist_data.emplace(def.type);
}
for (const auto& bp_index : def.bp_movement_data_indexes) {
if (bp_index >= ep_index.size()) {
ep_index.resize(bp_index + 1);
}
ep_index[bp_index].movement_data.emplace(def.type);
}
ret[bp_index].emplace_back(def.type);
}
}
}
auto& ep_index = data.at(static_cast<size_t>(episode));
return ep_index.at(bp_index);
}
static const std::set<EnemyType> empty_vec;
const std::set<EnemyType>& enemy_types_for_battle_param_stats_index(Episode episode, uint8_t bp_index) {
try {
return ret.at(bp_index);
return get_bp_index_cache_entry(episode, bp_index).stats;
} catch (const out_of_range&) {
return empty_vec;
}
}
const std::set<EnemyType>& enemy_types_for_battle_param_attack_data_index(Episode episode, uint8_t bp_index) {
try {
return get_bp_index_cache_entry(episode, bp_index).attack_data;
} catch (const out_of_range&) {
return empty_vec;
}
}
const std::set<EnemyType>& enemy_types_for_battle_param_resist_data_index(Episode episode, uint8_t bp_index) {
try {
return get_bp_index_cache_entry(episode, bp_index).resist_data;
} catch (const out_of_range&) {
return empty_vec;
}
}
const std::set<EnemyType>& enemy_types_for_battle_param_movement_data_index(Episode episode, uint8_t bp_index) {
try {
return get_bp_index_cache_entry(episode, bp_index).movement_data;
} catch (const out_of_range&) {
static const vector<EnemyType> empty_vec;
return empty_vec;
}
}
+6 -1
View File
@@ -3,6 +3,7 @@
#include <inttypes.h>
#include <phosg/Tools.hh>
#include <set>
#include "StaticGameData.hh"
#include "Types.hh"
@@ -165,6 +166,7 @@ struct EnemyTypeDefinition {
std::vector<uint8_t> bp_stats_indexes;
std::vector<uint8_t> bp_attack_data_indexes;
std::vector<uint8_t> bp_resist_data_indexes;
std::vector<uint8_t> bp_movement_data_indexes;
// Note: movement data isn't bound as strongly to the enemy types; some enemies use many entries and some use none at
// all, so we don't list them here. See notes/movement-data.txt for a listing of which enemies use which entries.
const char* enum_name;
@@ -200,4 +202,7 @@ template <>
EnemyType phosg::enum_for_name<EnemyType>(const char* name);
const std::vector<EnemyType>& enemy_types_for_rare_table_index(Episode episode, uint8_t rt_index);
const std::vector<EnemyType>& enemy_types_for_battle_param_stats_index(Episode episode, uint8_t bp_index);
const std::set<EnemyType>& enemy_types_for_battle_param_stats_index(Episode episode, uint8_t bp_index);
const std::set<EnemyType>& enemy_types_for_battle_param_attack_data_index(Episode episode, uint8_t bp_index);
const std::set<EnemyType>& enemy_types_for_battle_param_resist_data_index(Episode episode, uint8_t bp_index);
const std::set<EnemyType>& enemy_types_for_battle_param_movement_data_index(Episode episode, uint8_t bp_index);
+1 -1
View File
@@ -323,7 +323,7 @@ void PlayerState::apply_assist_card_effect_on_set(shared_ptr<PlayerState> setter
if (!s->options.is_nte()) {
s->team_num_cards_destroyed[this->team_id] = 0;
for (size_t client_id = 0; client_id < 4; client_id++) {
const auto other_ps = s->get_player_state(client_id);
auto other_ps = s->get_player_state(client_id);
if (other_ps && (this->team_id == other_ps->get_team_id())) {
auto card = other_ps->get_sc_card();
if (card) {
+32 -1
View File
@@ -2473,7 +2473,38 @@ Action a_encode_level_table(
write_output_data(args, data.data(), data.size(), nullptr);
});
Action a_find_rel_sectionr(
Action a_decode_battle_params(
"decode-battle-params", nullptr,
+[](phosg::Arguments& args) {
auto data_on_ep1 = std::make_shared<std::string>(phosg::load_file(args.get<std::string>(1)));
auto data_on_ep2 = std::make_shared<std::string>(phosg::load_file(args.get<std::string>(2)));
auto data_on_ep4 = std::make_shared<std::string>(phosg::load_file(args.get<std::string>(3)));
auto data_off_ep1 = std::make_shared<std::string>(phosg::load_file(args.get<std::string>(4)));
auto data_off_ep2 = std::make_shared<std::string>(phosg::load_file(args.get<std::string>(5)));
auto data_off_ep4 = std::make_shared<std::string>(phosg::load_file(args.get<std::string>(6)));
BinaryBattleParamsIndex index(data_on_ep1, data_on_ep2, data_on_ep4, data_off_ep1, data_off_ep2, data_off_ep4);
auto json = index.json();
uint32_t serialize_options = phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS;
if (args.get<bool>("hex")) {
serialize_options |= phosg::JSON::SerializeOption::HEX_INTEGERS;
}
phosg::save_file(args.get<std::string>(7), json.serialize(serialize_options));
});
Action a_encode_battle_params(
"encode-battle-params", nullptr,
+[](phosg::Arguments& args) {
JSONBattleParamsIndex index(phosg::JSON::parse(read_input_data(args)));
std::string pfx = args.get<string>(2);
phosg::save_file(pfx + "_on.dat", &index.get_table(false, Episode::EP1), sizeof(BattleParamsIndex::Table));
phosg::save_file(pfx + "_lab_on.dat", &index.get_table(false, Episode::EP2), sizeof(BattleParamsIndex::Table));
phosg::save_file(pfx + "_ep4_on.dat", &index.get_table(false, Episode::EP4), sizeof(BattleParamsIndex::Table));
phosg::save_file(pfx + ".dat", &index.get_table(true, Episode::EP1), sizeof(BattleParamsIndex::Table));
phosg::save_file(pfx + "_lab.dat", &index.get_table(true, Episode::EP2), sizeof(BattleParamsIndex::Table));
phosg::save_file(pfx + "_ep4.dat", &index.get_table(true, Episode::EP4), sizeof(BattleParamsIndex::Table));
});
Action a_find_rel_section(
"find-rel-sections", nullptr,
+[](phosg::Arguments& args) {
auto data = read_input_data(args);
+69 -46
View File
@@ -1876,21 +1876,30 @@ void ServerState::load_set_data_tables() {
}
void ServerState::load_battle_params() {
config_log.info_f("Loading battle parameters");
this->battle_params = make_shared<BattleParamsIndex>(
this->load_bb_file("BattleParamEntry_on.dat"),
this->load_bb_file("BattleParamEntry_lab_on.dat"),
this->load_bb_file("BattleParamEntry_ep4_on.dat"),
this->load_bb_file("BattleParamEntry.dat"),
this->load_bb_file("BattleParamEntry_lab.dat"),
this->load_bb_file("BattleParamEntry_ep4.dat"));
config_log.info_f("Loading JSON battle parameters");
try {
this->battle_params = make_shared<JSONBattleParamsIndex>(phosg::JSON::parse(phosg::load_file(
"system/tables/battle-params.json")));
} catch (const std::exception& e) {
config_log.info_f("Cannot load JSON battle parameters ({}); loading binary battle parameters", e.what());
this->battle_params = make_shared<BinaryBattleParamsIndex>(
this->load_bb_file("BattleParamEntry_on.dat"),
this->load_bb_file("BattleParamEntry_lab_on.dat"),
this->load_bb_file("BattleParamEntry_ep4_on.dat"),
this->load_bb_file("BattleParamEntry.dat"),
this->load_bb_file("BattleParamEntry_lab.dat"),
this->load_bb_file("BattleParamEntry_ep4.dat"));
}
}
void ServerState::load_level_tables() {
config_log.info_f("Loading level tables");
this->level_table_v1_v2 = make_shared<JSONLevelTable>(phosg::JSON::parse(phosg::load_file("system/level-tables/level-table-v1-v2.json")));
this->level_table_v3 = make_shared<JSONLevelTable>(phosg::JSON::parse(phosg::load_file("system/level-tables/level-table-v3.json")));
this->level_table_v4 = make_shared<JSONLevelTable>(phosg::JSON::parse(phosg::load_file("system/level-tables/level-table-v4.json")));
this->level_table_v1_v2 = make_shared<JSONLevelTable>(phosg::JSON::parse(phosg::load_file(
"system/tables/level-table-v1-v2.json")));
this->level_table_v3 = make_shared<JSONLevelTable>(phosg::JSON::parse(phosg::load_file(
"system/tables/level-table-v3.json")));
this->level_table_v4 = make_shared<JSONLevelTable>(phosg::JSON::parse(phosg::load_file(
"system/tables/level-table-v4.json")));
}
void ServerState::load_text_index() {
@@ -2016,11 +2025,11 @@ void ServerState::load_drop_tables() {
unordered_map<string, shared_ptr<const RareItemSet>> new_rare_item_sets;
unordered_map<string, shared_ptr<const CommonItemSet>> new_common_item_sets;
for (const auto& item : std::filesystem::directory_iterator("system/item-tables")) {
for (const auto& item : std::filesystem::directory_iterator("system/tables")) {
string filename = item.path().filename().string();
if (filename.starts_with("common-table-") || filename.starts_with("ItemPT-")) {
string path = "system/item-tables/" + filename;
string path = "system/tables/" + filename;
size_t ext_offset = filename.rfind('.');
string basename = (ext_offset == string::npos) ? filename : filename.substr(0, ext_offset);
@@ -2039,7 +2048,7 @@ void ServerState::load_drop_tables() {
auto data = make_shared<string>(phosg::load_file(path));
shared_ptr<string> ct_data;
try {
string ct_path = "system/item-tables/" + ct_filename;
string ct_path = "system/tables/" + ct_filename;
ct_data = make_shared<string>(phosg::load_file(ct_path));
config_log.info_f("Loading AFS common item table {} with challenge table {}", filename, ct_filename);
} catch (const phosg::cannot_open_file&) {
@@ -2059,7 +2068,7 @@ void ServerState::load_drop_tables() {
}
} else if (filename.starts_with("rare-table-") || filename.starts_with("ItemRT-")) {
string path = "system/item-tables/" + filename;
string path = "system/tables/" + filename;
size_t ext_offset = filename.rfind('.');
string basename = (ext_offset == string::npos) ? filename : filename.substr(0, ext_offset);
@@ -2108,20 +2117,20 @@ void ServerState::load_drop_tables() {
}
config_log.info_f("Loading armor table");
auto armor_data = make_shared<string>(phosg::load_file("system/item-tables/ArmorRandom-gc-v3.rel"));
auto armor_data = make_shared<string>(phosg::load_file("system/tables/ArmorRandom-gc-v3.rel"));
auto new_armor_random_set = make_shared<ArmorRandomSet>(armor_data);
config_log.info_f("Loading tool table");
auto tool_data = make_shared<string>(phosg::load_file("system/item-tables/ToolRandom-gc-v3.rel"));
auto tool_data = make_shared<string>(phosg::load_file("system/tables/ToolRandom-gc-v3.rel"));
auto new_tool_random_set = make_shared<ToolRandomSet>(tool_data);
config_log.info_f("Loading weapon tables");
array<shared_ptr<const WeaponRandomSet>, 4> new_weapon_random_sets;
const char* filenames[4] = {
"system/item-tables/WeaponRandomNormal-gc-v3.rel",
"system/item-tables/WeaponRandomHard-gc-v3.rel",
"system/item-tables/WeaponRandomVeryHard-gc-v3.rel",
"system/item-tables/WeaponRandomUltimate-gc-v3.rel",
"system/tables/WeaponRandomNormal-gc-v3.rel",
"system/tables/WeaponRandomHard-gc-v3.rel",
"system/tables/WeaponRandomVeryHard-gc-v3.rel",
"system/tables/WeaponRandomUltimate-gc-v3.rel",
};
for (size_t z = 0; z < 4; z++) {
auto weapon_data = make_shared<string>(phosg::load_file(filenames[z]));
@@ -2129,7 +2138,7 @@ void ServerState::load_drop_tables() {
}
config_log.info_f("Loading tekker adjustment table");
auto tekker_data = make_shared<string>(phosg::load_file("system/item-tables/JudgeItem-gc-v3.rel"));
auto tekker_data = make_shared<string>(phosg::load_file("system/tables/JudgeItem-gc-v3.rel"));
auto new_tekker_adjustment_set = make_shared<TekkerAdjustmentSet>(tekker_data);
this->rare_item_sets = std::move(new_rare_item_sets);
@@ -2145,25 +2154,32 @@ void ServerState::load_item_definitions() {
config_log.info_f("Loading item definition tables");
for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) {
Version v = static_cast<Version>(v_s);
string path = std::format("system/item-tables/item-parameter-table-{}.json", file_path_token_for_version(v));
config_log.debug_f("Loading item definition table {}", path);
new_item_parameter_tables[v_s] = ItemParameterTable::from_json(phosg::JSON::parse(phosg::load_file(path)));
string json_path = std::format("system/tables/item-parameter-table-{}.json", file_path_token_for_version(v));
try {
config_log.debug_f("Loading item definition table {}", json_path);
new_item_parameter_tables[v_s] = ItemParameterTable::from_json(phosg::JSON::parse(phosg::load_file(json_path)));
} catch (const std::exception& e) {
string path = std::format("system/tables/ItemPMT-{}.prs", file_path_token_for_version(v));
config_log.debug_f("Cannot load {} ({}); loading item definition table {}", json_path, e.what(), path);
auto data = std::make_shared<std::string>(prs_decompress(phosg::load_file(path)));
new_item_parameter_tables[v_s] = ItemParameterTable::from_binary(data, v);
}
}
auto json = phosg::JSON::parse(phosg::load_file("system/item-tables/translation-table.json"));
auto json = phosg::JSON::parse(phosg::load_file("system/tables/translation-table.json"));
auto new_item_translation_table = make_shared<ItemTranslationTable>(json, new_item_parameter_tables);
config_log.info_f("Loading v1 mag evolution table");
auto mag_data_v1 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-dc-v1.prs")));
auto mag_data_v1 = make_shared<string>(prs_decompress(phosg::load_file("system/tables/ItemMagEdit-dc-v1.prs")));
auto new_table_v1 = MagEvolutionTable::create(mag_data_v1, Version::DC_V1);
config_log.info_f("Loading v2 mag evolution table");
auto mag_data_v2 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-dc-v2.prs")));
auto mag_data_v2 = make_shared<string>(prs_decompress(phosg::load_file("system/tables/ItemMagEdit-dc-v2.prs")));
auto new_table_v2 = MagEvolutionTable::create(mag_data_v2, Version::DC_V2);
config_log.info_f("Loading v3 mag evolution table");
auto mag_data_v3 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-xb-v3.prs")));
auto mag_data_v3 = make_shared<string>(prs_decompress(phosg::load_file("system/tables/ItemMagEdit-xb-v3.prs")));
auto new_table_v3 = MagEvolutionTable::create(mag_data_v3, Version::XB_V3);
config_log.info_f("Loading v4 mag evolution table");
auto mag_data_v4 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-bb-v4.prs")));
auto mag_data_v4 = make_shared<string>(prs_decompress(phosg::load_file("system/tables/ItemMagEdit-bb-v4.prs")));
auto new_table_v4 = MagEvolutionTable::create(mag_data_v4, Version::BB_V4);
this->item_parameter_tables = std::move(new_item_parameter_tables);
@@ -2227,31 +2243,38 @@ void ServerState::generate_bb_stream_file() {
config_log.info_f("Generating BB stream file");
auto sf = std::make_shared<BBStreamFile>();
auto add_file = [&](const std::string& filename, std::string&& file_data = "") -> void {
if (file_data.empty()) {
file_data = phosg::load_file("system/blueburst/" + filename);
}
auto add_file = [&](const std::string& filename, const void* data = nullptr, size_t size = 0) -> void {
auto& e = sf->entries.emplace_back();
e.size = file_data.size();
e.checksum = phosg::crc32(file_data.data(), file_data.size());
e.offset = sf->data.size();
e.filename = filename;
sf->data += file_data;
if (!data) {
std::string file_data = phosg::load_file("system/blueburst/" + filename);
e.size = file_data.size();
e.checksum = phosg::crc32(file_data.data(), file_data.size());
sf->data += file_data;
} else {
e.size = size;
e.checksum = phosg::crc32(data, size);
sf->data.append(reinterpret_cast<const char*>(data), size);
}
config_log.debug_f(
"[BBStreamFile] Added file {} at offset {:08X} ({:08X} bytes) with checksum {:08X}; total size is now {:08X}",
filename, e.offset, e.size, e.checksum, sf->data.size());
};
add_file("BattleParamEntry.dat");
add_file("BattleParamEntry_on.dat");
add_file("BattleParamEntry_lab.dat");
add_file("BattleParamEntry_lab_on.dat");
add_file("BattleParamEntry_ep4.dat");
add_file("BattleParamEntry_ep4_on.dat");
add_file("PlyLevelTbl.prs", prs_compress_optimal(this->level_table_v4->serialize_binary_v4()));
auto level_table_data = prs_compress_optimal(this->level_table_v4->serialize_binary_v4());
auto pmt_data = prs_compress_optimal(this->item_parameter_table(Version::BB_V4)->serialize_binary(Version::BB_V4));
const auto& bps = *this->battle_params;
add_file("BattleParamEntry.dat", &bps.get_table(true, Episode::EP1), sizeof(BattleParamsIndex::Table));
add_file("BattleParamEntry_lab.dat", &bps.get_table(true, Episode::EP2), sizeof(BattleParamsIndex::Table));
add_file("BattleParamEntry_ep4.dat", &bps.get_table(true, Episode::EP4), sizeof(BattleParamsIndex::Table));
add_file("BattleParamEntry_on.dat", &bps.get_table(false, Episode::EP1), sizeof(BattleParamsIndex::Table));
add_file("BattleParamEntry_lab_on.dat", &bps.get_table(false, Episode::EP2), sizeof(BattleParamsIndex::Table));
add_file("BattleParamEntry_ep4_on.dat", &bps.get_table(false, Episode::EP4), sizeof(BattleParamsIndex::Table));
add_file("PlyLevelTbl.prs", level_table_data.data(), level_table_data.size());
add_file("ItemMagEdit.prs");
auto pmt = this->item_parameter_table(Version::BB_V4);
add_file("ItemPMT.prs", prs_compress_optimal(pmt->serialize_binary(Version::BB_V4)));
add_file("ItemPMT.prs", pmt_data.data(), pmt_data.size());
this->bb_stream_file = sf;
}