reorganize game tables
This commit is contained in:
@@ -91,7 +91,7 @@ Some of the more likely useful files are:
|
|||||||
* **src/RareItemSet.hh/cc**: Format of ItemRT files (rare item drop tables)
|
* **src/RareItemSet.hh/cc**: Format of ItemRT files (rare item drop tables)
|
||||||
* **src/SaveFileFormats.hh**: Definitions of save file structures for all versions
|
* **src/SaveFileFormats.hh**: Definitions of save file structures for all versions
|
||||||
* **src/Episode3/DataIndexes.hh**: Episode 3 file structures, including card definition format and map/quest format
|
* **src/Episode3/DataIndexes.hh**: Episode 3 file structures, including card definition format and map/quest format
|
||||||
* **system/item-tables/names-v4.json**: Names of all items, indexed by the first 3 bytes of data1
|
* **system/tables/names-v4.json**: Names of all items, indexed by the first 3 bytes of data1
|
||||||
|
|
||||||
## Contributing to newserv
|
## Contributing to newserv
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ In the `private` and `duplicate` modes, there is no incentive to pick up items b
|
|||||||
|
|
||||||
The drop mode can be changed at any time during a game with the `$dropmode` chat command. If the mode is changed after some items have already been dropped, the existing items retain their visibility (that is, items dropped in private mode still can't be picked up by other players since they were dropped before the mode was changed). You can configure which drop modes are used by default, and which modes players are allowed to choose, in config.json. See the comments above the AllowedDropModes and DefaultDropMode keys.
|
The drop mode can be changed at any time during a game with the `$dropmode` chat command. If the mode is changed after some items have already been dropped, the existing items retain their visibility (that is, items dropped in private mode still can't be picked up by other players since they were dropped before the mode was changed). You can configure which drop modes are used by default, and which modes players are allowed to choose, in config.json. See the comments above the AllowedDropModes and DefaultDropMode keys.
|
||||||
|
|
||||||
In the server drop modes, the item tables used to generate common items are in the `system/item-tables/ItemPT-*` files. (The V2 files are used for V1 as well.) The rare item tables are in the `rare-table-*.json` files. Unlike the original formats, it's possible to make each enemy drop multiple different rare items at different rates, though the default tables never do this.
|
In the server drop modes, the item tables used to generate common items are in the `system/tables/common-table-*` files. The rare item tables are in the `rare-table-*.json` files. Unlike the original formats, it's possible to make each enemy drop multiple different rare items at different rates, though the default tables never do this.
|
||||||
|
|
||||||
## Cross-version play
|
## Cross-version play
|
||||||
|
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ MOVEMENT DATA 1E (YOWIE_DESERT)
|
|||||||
fparam1 = TODO: 59NL:005AEBC5; looks like an angle in degrees (range [0, 359])
|
fparam1 = TODO: 59NL:005AEBC5; looks like an angle in degrees (range [0, 359])
|
||||||
fparam2 = TODO: 59NL:005AEBEE
|
fparam2 = TODO: 59NL:005AEBEE
|
||||||
|
|
||||||
MOVEMENT DATA 0D (DARK_BRINGER)
|
MOVEMENT DATA 0D (CHAOS_BRINGER)
|
||||||
fparam1 = TODO: 3OE1:FUN_80097F98; NNF: charge speed
|
fparam1 = TODO: 3OE1:FUN_80097F98; NNF: charge speed
|
||||||
fparam2 = TODO: 3OE1:FUN_800983F8; NNF: movement speed
|
fparam2 = TODO: 3OE1:FUN_800983F8; NNF: movement speed
|
||||||
fparam6 = TODO: 3OE1:80097F3C; NNF: Regular attack cooldown. Delay between going red and shooting.
|
fparam6 = TODO: 3OE1:80097F3C; NNF: Regular attack cooldown. Delay between going red and shooting.
|
||||||
|
|||||||
+223
-11
@@ -9,6 +9,191 @@
|
|||||||
|
|
||||||
using namespace std;
|
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 {
|
void BattleParamsIndex::Table::print(FILE* stream, Episode episode) const {
|
||||||
phosg::fwrite_fmt(stream, "========== STATS\n");
|
phosg::fwrite_fmt(stream, "========== STATS\n");
|
||||||
for (Difficulty difficulty : ALL_DIFFICULTIES_V234) {
|
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_ep1,
|
||||||
shared_ptr<const string> data_on_ep2,
|
shared_ptr<const string> data_on_ep2,
|
||||||
shared_ptr<const string> data_on_ep4,
|
shared_ptr<const string> data_on_ep4,
|
||||||
@@ -103,21 +321,15 @@ BattleParamsIndex::BattleParamsIndex(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const BattleParamsIndex::Table& BattleParamsIndex::get_table(bool solo, Episode episode) const {
|
const BattleParamsIndex::Table& BinaryBattleParamsIndex::get_table(bool solo, Episode episode) const {
|
||||||
uint8_t ep_index;
|
|
||||||
switch (episode) {
|
switch (episode) {
|
||||||
case Episode::EP1:
|
case Episode::EP1:
|
||||||
ep_index = 0;
|
return *this->files[!!solo][0].table;
|
||||||
break;
|
|
||||||
case Episode::EP2:
|
case Episode::EP2:
|
||||||
ep_index = 1;
|
return *this->files[!!solo][1].table;
|
||||||
break;
|
|
||||||
case Episode::EP4:
|
case Episode::EP4:
|
||||||
ep_index = 2;
|
return *this->files[!!solo][2].table;
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
throw invalid_argument("invalid episode");
|
throw invalid_argument("invalid episode");
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this->files[!!solo][ep_index].table;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-10
@@ -35,7 +35,8 @@ public:
|
|||||||
/* 24 */ le_uint32_t unknown_a14;
|
/* 24 */ le_uint32_t unknown_a14;
|
||||||
/* 28 */ le_uint32_t unknown_a15;
|
/* 28 */ le_uint32_t unknown_a15;
|
||||||
/* 2C */ le_uint32_t unknown_a16;
|
/* 2C */ le_uint32_t unknown_a16;
|
||||||
/* 30 */
|
static AttackData from_json(const phosg::JSON& json);
|
||||||
|
phosg::JSON json() const;
|
||||||
} __packed_ws__(AttackData, 0x30);
|
} __packed_ws__(AttackData, 0x30);
|
||||||
|
|
||||||
struct ResistData {
|
struct ResistData {
|
||||||
@@ -50,7 +51,8 @@ public:
|
|||||||
/* 14 */ le_uint32_t unknown_a8;
|
/* 14 */ le_uint32_t unknown_a8;
|
||||||
/* 18 */ le_uint32_t unknown_a9;
|
/* 18 */ le_uint32_t unknown_a9;
|
||||||
/* 1C */ le_int32_t dfp_bonus;
|
/* 1C */ le_int32_t dfp_bonus;
|
||||||
/* 20 */
|
static ResistData from_json(const phosg::JSON& json);
|
||||||
|
phosg::JSON json() const;
|
||||||
} __packed_ws__(ResistData, 0x20);
|
} __packed_ws__(ResistData, 0x20);
|
||||||
|
|
||||||
struct MovementData {
|
struct MovementData {
|
||||||
@@ -66,7 +68,8 @@ public:
|
|||||||
/* 24 */ le_uint32_t iparam4;
|
/* 24 */ le_uint32_t iparam4;
|
||||||
/* 28 */ le_uint32_t iparam5;
|
/* 28 */ le_uint32_t iparam5;
|
||||||
/* 2C */ le_uint32_t iparam6;
|
/* 2C */ le_uint32_t iparam6;
|
||||||
/* 30 */
|
static MovementData from_json(const phosg::JSON& json);
|
||||||
|
phosg::JSON json() const;
|
||||||
} __packed_ws__(MovementData, 0x30);
|
} __packed_ws__(MovementData, 0x30);
|
||||||
|
|
||||||
struct Table {
|
struct Table {
|
||||||
@@ -76,23 +79,48 @@ public:
|
|||||||
/* AE00 */ parray<parray<MovementData, 0x60>, 4> movement_data; // [difficulty][bp_index]
|
/* AE00 */ parray<parray<MovementData, 0x60>, 4> movement_data; // [difficulty][bp_index]
|
||||||
/* F600 */
|
/* 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);
|
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);
|
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);
|
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);
|
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;
|
void print(FILE* stream, Episode episode) const;
|
||||||
} __packed_ws__(Table, 0xF600);
|
} __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_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_ep2, // BattleParamEntry_lab_on.dat
|
||||||
std::shared_ptr<const std::string> data_on_ep4, // BattleParamEntry_ep4_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_ep2, // BattleParamEntry_lab.dat
|
||||||
std::shared_ptr<const std::string> data_off_ep4); // BattleParamEntry_ep4.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 {
|
struct File {
|
||||||
std::shared_ptr<const std::string> data;
|
std::shared_ptr<const std::string> data;
|
||||||
const Table* table = nullptr;
|
const Table* table = nullptr;
|
||||||
|
|||||||
+207
-147
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <phosg/Filesystem.hh>
|
#include <phosg/Filesystem.hh>
|
||||||
#include <phosg/Strings.hh>
|
#include <phosg/Strings.hh>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include "Loggers.hh"
|
#include "Loggers.hh"
|
||||||
#include "PSOEncryption.hh"
|
#include "PSOEncryption.hh"
|
||||||
@@ -18,140 +19,140 @@ static constexpr uint8_t BOSS = EnemyTypeDefinition::Flag::IS_BOSS;
|
|||||||
static constexpr uint8_t NONE = 0xFF;
|
static constexpr uint8_t NONE = 0xFF;
|
||||||
static const vector<EnemyTypeDefinition> type_defs{
|
static const vector<EnemyTypeDefinition> type_defs{
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// TYPE FLAGS RT OLDRT BP-STATS BP-ATTACK BP-RESIST ENUM NAME IN-GAME NAME ULTIMATE NAME
|
// 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::UNKNOWN, 0, NONE, NONE, {}, {}, {}, {}, "UNKNOWN", "__UNKNOWN__", nullptr},
|
||||||
{EnemyType::NONE, 0, NONE, NONE, {}, {}, {}, "NONE", "__NONE__", nullptr},
|
{EnemyType::NONE, 0, NONE, NONE, {}, {}, {}, {}, "NONE", "__NONE__", nullptr},
|
||||||
{EnemyType::NON_ENEMY_NPC, EP1 | EP2 | EP4, NONE, NONE, {}, {}, {}, "NON_ENEMY_NPC", "__NPC__", 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::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}, "ASTARK", "Astark", nullptr},
|
{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}, "BA_BOOTA", "Ba Boota", 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}, "BARBA_RAY", "Barba Ray", 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::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::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}, "BEE_L", "Bee L", "Gee L"},
|
{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}, "BEE_R", "Bee R", "Gee R"},
|
{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}, "BOOMA", "Booma", "Bartle"},
|
{EnemyType::BOOMA, EP1, 0x09, 0x09, {0x4B}, {0x4E}, {0x4A}, {0x4A}, "BOOMA", "Booma", "Bartle"},
|
||||||
{EnemyType::BOOTA, EP4, 0x60, 0x4D, {0x00}, {0x00, 0x02, 0x04}, {0x00}, "BOOTA", "Boota", nullptr},
|
{EnemyType::BOOTA, EP4, 0x60, 0x4D, {0x00}, {0x00, 0x02, 0x04}, {0x00}, {0x00}, "BOOTA", "Boota", nullptr},
|
||||||
{EnemyType::BULCLAW, EP1, 0x28, 0x28, {0x1F}, {0x1F}, {0x1F}, "BULCLAW", "Bulclaw", nullptr},
|
{EnemyType::BULCLAW, EP1, 0x28, 0x28, {0x1F}, {0x1F}, {0x1F}, {0x1F, 0x20}, "BULCLAW", "Bulclaw", nullptr},
|
||||||
{EnemyType::BULK, EP1, 0x27, 0x27, {0x1F}, {0x1F}, {0x1F}, "BULK", "Bulk", nullptr},
|
{EnemyType::BULK, EP1, 0x27, 0x27, {0x1F}, {0x1F}, {0x1F}, {0x1F, 0x20}, "BULK", "Bulk", nullptr},
|
||||||
{EnemyType::CANADINE, EP1, 0x1C, 0x1C, {0x07}, {0x07}, {0x07}, "CANADINE", "Canadine", "Canabin"},
|
{EnemyType::CANADINE, EP1, 0x1C, 0x1C, {0x07}, {0x07}, {0x07}, {0x07}, "CANADINE", "Canadine", "Canabin"},
|
||||||
{EnemyType::CANADINE_GROUP, EP1, 0x1C, 0x1C, {0x08}, {0x08}, {0x08}, "CANADINE_GROUP", "Canadine (group)", "Canabin (group)"},
|
{EnemyType::CANADINE_GROUP, EP1, 0x1C, 0x1C, {0x08}, {0x08}, {0x08}, {0x08}, "CANADINE_GROUP", "Canadine (group)", "Canabin (group)"},
|
||||||
{EnemyType::CANANE, EP1, 0x1D, 0x1D, {0x09}, {0x09}, {0x09}, "CANANE", "Canane", "Canune"},
|
{EnemyType::CANANE, EP1, 0x1D, 0x1D, {0x09}, {0x09}, {0x09}, {0x09}, "CANANE", "Canane", "Canune"},
|
||||||
{EnemyType::CHAOS_BRINGER, EP1, 0x24, 0x24, {0x0D}, {0x0D}, {0x0D}, "CHAOS_BRINGER", "Chaos Bringer", "Dark Bringer"},
|
{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::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::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_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}, "DARK_FALZ_1", "Dark Falz (phase 1)", nullptr},
|
{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}, "DARK_FALZ_2", "Dark Falz (phase 2)", 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}, "DARK_FALZ_3", "Dark Falz (phase 3)", 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}, "DARK_GUNNER", "Dark Gunner", 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::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::DARVANT, EP1, NONE, NONE, {0x35}, {0x35}, {0x35}, {0x35, 0x39}, "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, 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}, "DE_ROL_LE_BODY", "De Rol Le (body)", "Dal Ral Lie (body)"},
|
{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}, "DE_ROL_LE_MINE", "De Rol Le (mine)", "Dal Ral Lie (mine)"},
|
{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}, "DEATH_GUNNER", "Death Gunner", nullptr},
|
{EnemyType::DEATH_GUNNER, EP1, 0x23, 0x23, {0x1E}, {0x1E}, {0x1E}, {0x1E}, "DEATH_GUNNER", "Death Gunner", nullptr},
|
||||||
{EnemyType::DEL_LILY, EP2, 0x53, 0x53, {0x25}, {0x25}, {0x25}, "DEL_LILY", "Del Lily", 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}, "DEL_RAPPY_CRATER", "Del Rappy (crater)", 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}, "DEL_RAPPY_DESERT", "Del Rappy (desert)", 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}, "DELBITER", "Delbiter", nullptr},
|
{EnemyType::DELBITER, EP2, 0x48, 0x48, {0x0D}, {0x0D}, {0x0D}, {0x0D}, "DELBITER", "Delbiter", nullptr},
|
||||||
{EnemyType::DELDEPTH, EP2, 0x47, 0x47, {0x30}, {0x30}, {0x30}, "DELDEPTH", "Deldepth", nullptr},
|
{EnemyType::DELDEPTH, EP2, 0x47, 0x47, {0x30}, {0x30}, {0x30}, {0x30}, "DELDEPTH", "Deldepth", nullptr},
|
||||||
{EnemyType::DELSABER, EP1 | EP2, 0x1E, 0x1E, {0x52}, {0x57, 0x58, 0x59}, {0x51}, "DELSABER", "Delsaber", 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}, "DIMENIAN", "Dimenian", "Arlan"},
|
{EnemyType::DIMENIAN, EP1 | EP2, 0x29, 0x29, {0x53}, {0x5A}, {0x52}, {0x52}, "DIMENIAN", "Dimenian", "Arlan"},
|
||||||
{EnemyType::DOLMDARL, EP2, 0x41, 0x41, {0x50}, {0x55}, {0x4F}, "DOLMDARL", "Dolmdarl", nullptr},
|
{EnemyType::DOLMDARL, EP2, 0x41, 0x41, {0x50}, {0x55}, {0x4F}, {0x4F}, "DOLMDARL", "Dolmdarl", nullptr},
|
||||||
{EnemyType::DOLMOLM, EP2, 0x40, 0x40, {0x4F}, {0x54}, {0x4E}, "DOLMOLM", "Dolmolm", nullptr},
|
{EnemyType::DOLMOLM, EP2, 0x40, 0x40, {0x4F}, {0x54}, {0x4E}, {0x4E}, "DOLMOLM", "Dolmolm", nullptr},
|
||||||
{EnemyType::DORPHON, EP4, 0x63, 0x50, {0x0F}, {0x0F}, {0x0F}, "DORPHON", "Dorphon", nullptr},
|
{EnemyType::DORPHON, EP4, 0x63, 0x50, {0x0F}, {0x0F}, {0x0F}, {0x0F}, "DORPHON", "Dorphon", nullptr},
|
||||||
{EnemyType::DORPHON_ECLAIR, EP4 | RARE, 0x64, 0x51, {0x10}, {0x10}, {0x10}, "DORPHON_ECLAIR", "Dorphon Eclair", 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}, "DRAGON", "Dragon", "Sil Dragon"},
|
{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}, "DUBCHIC", "Dubchic", "Dubchich"},
|
{EnemyType::DUBCHIC, EP1 | EP2, 0x18, 0x18, {0x1B}, {0x1B}, {0x1B}, {0x1B}, "DUBCHIC", "Dubchic", "Dubchich"},
|
||||||
{EnemyType::DUBWITCH, EP1 | EP2, NONE, NONE, {}, {}, {}, "DUBWITCH", "Dubwitch", "Duvuik"},
|
{EnemyType::DUBWITCH, EP1 | EP2, NONE, NONE, {}, {}, {}, {}, "DUBWITCH", "Dubwitch", "Duvuik"},
|
||||||
{EnemyType::EGG_RAPPY, EP2, 0x51, 0x51, {0x19}, {0x19}, {0x19}, "EGG_RAPPY", "Egg Rappy", nullptr},
|
{EnemyType::EGG_RAPPY, EP2, 0x51, 0x51, {0x19}, {0x19}, {0x19}, {0x19}, "EGG_RAPPY", "Egg Rappy", nullptr},
|
||||||
{EnemyType::EPSIGARD, EP2, NONE, NONE, {0x24}, {0x24}, {0x24}, "EPSIGARD", "Episgard", nullptr},
|
{EnemyType::EPSIGARD, EP2, NONE, NONE, {0x24}, {0x24}, {0x24}, {0x24}, "EPSIGARD", "Episgard", nullptr},
|
||||||
{EnemyType::EPSILON, EP2, 0x54, 0x54, {0x23}, {0x23}, {0x23}, "EPSILON", "Epsilon", nullptr},
|
{EnemyType::EPSILON, EP2, 0x54, 0x54, {0x23}, {0x23}, {0x23}, {0x23}, "EPSILON", "Epsilon", nullptr},
|
||||||
{EnemyType::EVIL_SHARK, EP1, 0x10, 0x10, {0x4F}, {0x54}, {0x4E}, "EVIL_SHARK", "Evil Shark", "Vulmer"},
|
{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::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::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}, "GARANZ", "Garanz", "Baranz"},
|
{EnemyType::GARANZ, EP1 | EP2, 0x19, 0x19, {0x1D}, {0x1D}, {0x1D}, {0x1D}, "GARANZ", "Garanz", "Baranz"},
|
||||||
{EnemyType::GEE, EP2, 0x36, 0x36, {0x07}, {0x07}, {0x07}, "GEE", "Gee", nullptr},
|
{EnemyType::GEE, EP2, 0x36, 0x36, {0x07}, {0x07}, {0x07}, {0x07}, "GEE", "Gee", nullptr},
|
||||||
{EnemyType::GI_GUE, EP2, 0x37, 0x37, {0x1A}, {0x1A}, {0x1A}, "GI_GUE", "Gi Gue", 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}, "GIBBLES", "Gibbles", nullptr},
|
{EnemyType::GIBBLES, EP2, 0x3D, 0x3D, {0x3D}, {0x3D, 0x3E, 0x3F}, {0x3D}, {0x3D}, "GIBBLES", "Gibbles", nullptr},
|
||||||
{EnemyType::GIGOBOOMA, EP1, 0x0B, 0x0B, {0x4D}, {0x50}, {0x4C}, "GIGOBOOMA", "Gigobooma", "Tollaw"},
|
{EnemyType::GIGOBOOMA, EP1, 0x0B, 0x0B, {0x4D}, {0x50}, {0x4C}, {0x4C}, "GIGOBOOMA", "Gigobooma", "Tollaw"},
|
||||||
{EnemyType::GILLCHIC, EP1 | EP2, 0x32, 0x32, {0x1C}, {0x1C}, {0x1C}, "GILLCHIC", "Gillchic", "Gillchich"},
|
{EnemyType::GILLCHIC, EP1 | EP2, 0x32, 0x32, {0x1C}, {0x1C}, {0x1C}, {0x1C}, "GILLCHIC", "Gillchic", "Gillchich"},
|
||||||
{EnemyType::GIRTABLULU, EP4, 0x5D, 0x48, {0x1F}, {0x1F}, {0x1F}, "GIRTABLULU", "Girtablulu", nullptr},
|
{EnemyType::GIRTABLULU, EP4, 0x5D, 0x48, {0x1F}, {0x1F}, {0x1F}, {0x1F}, "GIRTABLULU", "Girtablulu", nullptr},
|
||||||
{EnemyType::GOBOOMA, EP1, 0x0A, 0x0A, {0x4C}, {0x4F}, {0x4B}, "GOBOOMA", "Gobooma", "Barble"},
|
{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}, "GOL_DRAGON", "Gol Dragon", nullptr},
|
{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}, "GORAN", "Goran", nullptr},
|
{EnemyType::GORAN, EP4, 0x65, 0x52, {0x11}, {0x11, 0x14}, {0x11}, {0x11}, "GORAN", "Goran", nullptr},
|
||||||
{EnemyType::GORAN_DETONATOR, EP4, 0x66, 0x53, {0x13}, {0x13, 0x16}, {0x13}, "GORAN_DETONATOR", "Goran Detonator", 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}, "GRASS_ASSASSIN", "Grass Assassin", "Crimson Assassin"},
|
{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}, "GUIL_SHARK", "Guil Shark", "Melqueek"},
|
{EnemyType::GUIL_SHARK, EP1, 0x12, 0x12, {0x51}, {0x56}, {0x50}, {0x50}, "GUIL_SHARK", "Guil Shark", "Melqueek"},
|
||||||
{EnemyType::HALLO_RAPPY, EP2, 0x50, 0x50, {0x19}, {0x19}, {0x19}, "HALLO_RAPPY", "Hallo Rappy", nullptr},
|
{EnemyType::HALLO_RAPPY, EP2, 0x50, 0x50, {0x19}, {0x19}, {0x19}, {0x19}, "HALLO_RAPPY", "Hallo Rappy", nullptr},
|
||||||
{EnemyType::HIDOOM, EP1 | EP2, 0x17, 0x17, {0x32}, {0x32}, {0x32}, "HIDOOM", "Hidoom", 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}, "HILDEBEAR", "Hildebear", "Hildelt"},
|
{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}, "HILDEBLUE", "Hildeblue", "Hildetorr"},
|
{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}, "ILL_GILL", "Ill Gill", nullptr},
|
{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}, "KONDRIEU", "Kondrieu", 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}, "KONDRIEU_SPINNER", "Kondrieu (spinner)", 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}, "LA_DIMENIAN", "La Dimenian", "Merlan"},
|
{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}, "LOVE_RAPPY", "Love Rappy", nullptr},
|
{EnemyType::LOVE_RAPPY, EP2, 0x33, 0x33, {0x19}, {0x19}, {0x19}, {0x19}, "LOVE_RAPPY", "Love Rappy", nullptr},
|
||||||
{EnemyType::MERICARAND, EP2, 0x38, 0x38, {0x3A}, {0x3A}, {0x3A}, "MERICARAND", "Mericarand", nullptr},
|
{EnemyType::MERICARAND, EP2, 0x38, 0x38, {0x3A}, {0x3A}, {0x3A}, {0x3A}, "MERICARAND", "Mericarand", nullptr},
|
||||||
{EnemyType::MERICAROL, EP2, 0x38, 0x38, {0x3A}, {0x3A}, {0x3A}, "MERICAROL", "Mericarol", nullptr},
|
{EnemyType::MERICAROL, EP2, 0x38, 0x38, {0x3A}, {0x3A}, {0x3A}, {0x3A}, "MERICAROL", "Mericarol", nullptr},
|
||||||
{EnemyType::MERICUS, EP2 | RARE, 0x3A, 0x3A, {0x46}, {0x46}, {0x46}, "MERICUS", "Mericus", nullptr},
|
{EnemyType::MERICUS, EP2 | RARE, 0x3A, 0x3A, {0x46}, {0x46}, {0x46}, {0x46}, "MERICUS", "Mericus", nullptr},
|
||||||
{EnemyType::MERIKLE, EP2 | RARE, 0x39, 0x39, {0x45}, {0x45}, {0x45}, "MERIKLE", "Merikle", nullptr},
|
{EnemyType::MERIKLE, EP2 | RARE, 0x39, 0x39, {0x45}, {0x45}, {0x45}, {0x45}, "MERIKLE", "Merikle", nullptr},
|
||||||
{EnemyType::MERILLIA, EP2, 0x34, 0x34, {0x4B}, {0x4E}, {0x4A}, "MERILLIA", "Merillia", nullptr},
|
{EnemyType::MERILLIA, EP2, 0x34, 0x34, {0x4B}, {0x4E}, {0x4A}, {0x4A}, "MERILLIA", "Merillia", nullptr},
|
||||||
{EnemyType::MERILTAS, EP2, 0x35, 0x35, {0x4C}, {0x4F}, {0x4B}, "MERILTAS", "Meriltas", nullptr},
|
{EnemyType::MERILTAS, EP2, 0x35, 0x35, {0x4C}, {0x4F}, {0x4B}, {0x4B}, "MERILTAS", "Meriltas", nullptr},
|
||||||
{EnemyType::MERISSA_A, EP4, 0x5B, 0x46, {0x19}, {0x19}, {0x19}, "MERISSA_A", "Merissa A", 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}, "MERISSA_AA", "Merissa AA", 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}, "MIGIUM", "Migium", nullptr},
|
{EnemyType::MIGIUM, EP1 | EP2, 0x16, 0x16, {0x33}, {0x33}, {0x33}, {0x33}, "MIGIUM", "Migium", nullptr},
|
||||||
{EnemyType::MONEST, EP1 | EP2, 0x04, 0x04, {0x01}, {0x01}, {0x01}, "MONEST", "Monest", "Mothvist"},
|
{EnemyType::MONEST, EP1 | EP2, 0x04, 0x04, {0x01}, {0x01}, {0x01}, {0x01}, "MONEST", "Monest", "Mothvist"},
|
||||||
{EnemyType::MORFOS, EP2, 0x42, 0x42, {0x40}, {0x40, 0x50}, {0x40}, "MORFOS", "Morfos", nullptr},
|
{EnemyType::MORFOS, EP2, 0x42, 0x42, {0x40}, {0x40, 0x50}, {0x40}, {0x40}, "MORFOS", "Morfos", nullptr},
|
||||||
{EnemyType::MOTHMANT, EP1 | EP2, 0x03, 0x03, {0x00}, {0x00}, {0x00}, "MOTHMANT", "Mothmant", "Mothvert"},
|
{EnemyType::MOTHMANT, EP1 | EP2, 0x03, 0x03, {0x00}, {0x00}, {0x00}, {0x00}, "MOTHMANT", "Mothmant", "Mothvert"},
|
||||||
{EnemyType::NANO_DRAGON, EP1, 0x0F, 0x0F, {0x1A}, {0x1A}, {0x1A}, "NANO_DRAGON", "Nano Dragon", nullptr},
|
{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}, "NAR_LILY", "Nar Lily", "Mil Lily"},
|
{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}, "OLGA_FLOW_1", "Olga Flow (phase 1)", nullptr},
|
{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}, "OLGA_FLOW_2", "Olga Flow (phase 2)", 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}, "PAL_SHARK", "Pal Shark", "Govulmer"},
|
{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}, "PAN_ARMS", "Pan Arms", nullptr},
|
{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}, "PAZUZU_CRATER", "Pazuzu (crater)", 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}, "PAZUZU_DESERT", "Pazuzu (desert)", 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}, "PIG_RAY", "Pig Ray", 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}, "POFUILLY_SLIME", "Pofuilly Slime", nullptr},
|
{EnemyType::POFUILLY_SLIME, EP1, 0x13, 0x13, {0x30}, {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}, {0x04}, "POISON_LILY", "Poison Lily", "Ob Lily"},
|
||||||
{EnemyType::POISON_LILY, EP1 | EP2, 0x0D, 0x0D, {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}, "PYRO_GORAN", "Pyro Goran", 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}, "RAG_RAPPY", "Rag Rappy", "El Rappy"},
|
{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}, "RECOBOX", "Recobox", nullptr},
|
{EnemyType::RECOBOX, EP2, 0x43, 0x43, {0x41}, {0x41}, {0x41}, {0x41}, "RECOBOX", "Recobox", nullptr},
|
||||||
{EnemyType::RECON, EP2, 0x44, 0x44, {0x42}, {0x42}, {0x42}, "RECON", "Recon", 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}, "SAINT_MILION", "Saint-Milion", 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}, "SAINT_MILION_SPINNER", "Saint-Milion (spinner)", 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}, "SAINT_RAPPY", "Saint Rappy", 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}, "SAND_RAPPY_CRATER", "Sand Rappy (crater)", 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}, "SAND_RAPPY_DESERT", "Sand Rappy (desert)", 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}, "SATELLITE_LIZARD_CRATER", "Satellite Lizard (crater)", 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}, "SATELLITE_LIZARD_DESERT", "Satellite Lizard (desert)", 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}, "SAVAGE_WOLF", "Savage Wolf", "Gulgus"},
|
{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}, "SHAMBERTIN", "Shambertin", nullptr},
|
{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}, "SHAMBERTIN_SPINNER", "Shambertin (spinner)", 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}, "SINOW_BEAT", "Sinow Beat", "Sinow Blue"},
|
{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}, "SINOW_BERILL", "Sinow Berill", nullptr},
|
{EnemyType::SINOW_BERILL, EP2, 0x3E, 0x3E, {0x06}, {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_GOLD, EP1, 0x1B, 0x1B, {0x13}, {0x47}, {0x13}, {0x10}, "SINOW_GOLD", "Sinow Gold", "Sinow Red"},
|
||||||
{EnemyType::SINOW_SPIGELL, EP2, 0x3F, 0x3F, {0x13}, {0x47}, {0x13}, "SINOW_SPIGELL", "Sinow Spigell", nullptr},
|
{EnemyType::SINOW_SPIGELL, EP2, 0x3F, 0x3F, {0x13}, {0x47}, {0x13}, {0x10}, "SINOW_SPIGELL", "Sinow Spigell", nullptr},
|
||||||
{EnemyType::SINOW_ZELE, EP2, 0x46, 0x46, {0x44}, {0x44}, {0x44}, "SINOW_ZELE", "Sinow Zele", 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}, "SINOW_ZOA", "Sinow Zoa", 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}, "SO_DIMENIAN", "So Dimenian", "Del-D"},
|
{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}, "UL_GIBBON", "Ul Gibbon", nullptr},
|
{EnemyType::UL_GIBBON, EP2, 0x3B, 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::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}, "VOL_OPT_1", "Vol Opt (phase 1)", "Vol Opt ver.2 (phase 1)"},
|
{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}, "VOL_OPT_2", "Vol Opt (phase 2)", "Vol Opt ver.2 (phase 2)"},
|
{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_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_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_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::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_CRATER, EP4, 0x59, 0x42, {0x0E}, {0x0E}, {0x0E}, {0x0E}, "YOWIE_CRATER", "Yowie (crater)", nullptr},
|
||||||
{EnemyType::YOWIE_DESERT, EP4, 0x59, 0x43, {0x1E}, {0x1E}, {0x1E}, "YOWIE_DESERT", "Yowie (desert)", 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}, "ZE_BOOTA", "Ze Boota", 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}, "ZOL_GIBBON", "Zol Gibbon", 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}, "ZU_CRATER", "Zu (crater)", 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}, "ZU_DESERT", "Zu (desert)", nullptr},
|
{EnemyType::ZU_DESERT, EP4, 0x5E, 0x4A, {0x1B}, {0x1B}, {0x1B}, {0x1B}, "ZU_DESERT", "Zu (desert)", nullptr},
|
||||||
// clang-format on
|
// 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) {
|
struct BPIndexCacheEntry {
|
||||||
static array<vector<vector<EnemyType>>, 5> data;
|
std::set<EnemyType> stats;
|
||||||
auto& ret = data.at(static_cast<size_t>(episode));
|
std::set<EnemyType> attack_data;
|
||||||
if (ret.empty()) {
|
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) {
|
for (const auto& def : type_defs) {
|
||||||
if (def.valid_in_episode(episode) && !def.bp_stats_indexes.empty()) {
|
for (const auto& episode : ALL_EPISODES_V4) {
|
||||||
// Some enemies (e.g. Ep4 bosses) have multiple stats structures; we use the last one, since that's the only
|
if (!def.valid_in_episode(episode)) {
|
||||||
// one used when giving EXP
|
continue;
|
||||||
size_t bp_index = def.bp_stats_indexes.back();
|
}
|
||||||
if (bp_index >= ret.size()) {
|
auto& ep_index = data[static_cast<size_t>(episode)];
|
||||||
ret.resize(bp_index + 1);
|
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 {
|
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&) {
|
} catch (const out_of_range&) {
|
||||||
static const vector<EnemyType> empty_vec;
|
|
||||||
return empty_vec;
|
return empty_vec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -3,6 +3,7 @@
|
|||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#include <phosg/Tools.hh>
|
#include <phosg/Tools.hh>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include "StaticGameData.hh"
|
#include "StaticGameData.hh"
|
||||||
#include "Types.hh"
|
#include "Types.hh"
|
||||||
@@ -165,6 +166,7 @@ struct EnemyTypeDefinition {
|
|||||||
std::vector<uint8_t> bp_stats_indexes;
|
std::vector<uint8_t> bp_stats_indexes;
|
||||||
std::vector<uint8_t> bp_attack_data_indexes;
|
std::vector<uint8_t> bp_attack_data_indexes;
|
||||||
std::vector<uint8_t> bp_resist_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
|
// 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.
|
// 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;
|
const char* enum_name;
|
||||||
@@ -200,4 +202,7 @@ template <>
|
|||||||
EnemyType phosg::enum_for_name<EnemyType>(const char* name);
|
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_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);
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ void PlayerState::apply_assist_card_effect_on_set(shared_ptr<PlayerState> setter
|
|||||||
if (!s->options.is_nte()) {
|
if (!s->options.is_nte()) {
|
||||||
s->team_num_cards_destroyed[this->team_id] = 0;
|
s->team_num_cards_destroyed[this->team_id] = 0;
|
||||||
for (size_t client_id = 0; client_id < 4; client_id++) {
|
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())) {
|
if (other_ps && (this->team_id == other_ps->get_team_id())) {
|
||||||
auto card = other_ps->get_sc_card();
|
auto card = other_ps->get_sc_card();
|
||||||
if (card) {
|
if (card) {
|
||||||
|
|||||||
+32
-1
@@ -2473,7 +2473,38 @@ Action a_encode_level_table(
|
|||||||
write_output_data(args, data.data(), data.size(), nullptr);
|
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,
|
"find-rel-sections", nullptr,
|
||||||
+[](phosg::Arguments& args) {
|
+[](phosg::Arguments& args) {
|
||||||
auto data = read_input_data(args);
|
auto data = read_input_data(args);
|
||||||
|
|||||||
+69
-46
@@ -1876,21 +1876,30 @@ void ServerState::load_set_data_tables() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServerState::load_battle_params() {
|
void ServerState::load_battle_params() {
|
||||||
config_log.info_f("Loading battle parameters");
|
config_log.info_f("Loading JSON battle parameters");
|
||||||
this->battle_params = make_shared<BattleParamsIndex>(
|
try {
|
||||||
this->load_bb_file("BattleParamEntry_on.dat"),
|
this->battle_params = make_shared<JSONBattleParamsIndex>(phosg::JSON::parse(phosg::load_file(
|
||||||
this->load_bb_file("BattleParamEntry_lab_on.dat"),
|
"system/tables/battle-params.json")));
|
||||||
this->load_bb_file("BattleParamEntry_ep4_on.dat"),
|
} catch (const std::exception& e) {
|
||||||
this->load_bb_file("BattleParamEntry.dat"),
|
config_log.info_f("Cannot load JSON battle parameters ({}); loading binary battle parameters", e.what());
|
||||||
this->load_bb_file("BattleParamEntry_lab.dat"),
|
this->battle_params = make_shared<BinaryBattleParamsIndex>(
|
||||||
this->load_bb_file("BattleParamEntry_ep4.dat"));
|
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() {
|
void ServerState::load_level_tables() {
|
||||||
config_log.info_f("Loading 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_v1_v2 = make_shared<JSONLevelTable>(phosg::JSON::parse(phosg::load_file(
|
||||||
this->level_table_v3 = make_shared<JSONLevelTable>(phosg::JSON::parse(phosg::load_file("system/level-tables/level-table-v3.json")));
|
"system/tables/level-table-v1-v2.json")));
|
||||||
this->level_table_v4 = make_shared<JSONLevelTable>(phosg::JSON::parse(phosg::load_file("system/level-tables/level-table-v4.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() {
|
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 RareItemSet>> new_rare_item_sets;
|
||||||
unordered_map<string, shared_ptr<const CommonItemSet>> new_common_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();
|
string filename = item.path().filename().string();
|
||||||
|
|
||||||
if (filename.starts_with("common-table-") || filename.starts_with("ItemPT-")) {
|
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('.');
|
size_t ext_offset = filename.rfind('.');
|
||||||
string basename = (ext_offset == string::npos) ? filename : filename.substr(0, ext_offset);
|
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));
|
auto data = make_shared<string>(phosg::load_file(path));
|
||||||
shared_ptr<string> ct_data;
|
shared_ptr<string> ct_data;
|
||||||
try {
|
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));
|
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);
|
config_log.info_f("Loading AFS common item table {} with challenge table {}", filename, ct_filename);
|
||||||
} catch (const phosg::cannot_open_file&) {
|
} 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-")) {
|
} 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('.');
|
size_t ext_offset = filename.rfind('.');
|
||||||
string basename = (ext_offset == string::npos) ? filename : filename.substr(0, ext_offset);
|
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");
|
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);
|
auto new_armor_random_set = make_shared<ArmorRandomSet>(armor_data);
|
||||||
|
|
||||||
config_log.info_f("Loading tool table");
|
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);
|
auto new_tool_random_set = make_shared<ToolRandomSet>(tool_data);
|
||||||
|
|
||||||
config_log.info_f("Loading weapon tables");
|
config_log.info_f("Loading weapon tables");
|
||||||
array<shared_ptr<const WeaponRandomSet>, 4> new_weapon_random_sets;
|
array<shared_ptr<const WeaponRandomSet>, 4> new_weapon_random_sets;
|
||||||
const char* filenames[4] = {
|
const char* filenames[4] = {
|
||||||
"system/item-tables/WeaponRandomNormal-gc-v3.rel",
|
"system/tables/WeaponRandomNormal-gc-v3.rel",
|
||||||
"system/item-tables/WeaponRandomHard-gc-v3.rel",
|
"system/tables/WeaponRandomHard-gc-v3.rel",
|
||||||
"system/item-tables/WeaponRandomVeryHard-gc-v3.rel",
|
"system/tables/WeaponRandomVeryHard-gc-v3.rel",
|
||||||
"system/item-tables/WeaponRandomUltimate-gc-v3.rel",
|
"system/tables/WeaponRandomUltimate-gc-v3.rel",
|
||||||
};
|
};
|
||||||
for (size_t z = 0; z < 4; z++) {
|
for (size_t z = 0; z < 4; z++) {
|
||||||
auto weapon_data = make_shared<string>(phosg::load_file(filenames[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");
|
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);
|
auto new_tekker_adjustment_set = make_shared<TekkerAdjustmentSet>(tekker_data);
|
||||||
|
|
||||||
this->rare_item_sets = std::move(new_rare_item_sets);
|
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");
|
config_log.info_f("Loading item definition tables");
|
||||||
for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) {
|
for (size_t v_s = NUM_PATCH_VERSIONS; v_s < NUM_VERSIONS; v_s++) {
|
||||||
Version v = static_cast<Version>(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));
|
string json_path = std::format("system/tables/item-parameter-table-{}.json", file_path_token_for_version(v));
|
||||||
config_log.debug_f("Loading item definition table {}", path);
|
try {
|
||||||
new_item_parameter_tables[v_s] = ItemParameterTable::from_json(phosg::JSON::parse(phosg::load_file(path)));
|
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);
|
auto new_item_translation_table = make_shared<ItemTranslationTable>(json, new_item_parameter_tables);
|
||||||
|
|
||||||
config_log.info_f("Loading v1 mag evolution table");
|
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);
|
auto new_table_v1 = MagEvolutionTable::create(mag_data_v1, Version::DC_V1);
|
||||||
config_log.info_f("Loading v2 mag evolution table");
|
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);
|
auto new_table_v2 = MagEvolutionTable::create(mag_data_v2, Version::DC_V2);
|
||||||
config_log.info_f("Loading v3 mag evolution table");
|
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);
|
auto new_table_v3 = MagEvolutionTable::create(mag_data_v3, Version::XB_V3);
|
||||||
config_log.info_f("Loading v4 mag evolution table");
|
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);
|
auto new_table_v4 = MagEvolutionTable::create(mag_data_v4, Version::BB_V4);
|
||||||
|
|
||||||
this->item_parameter_tables = std::move(new_item_parameter_tables);
|
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");
|
config_log.info_f("Generating BB stream file");
|
||||||
auto sf = std::make_shared<BBStreamFile>();
|
auto sf = std::make_shared<BBStreamFile>();
|
||||||
|
|
||||||
auto add_file = [&](const std::string& filename, std::string&& file_data = "") -> void {
|
auto add_file = [&](const std::string& filename, const void* data = nullptr, size_t size = 0) -> void {
|
||||||
if (file_data.empty()) {
|
|
||||||
file_data = phosg::load_file("system/blueburst/" + filename);
|
|
||||||
}
|
|
||||||
auto& e = sf->entries.emplace_back();
|
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.offset = sf->data.size();
|
||||||
e.filename = filename;
|
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(
|
config_log.debug_f(
|
||||||
"[BBStreamFile] Added file {} at offset {:08X} ({:08X} bytes) with checksum {:08X}; total size is now {:08X}",
|
"[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());
|
filename, e.offset, e.size, e.checksum, sf->data.size());
|
||||||
};
|
};
|
||||||
|
|
||||||
add_file("BattleParamEntry.dat");
|
auto level_table_data = prs_compress_optimal(this->level_table_v4->serialize_binary_v4());
|
||||||
add_file("BattleParamEntry_on.dat");
|
auto pmt_data = prs_compress_optimal(this->item_parameter_table(Version::BB_V4)->serialize_binary(Version::BB_V4));
|
||||||
add_file("BattleParamEntry_lab.dat");
|
|
||||||
add_file("BattleParamEntry_lab_on.dat");
|
const auto& bps = *this->battle_params;
|
||||||
add_file("BattleParamEntry_ep4.dat");
|
add_file("BattleParamEntry.dat", &bps.get_table(true, Episode::EP1), sizeof(BattleParamsIndex::Table));
|
||||||
add_file("BattleParamEntry_ep4_on.dat");
|
add_file("BattleParamEntry_lab.dat", &bps.get_table(true, Episode::EP2), sizeof(BattleParamsIndex::Table));
|
||||||
add_file("PlyLevelTbl.prs", prs_compress_optimal(this->level_table_v4->serialize_binary_v4()));
|
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");
|
add_file("ItemMagEdit.prs");
|
||||||
auto pmt = this->item_parameter_table(Version::BB_V4);
|
add_file("ItemPMT.prs", pmt_data.data(), pmt_data.size());
|
||||||
add_file("ItemPMT.prs", prs_compress_optimal(pmt->serialize_binary(Version::BB_V4)));
|
|
||||||
|
|
||||||
this->bb_stream_file = sf;
|
this->bb_stream_file = sf;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
../item-tables/ItemMagEdit-bb-v4.prs
|
../tables/ItemMagEdit-bb-v4.prs
|
||||||
@@ -39,16 +39,16 @@
|
|||||||
|
|
||||||
// If this field is specified, it overrides the default common item table for the duration of the quest. The common
|
// If this field is specified, it overrides the default common item table for the duration of the quest. The common
|
||||||
// item table name must begin with either "common-table-" or "ItemPT-", and the corresponding file should be in
|
// item table name must begin with either "common-table-" or "ItemPT-", and the corresponding file should be in
|
||||||
// system/item-tables/ and should be in .json, .afs, .gsl, or .gslb format. The file extension (.json, etc.) should
|
// system/tables/ and should be in .json, .afs, .gsl, or .gslb format. The file extension (.json, etc.) should not be
|
||||||
// not be included here. If you use this, make sure to set AllowedDropModes appropriately below so the CLIENT drop
|
// included here. If you use this, make sure to set AllowedDropModes appropriately below so the CLIENT drop mode is
|
||||||
// mode is not allowed (since the client won't use the custom table).
|
// not allowed (since the client won't use the custom table).
|
||||||
// "CommonItemSetName": "common-table-custom1",
|
// "CommonItemSetName": "common-table-custom1",
|
||||||
|
|
||||||
// If this field is specified, it overrides the default rare item table for the duration of the quest. The rare item
|
// If this field is specified, it overrides the default rare item table for the duration of the quest. The rare item
|
||||||
// table name must begin with either "rare-table-" or "ItemRT-". As for common item sets, the rare table must be in
|
// table name must begin with either "rare-table-" or "ItemRT-". As for common item sets, the rare table must be in
|
||||||
// system/item-tables/. If it's in JSON format, the table name must end with -v1, -v2, -v3, or -v4. If you use this,
|
// system/tables/. If it's in JSON format, the table name must end with -v1, -v2, -v3, or -v4. If you use this, make
|
||||||
// make sure to set AllowedDropModes appropriately below so the CLIENT drop mode is not allowed (since the client
|
// sure to set AllowedDropModes appropriately below so the CLIENT drop mode is not allowed (since the client won't
|
||||||
// won't use the custom table).
|
// use the custom table).
|
||||||
// "RareItemSetName": "rare-table-custom1",
|
// "RareItemSetName": "rare-table-custom1",
|
||||||
|
|
||||||
// If these fields are specified, they override the allowed drop modes and the default drop mode for the game when
|
// If these fields are specified, they override the allowed drop modes and the default drop mode for the game when
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10,6 +10,16 @@ fi
|
|||||||
DIR=tests/game-tables
|
DIR=tests/game-tables
|
||||||
PMT_PREFIX=$DIR/item-parameter-table
|
PMT_PREFIX=$DIR/item-parameter-table
|
||||||
|
|
||||||
|
echo "... (battle-params)"
|
||||||
|
$EXECUTABLE decode-battle-params tests/game-tables/battle-params-ep1-on.dat tests/game-tables/battle-params-ep2-on.dat tests/game-tables/battle-params-ep4-on.dat tests/game-tables/battle-params-ep1-off.dat tests/game-tables/battle-params-ep2-off.dat tests/game-tables/battle-params-ep4-off.dat tests/game-tables/battle-params.json
|
||||||
|
$EXECUTABLE encode-battle-params tests/game-tables/battle-params.json tests/game-tables/battle-params-encoded
|
||||||
|
bindiff tests/game-tables/battle-params-ep1-on.dat tests/game-tables/battle-params-encoded_on.dat
|
||||||
|
bindiff tests/game-tables/battle-params-ep2-on.dat tests/game-tables/battle-params-encoded_lab_on.dat
|
||||||
|
bindiff tests/game-tables/battle-params-ep4-on.dat tests/game-tables/battle-params-encoded_ep4_on.dat
|
||||||
|
bindiff tests/game-tables/battle-params-ep1-off.dat tests/game-tables/battle-params-encoded.dat
|
||||||
|
bindiff tests/game-tables/battle-params-ep2-off.dat tests/game-tables/battle-params-encoded_lab.dat
|
||||||
|
bindiff tests/game-tables/battle-params-ep4-off.dat tests/game-tables/battle-params-encoded_ep4.dat
|
||||||
|
|
||||||
echo "... (level-table) BB"
|
echo "... (level-table) BB"
|
||||||
$EXECUTABLE decode-level-table --bb-v4 $DIR/level-table-bb-v4.expected.bin --decompressed $DIR/level-table-bb-v4.json --hex
|
$EXECUTABLE decode-level-table --bb-v4 $DIR/level-table-bb-v4.expected.bin --decompressed $DIR/level-table-bb-v4.json --hex
|
||||||
$EXECUTABLE encode-level-table-v4 $DIR/level-table-bb-v4.json $DIR/level-table-bb-v4.encoded.bin --decompressed
|
$EXECUTABLE encode-level-table-v4 $DIR/level-table-bb-v4.json $DIR/level-table-bb-v4.encoded.bin --decompressed
|
||||||
@@ -76,4 +86,4 @@ $EXECUTABLE encode-item-parameter-table --bb-v4 $PMT_PREFIX-bb-v4.json $PMT_PREF
|
|||||||
bindiff $PMT_PREFIX-bb-v4.expected.bin $PMT_PREFIX-bb-v4.encoded.bin
|
bindiff $PMT_PREFIX-bb-v4.expected.bin $PMT_PREFIX-bb-v4.encoded.bin
|
||||||
|
|
||||||
echo "... clean up"
|
echo "... clean up"
|
||||||
rm -f $DIR/*.encoded.bin $DIR/*.json
|
rm -f $DIR/*.encoded.bin $DIR/*.json $DIR/battle-params.json.enc* $DIR/battle-params-encoded*
|
||||||
|
|||||||
Reference in New Issue
Block a user