add v4 ItemPT data

This commit is contained in:
Martin Michelsen
2023-12-05 22:57:42 -08:00
parent 4ccbb2f683
commit 0ce5210c22
11 changed files with 462 additions and 497 deletions
+136 -181
View File
@@ -6,176 +6,107 @@
using namespace std;
CommonItemSet::Table::Table(
shared_ptr<const string> owned_data, const StringReader& r, bool is_big_endian, bool is_v3)
: owned_data(owned_data),
r(r),
is_big_endian(is_big_endian),
is_v3(is_v3) {
CommonItemSet::Table::Table(const StringReader& r, bool is_big_endian, bool is_v3) {
if (is_big_endian) {
const auto& be_offsets = r.pget<Offsets<true>>(r.pget_u32b(this->r.size() - 0x10));
this->offsets.base_weapon_type_prob_table_offset = be_offsets.base_weapon_type_prob_table_offset.load();
this->offsets.subtype_base_table_offset = be_offsets.subtype_base_table_offset.load();
this->offsets.subtype_area_length_table_offset = be_offsets.subtype_area_length_table_offset.load();
this->offsets.grind_prob_table_offset = be_offsets.grind_prob_table_offset.load();
this->offsets.armor_shield_type_index_prob_table_offset = be_offsets.armor_shield_type_index_prob_table_offset.load();
this->offsets.armor_slot_count_prob_table_offset = be_offsets.armor_slot_count_prob_table_offset.load();
this->offsets.enemy_meseta_ranges_offset = be_offsets.enemy_meseta_ranges_offset.load();
this->offsets.enemy_type_drop_probs_offset = be_offsets.enemy_type_drop_probs_offset.load();
this->offsets.enemy_item_classes_offset = be_offsets.enemy_item_classes_offset.load();
this->offsets.box_meseta_ranges_offset = be_offsets.box_meseta_ranges_offset.load();
this->offsets.bonus_value_prob_table_offset = be_offsets.bonus_value_prob_table_offset.load();
this->offsets.nonrare_bonus_prob_spec_offset = be_offsets.nonrare_bonus_prob_spec_offset.load();
this->offsets.bonus_type_prob_table_offset = be_offsets.bonus_type_prob_table_offset.load();
this->offsets.special_mult_offset = be_offsets.special_mult_offset.load();
this->offsets.special_percent_offset = be_offsets.special_percent_offset.load();
this->offsets.tool_class_prob_table_offset = be_offsets.tool_class_prob_table_offset.load();
this->offsets.technique_index_prob_table_offset = be_offsets.technique_index_prob_table_offset.load();
this->offsets.technique_level_ranges_offset = be_offsets.technique_level_ranges_offset.load();
this->offsets.armor_or_shield_type_bias = be_offsets.armor_or_shield_type_bias;
this->offsets.unit_max_stars_offset = be_offsets.unit_max_stars_offset.load();
this->offsets.box_item_class_prob_table_offset = be_offsets.box_item_class_prob_table_offset.load();
this->parse_itempt_t<true>(r, is_v3);
} else {
this->offsets = r.pget<Offsets<false>>(r.pget_u32l(this->r.size() - 0x10));
this->parse_itempt_t<false>(r, is_v3);
}
}
const parray<uint8_t, 0x0C>& CommonItemSet::Table::base_weapon_type_prob_table() const {
return this->r.pget<parray<uint8_t, 0x0C>>(this->offsets.base_weapon_type_prob_table_offset);
}
const parray<int8_t, 0x0C>& CommonItemSet::Table::subtype_base_table() const {
return this->r.pget<parray<int8_t, 0x0C>>(this->offsets.subtype_base_table_offset);
}
const parray<uint8_t, 0x0C>& CommonItemSet::Table::subtype_area_length_table() const {
return this->r.pget<parray<uint8_t, 0x0C>>(this->offsets.subtype_area_length_table_offset);
}
const parray<parray<uint8_t, 4>, 9>& CommonItemSet::Table::grind_prob_table() const {
return this->r.pget<parray<parray<uint8_t, 4>, 9>>(this->offsets.grind_prob_table_offset);
}
const parray<uint8_t, 0x05>& CommonItemSet::Table::armor_shield_type_index_prob_table() const {
return this->r.pget<parray<uint8_t, 0x05>>(this->offsets.armor_shield_type_index_prob_table_offset);
}
const parray<uint8_t, 0x05>& CommonItemSet::Table::armor_slot_count_prob_table() const {
return this->r.pget<parray<uint8_t, 0x05>>(this->offsets.armor_slot_count_prob_table_offset);
}
const parray<CommonItemSet::Table::Range<uint16_t>, 0x64>& CommonItemSet::Table::enemy_meseta_ranges() const {
if (!this->parsed_enemy_meseta_ranges_populated) {
if (this->is_big_endian) {
const auto& data = r.pget<parray<Range<be_uint16_t>, 0x64>>(this->offsets.enemy_meseta_ranges_offset);
for (size_t z = 0; z < data.size(); z++) {
this->parsed_enemy_meseta_ranges[z] = Range<uint16_t>{data[z].min, data[z].max};
}
} else {
const auto& data = r.pget<parray<Range<le_uint16_t>, 0x64>>(this->offsets.enemy_meseta_ranges_offset);
for (size_t z = 0; z < data.size(); z++) {
this->parsed_enemy_meseta_ranges[z] = Range<uint16_t>{data[z].min, data[z].max};
template <bool IsBigEndian>
void CommonItemSet::Table::parse_itempt_t(const StringReader& r, bool is_v3) {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
using U32T = typename std::conditional<IsBigEndian, be_uint32_t, le_uint32_t>::type;
const auto& offsets = r.pget<Offsets<IsBigEndian>>(r.pget<U32T>(r.size() - 0x10));
this->base_weapon_type_prob_table = r.pget<parray<uint8_t, 0x0C>>(offsets.base_weapon_type_prob_table_offset);
this->subtype_base_table = r.pget<parray<int8_t, 0x0C>>(offsets.subtype_base_table_offset);
this->subtype_area_length_table = r.pget<parray<uint8_t, 0x0C>>(offsets.subtype_area_length_table_offset);
this->grind_prob_table = r.pget<parray<parray<uint8_t, 4>, 9>>(offsets.grind_prob_table_offset);
this->armor_shield_type_index_prob_table = r.pget<parray<uint8_t, 0x05>>(offsets.armor_shield_type_index_prob_table_offset);
this->armor_slot_count_prob_table = r.pget<parray<uint8_t, 0x05>>(offsets.armor_slot_count_prob_table_offset);
const auto& data = r.pget<parray<Range<U16T>, 0x64>>(offsets.enemy_meseta_ranges_offset);
for (size_t z = 0; z < data.size(); z++) {
this->enemy_meseta_ranges[z] = Range<uint16_t>{data[z].min, data[z].max};
}
this->enemy_type_drop_probs = r.pget<parray<uint8_t, 0x64>>(offsets.enemy_type_drop_probs_offset);
this->enemy_item_classes = r.pget<parray<uint8_t, 0x64>>(offsets.enemy_item_classes_offset);
{
const auto& data = r.pget<parray<Range<U16T>, 0x0A>>(offsets.box_meseta_ranges_offset);
for (size_t z = 0; z < data.size(); z++) {
this->box_meseta_ranges[z] = Range<uint16_t>{data[z].min, data[z].max};
}
}
this->has_rare_bonus_value_prob_table = is_v3;
if (!this->has_rare_bonus_value_prob_table) { // V2
const auto& data = r.pget<parray<parray<uint8_t, 5>, 0x17>>(offsets.bonus_value_prob_table_offset);
for (size_t z = 0; z < data.size(); z++) {
for (size_t x = 0; x < data[z].size(); x++) {
this->bonus_value_prob_table[z][x] = data[z][x];
}
}
this->parsed_enemy_meseta_ranges_populated = true;
}
return this->parsed_enemy_meseta_ranges;
}
const parray<uint8_t, 0x64>& CommonItemSet::Table::enemy_type_drop_probs() const {
return this->r.pget<parray<uint8_t, 0x64>>(this->offsets.enemy_type_drop_probs_offset);
}
const parray<uint8_t, 0x64>& CommonItemSet::Table::enemy_item_classes() const {
return this->r.pget<parray<uint8_t, 0x64>>(this->offsets.enemy_item_classes_offset);
}
const parray<CommonItemSet::Table::Range<uint16_t>, 0x0A>& CommonItemSet::Table::box_meseta_ranges() const {
if (!this->parsed_box_meseta_ranges_populated) {
if (this->is_big_endian) {
const auto& data = r.pget<parray<Range<be_uint16_t>, 0x0A>>(this->offsets.box_meseta_ranges_offset);
for (size_t z = 0; z < data.size(); z++) {
this->parsed_box_meseta_ranges[z] = Range<uint16_t>{data[z].min, data[z].max};
}
} else {
const auto& data = r.pget<parray<Range<le_uint16_t>, 0x0A>>(this->offsets.box_meseta_ranges_offset);
for (size_t z = 0; z < data.size(); z++) {
this->parsed_box_meseta_ranges[z] = Range<uint16_t>{data[z].min, data[z].max};
} else { // V3
const auto& data = r.pget<parray<parray<U16T, 6>, 0x17>>(offsets.bonus_value_prob_table_offset);
for (size_t z = 0; z < data.size(); z++) {
for (size_t x = 0; x < data[z].size(); x++) {
this->bonus_value_prob_table[z][x] = data[z][x];
}
}
this->parsed_box_meseta_ranges_populated = true;
}
return this->parsed_box_meseta_ranges;
}
bool CommonItemSet::Table::has_rare_bonus_value_prob_table() const {
return this->is_v3;
}
const parray<parray<uint16_t, 6>, 0x17>& CommonItemSet::Table::bonus_value_prob_table() const {
if (!this->parsed_bonus_value_prob_table_populated) {
if (!this->is_v3) { // V2
const auto& data = r.pget<parray<parray<uint8_t, 5>, 0x17>>(this->offsets.bonus_value_prob_table_offset);
for (size_t z = 0; z < data.size(); z++) {
for (size_t x = 0; x < data[z].size(); x++) {
this->parsed_bonus_value_prob_table[z][x] = data[z][x];
}
}
} else if (this->is_big_endian) { // BE V3
const auto& data = r.pget<parray<parray<be_uint16_t, 6>, 0x17>>(this->offsets.bonus_value_prob_table_offset);
for (size_t z = 0; z < data.size(); z++) {
for (size_t x = 0; x < data[z].size(); x++) {
this->parsed_bonus_value_prob_table[z][x] = data[z][x];
}
}
} else { // LE V3
const auto& data = r.pget<parray<parray<le_uint16_t, 6>, 0x17>>(this->offsets.bonus_value_prob_table_offset);
for (size_t z = 0; z < data.size(); z++) {
for (size_t x = 0; x < data[z].size(); x++) {
this->parsed_bonus_value_prob_table[z][x] = data[z][x];
}
this->nonrare_bonus_prob_spec = r.pget<parray<parray<uint8_t, 10>, 3>>(offsets.nonrare_bonus_prob_spec_offset);
this->bonus_type_prob_table = r.pget<parray<parray<uint8_t, 10>, 6>>(offsets.bonus_type_prob_table_offset);
this->special_mult = r.pget<parray<uint8_t, 0x0A>>(offsets.special_mult_offset);
this->special_percent = r.pget<parray<uint8_t, 0x0A>>(offsets.special_percent_offset);
{
const auto& data = r.pget<parray<parray<U16T, 0x0A>, 0x1C>>(offsets.tool_class_prob_table_offset);
for (size_t z = 0; z < data.size(); z++) {
for (size_t x = 0; x < data[z].size(); x++) {
this->tool_class_prob_table[z][x] = data[z][x];
}
}
this->parsed_bonus_value_prob_table_populated = true;
}
return this->parsed_bonus_value_prob_table;
this->technique_index_prob_table = r.pget<parray<parray<uint8_t, 0x0A>, 0x13>>(offsets.technique_index_prob_table_offset);
this->technique_level_ranges = r.pget<parray<parray<Range<uint8_t>, 0x0A>, 0x13>>(offsets.technique_level_ranges_offset);
this->armor_or_shield_type_bias = offsets.armor_or_shield_type_bias;
this->unit_max_stars_table = r.pget<parray<uint8_t, 0x0A>>(offsets.unit_max_stars_offset);
this->box_item_class_prob_table = r.pget<parray<parray<uint8_t, 10>, 7>>(offsets.box_item_class_prob_table_offset);
}
const parray<parray<uint8_t, 10>, 3>& CommonItemSet::Table::nonrare_bonus_prob_spec() const {
return this->r.pget<parray<parray<uint8_t, 10>, 3>>(this->offsets.nonrare_bonus_prob_spec_offset);
}
const parray<parray<uint8_t, 10>, 6>& CommonItemSet::Table::bonus_type_prob_table() const {
return this->r.pget<parray<parray<uint8_t, 10>, 6>>(this->offsets.bonus_type_prob_table_offset);
}
const parray<uint8_t, 0x0A>& CommonItemSet::Table::special_mult() const {
return this->r.pget<parray<uint8_t, 0x0A>>(this->offsets.special_mult_offset);
}
const parray<uint8_t, 0x0A>& CommonItemSet::Table::special_percent() const {
return this->r.pget<parray<uint8_t, 0x0A>>(this->offsets.special_percent_offset);
}
const parray<parray<uint16_t, 0x0A>, 0x1C>& CommonItemSet::Table::tool_class_prob_table() const {
if (!this->parsed_tool_class_prob_table_populated) {
if (this->is_big_endian) {
const auto& data = r.pget<parray<parray<be_uint16_t, 0x0A>, 0x1C>>(this->offsets.tool_class_prob_table_offset);
for (size_t z = 0; z < data.size(); z++) {
for (size_t x = 0; x < data[z].size(); x++) {
this->parsed_tool_class_prob_table[z][x] = data[z][x];
}
}
} else {
const auto& data = r.pget<parray<parray<le_uint16_t, 0x0A>, 0x1C>>(this->offsets.tool_class_prob_table_offset);
for (size_t z = 0; z < data.size(); z++) {
for (size_t x = 0; x < data[z].size(); x++) {
this->parsed_tool_class_prob_table[z][x] = data[z][x];
}
}
void CommonItemSet::Table::print_enemy_table(FILE* stream) const {
const auto& meseta_ranges = this->enemy_meseta_ranges;
const auto& drop_probs = this->enemy_type_drop_probs;
const auto& item_classes = this->enemy_item_classes;
// const parray<Range<uint16_t>, 0x64>& enemy_meseta_ranges() const;
// const parray<uint8_t, 0x64>& enemy_type_drop_probs() const;
// const parray<uint8_t, 0x64>& enemy_item_classes() const;
fprintf(stream, "## $_LOW $_HIGH DAR ITEM\n");
for (size_t z = 0; z < 0x64; z++) {
const char* item_class_name = "__UNKNOWN__";
switch (item_classes[z]) {
case 0x00:
item_class_name = "WEAPON";
break;
case 0x01:
item_class_name = "ARMOR";
break;
case 0x02:
item_class_name = "SHIELD";
break;
case 0x03:
item_class_name = "UNIT";
break;
case 0x04:
item_class_name = "TOOL";
break;
case 0x05:
item_class_name = "MESETA";
break;
}
this->parsed_tool_class_prob_table_populated = true;
fprintf(stream, "%02zX %5hu %5hu %3hhu %02hX (%s)\n",
z, meseta_ranges[z].min, meseta_ranges[z].max, drop_probs[z], item_classes[z], item_class_name);
}
return this->parsed_tool_class_prob_table;
}
const parray<parray<uint8_t, 0x0A>, 0x13>& CommonItemSet::Table::technique_index_prob_table() const {
return this->r.pget<parray<parray<uint8_t, 0x0A>, 0x13>>(this->offsets.technique_index_prob_table_offset);
}
const parray<parray<CommonItemSet::Table::Range<uint8_t>, 0x0A>, 0x13>& CommonItemSet::Table::technique_level_ranges() const {
return this->r.pget<parray<parray<Range<uint8_t>, 0x0A>, 0x13>>(this->offsets.technique_level_ranges_offset);
}
uint8_t CommonItemSet::Table::armor_or_shield_type_bias() const {
return this->offsets.armor_or_shield_type_bias;
}
const parray<uint8_t, 0x0A>& CommonItemSet::Table::unit_max_stars_table() const {
return this->r.pget<parray<uint8_t, 0x0A>>(this->offsets.unit_max_stars_offset);
}
const parray<parray<uint8_t, 10>, 7>& CommonItemSet::Table::box_item_class_prob_table() const {
return this->r.pget<parray<parray<uint8_t, 10>, 7>>(this->offsets.box_item_class_prob_table_offset);
}
uint16_t CommonItemSet::key_for_table(Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) {
@@ -205,7 +136,7 @@ AFSV2CommonItemSet::AFSV2CommonItemSet(
for (size_t section_id = 0; section_id < 10; section_id++) {
auto entry = pt_afs.get(difficulty * 10 + section_id);
StringReader r(entry.first, entry.second);
auto table = make_shared<Table>(pt_afs_data, r, false, false);
auto table = make_shared<Table>(r, false, false);
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::NORMAL, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::BATTLE, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::SOLO, difficulty, section_id), table);
@@ -217,48 +148,72 @@ AFSV2CommonItemSet::AFSV2CommonItemSet(
AFSArchive ct_afs(ct_afs_data);
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
auto r = ct_afs.get_reader(difficulty * 10);
auto table = make_shared<Table>(ct_afs_data, r, false, false);
auto table = make_shared<Table>(r, false, false);
for (size_t section_id = 0; section_id < 10; section_id++) {
this->tables.emplace(this->key_for_table(Episode::EP1, GameMode::CHALLENGE, difficulty, section_id), table);
}
}
}
GSLV3CommonItemSet::GSLV3CommonItemSet(std::shared_ptr<const std::string> gsl_data, bool is_big_endian) {
GSLV3V4CommonItemSet::GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gsl_data, bool is_big_endian) {
GSLArchive gsl(gsl_data, is_big_endian);
vector<Episode> episodes = {Episode::EP1, Episode::EP2};
auto filename_for_table = +[](Episode episode, uint8_t difficulty, uint8_t section_id, bool is_challenge) -> string {
const char* episode_token = "";
switch (episode) {
case Episode::EP1:
episode_token = "";
break;
case Episode::EP2:
episode_token = "l";
break;
case Episode::EP4:
episode_token = "s";
break;
default:
throw runtime_error("invalid episode");
}
return string_printf(
"ItemPT%s%s%c%1hhu.rel",
is_challenge ? "c" : "",
episode_token,
tolower(abbreviation_for_difficulty(difficulty)),
section_id);
};
vector<Episode> episodes = {Episode::EP1, Episode::EP2, Episode::EP4};
for (Episode episode : episodes) {
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
for (size_t section_id = 0; section_id < 10; section_id++) {
string filename = string_printf(
"ItemPT%s%c%1zu.rel",
((episode == Episode::EP2) ? "l" : ""),
tolower(abbreviation_for_difficulty(difficulty)),
section_id);
auto r = gsl.get_reader(filename);
auto table = make_shared<Table>(gsl_data, r, is_big_endian, true);
StringReader r;
try {
r = gsl.get_reader(filename_for_table(episode, difficulty, section_id, false));
} catch (const exception&) {
// Fall back to Episode 1 if Episode 4 data is missing
if (episode == Episode::EP4) {
auto ep1_table = this->tables.at(this->key_for_table(Episode::EP1, GameMode::NORMAL, difficulty, section_id));
this->tables.emplace(this->key_for_table(episode, GameMode::NORMAL, difficulty, section_id), ep1_table);
this->tables.emplace(this->key_for_table(episode, GameMode::BATTLE, difficulty, section_id), ep1_table);
this->tables.emplace(this->key_for_table(episode, GameMode::SOLO, difficulty, section_id), ep1_table);
continue;
} else {
throw;
}
}
auto table = make_shared<Table>(r, is_big_endian, true);
this->tables.emplace(this->key_for_table(episode, GameMode::NORMAL, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(episode, GameMode::BATTLE, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(episode, GameMode::SOLO, difficulty, section_id), table);
// TODO: These tables don't exist for Episode 4, and the GC version is
// the closest we have, so we use the Ep1 data from GC for Ep4.
if (episode == Episode::EP1) {
this->tables.emplace(this->key_for_table(Episode::EP4, GameMode::NORMAL, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(Episode::EP4, GameMode::BATTLE, difficulty, section_id), table);
this->tables.emplace(this->key_for_table(Episode::EP4, GameMode::SOLO, difficulty, section_id), table);
}
}
}
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
string filename = string_printf(
"ItemPTc%s%c0.rel",
((episode == Episode::EP2) ? "l" : ""),
tolower(abbreviation_for_difficulty(difficulty)));
auto r = gsl.get_reader(filename);
auto table = make_shared<Table>(gsl_data, r, is_big_endian, true);
for (size_t section_id = 0; section_id < 10; section_id++) {
this->tables.emplace(this->key_for_table(episode, GameMode::CHALLENGE, difficulty, section_id), table);
if (episode != Episode::EP4) {
for (size_t difficulty = 0; difficulty < 4; difficulty++) {
auto r = gsl.get_reader(filename_for_table(episode, difficulty, 0, true));
auto table = make_shared<Table>(r, is_big_endian, true);
for (size_t section_id = 0; section_id < 10; section_id++) {
this->tables.emplace(this->key_for_table(episode, GameMode::CHALLENGE, difficulty, section_id), table);
}
}
}
}
+30 -41
View File
@@ -13,7 +13,7 @@ public:
class Table {
public:
Table() = delete;
Table(std::shared_ptr<const std::string> owned_data, const StringReader& r, bool big_endian, bool is_v3);
Table(const StringReader& r, bool big_endian, bool is_v3);
template <typename IntT>
struct Range {
@@ -21,30 +21,35 @@ public:
IntT max;
} __attribute__((packed));
const parray<uint8_t, 0x0C>& base_weapon_type_prob_table() const;
const parray<int8_t, 0x0C>& subtype_base_table() const;
const parray<uint8_t, 0x0C>& subtype_area_length_table() const;
const parray<parray<uint8_t, 4>, 9>& grind_prob_table() const;
const parray<uint8_t, 0x05>& armor_shield_type_index_prob_table() const;
const parray<uint8_t, 0x05>& armor_slot_count_prob_table() const;
const parray<Range<uint16_t>, 0x64>& enemy_meseta_ranges() const;
const parray<uint8_t, 0x64>& enemy_type_drop_probs() const;
const parray<uint8_t, 0x64>& enemy_item_classes() const;
const parray<Range<uint16_t>, 0x0A>& box_meseta_ranges() const;
bool has_rare_bonus_value_prob_table() const;
const parray<parray<uint16_t, 6>, 0x17>& bonus_value_prob_table() const;
const parray<parray<uint8_t, 10>, 3>& nonrare_bonus_prob_spec() const;
const parray<parray<uint8_t, 10>, 6>& bonus_type_prob_table() const;
const parray<uint8_t, 0x0A>& special_mult() const;
const parray<uint8_t, 0x0A>& special_percent() const;
const parray<parray<uint16_t, 0x0A>, 0x1C>& tool_class_prob_table() const;
const parray<parray<uint8_t, 0x0A>, 0x13>& technique_index_prob_table() const;
const parray<parray<Range<uint8_t>, 0x0A>, 0x13>& technique_level_ranges() const;
uint8_t armor_or_shield_type_bias() const;
const parray<uint8_t, 0x0A>& unit_max_stars_table() const;
const parray<parray<uint8_t, 10>, 7>& box_item_class_prob_table() const;
parray<uint8_t, 0x0C> base_weapon_type_prob_table;
parray<int8_t, 0x0C> subtype_base_table;
parray<uint8_t, 0x0C> subtype_area_length_table;
parray<parray<uint8_t, 4>, 9> grind_prob_table;
parray<uint8_t, 0x05> armor_shield_type_index_prob_table;
parray<uint8_t, 0x05> armor_slot_count_prob_table;
parray<Range<uint16_t>, 0x64> enemy_meseta_ranges;
parray<uint8_t, 0x64> enemy_type_drop_probs;
parray<uint8_t, 0x64> enemy_item_classes;
parray<Range<uint16_t>, 0x0A> box_meseta_ranges;
bool has_rare_bonus_value_prob_table;
parray<parray<uint16_t, 6>, 0x17> bonus_value_prob_table;
parray<parray<uint8_t, 10>, 3> nonrare_bonus_prob_spec;
parray<parray<uint8_t, 10>, 6> bonus_type_prob_table;
parray<uint8_t, 0x0A> special_mult;
parray<uint8_t, 0x0A> special_percent;
parray<parray<uint16_t, 0x0A>, 0x1C> tool_class_prob_table;
parray<parray<uint8_t, 0x0A>, 0x13> technique_index_prob_table;
parray<parray<Range<uint8_t>, 0x0A>, 0x13> technique_level_ranges;
uint8_t armor_or_shield_type_bias;
parray<uint8_t, 0x0A> unit_max_stars_table;
parray<parray<uint8_t, 10>, 7> box_item_class_prob_table;
void print_enemy_table(FILE* stream) const;
private:
template <bool IsBigEndian>
void parse_itempt_t(const StringReader& r, bool is_v3);
template <bool IsBigEndian>
struct Offsets {
using U16T = typename std::conditional<IsBigEndian, be_uint16_t, le_uint16_t>::type;
@@ -248,22 +253,6 @@ public:
// There are several unused fields here.
} __attribute__((packed));
std::shared_ptr<const std::string> owned_data;
StringReader r;
bool is_big_endian;
bool is_v3;
Offsets<false> offsets;
mutable parray<Range<uint16_t>, 0x64> parsed_enemy_meseta_ranges;
mutable bool parsed_enemy_meseta_ranges_populated = false;
mutable parray<Range<uint16_t>, 0x0A> parsed_box_meseta_ranges;
mutable bool parsed_box_meseta_ranges_populated = false;
mutable parray<parray<uint16_t, 6>, 0x17> parsed_bonus_value_prob_table;
mutable bool parsed_bonus_value_prob_table_populated = false;
mutable parray<parray<uint16_t, 0x0A>, 0x1C> parsed_tool_class_prob_table;
mutable bool parsed_tool_class_prob_table_populated = false;
};
std::shared_ptr<const Table> get_table(Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const;
@@ -281,9 +270,9 @@ public:
AFSV2CommonItemSet(std::shared_ptr<const std::string> pt_afs_data, std::shared_ptr<const std::string> ct_afs_data);
};
class GSLV3CommonItemSet : public CommonItemSet {
class GSLV3V4CommonItemSet : public CommonItemSet {
public:
GSLV3CommonItemSet(std::shared_ptr<const std::string> gsl_data, bool is_big_endian);
GSLV3V4CommonItemSet(std::shared_ptr<const std::string> gsl_data, bool is_big_endian);
};
// Note: There are clearly better ways of doing this, but this implementation
+27 -21
View File
@@ -852,9 +852,9 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::AL_RAPPY:
return 0x06;
case EnemyType::ASTARK:
return 0x01;
return 0x41;
case EnemyType::BA_BOOTA:
return 0x0B;
return 0x4F;
case EnemyType::BARBA_RAY:
return 0x49;
case EnemyType::BARBAROUS_WOLF:
@@ -862,7 +862,7 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::BOOMA:
return 0x09;
case EnemyType::BOOTA:
return 0x09;
return 0x4D;
case EnemyType::BULCLAW:
return 0x28;
case EnemyType::CANADINE:
@@ -889,8 +889,9 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::DEL_LILY:
return 0x53;
case EnemyType::DEL_RAPPY:
return 0x57;
case EnemyType::DEL_RAPPY_ALT:
return 0x12;
return 0x58;
case EnemyType::DELBITER:
return 0x48;
case EnemyType::DELDEPTH:
@@ -904,9 +905,9 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::DOLMOLM:
return 0x40;
case EnemyType::DORPHON:
return 0x0C;
return 0x50;
case EnemyType::DORPHON_ECLAIR:
return 0x0D;
return 0x51;
case EnemyType::DRAGON:
return 0x2C;
case EnemyType::DUBCHIC:
@@ -932,15 +933,15 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::GILLCHIC:
return 0x32;
case EnemyType::GIRTABLULU:
return 0x06;
return 0x48;
case EnemyType::GOBOOMA:
return 0x0A;
case EnemyType::GOL_DRAGON:
return 0x4C;
case EnemyType::GORAN:
return 0x0E;
return 0x52;
case EnemyType::GORAN_DETONATOR:
return 0x0F;
return 0x53;
case EnemyType::GRASS_ASSASSIN:
return 0x0C;
case EnemyType::GUIL_SHARK:
@@ -956,7 +957,7 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::ILL_GILL:
return 0x52;
case EnemyType::KONDRIEU:
return 0x15;
return 0x5B;
case EnemyType::LA_DIMENIAN:
return 0x2A;
case EnemyType::LOVE_RAPPY:
@@ -972,9 +973,9 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::MERILTAS:
return 0x35;
case EnemyType::MERISSA_A:
return 0x04;
return 0x46;
case EnemyType::MERISSA_AA:
return 0x05;
return 0x47;
case EnemyType::MIGIUM:
return 0x16;
case EnemyType::MONEST:
@@ -994,8 +995,9 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::PAN_ARMS:
return 0x15;
case EnemyType::PAZUZU:
return 0x4B;
case EnemyType::PAZUZU_ALT:
return 0x08;
return 0x4C;
case EnemyType::POFUILLY_SLIME:
return 0x13;
case EnemyType::POUILLY_SLIME:
@@ -1003,7 +1005,7 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::POISON_LILY:
return 0x0D;
case EnemyType::PYRO_GORAN:
return 0x10;
return 0x54;
case EnemyType::RAG_RAPPY:
return 0x05;
case EnemyType::RECOBOX:
@@ -1013,17 +1015,19 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::SAINT_RAPPY:
return 0x4F;
case EnemyType::SAINT_MILLION:
return 0x13;
return 0x59;
case EnemyType::SAND_RAPPY:
return 0x55;
case EnemyType::SAND_RAPPY_ALT:
return 0x11;
return 0x56;
case EnemyType::SATELLITE_LIZARD:
return 0x44;
case EnemyType::SATELLITE_LIZARD_ALT:
return 0x03;
return 0x45;
case EnemyType::SAVAGE_WOLF:
return 0x07;
case EnemyType::SHAMBERTIN:
return 0x14;
return 0x5A;
case EnemyType::SINOW_BEAT:
return 0x1A;
case EnemyType::SINOW_BERILL:
@@ -1043,15 +1047,17 @@ uint8_t rare_table_index_for_enemy_type(EnemyType enemy_type) {
case EnemyType::VOL_OPT_2:
return 0x2E;
case EnemyType::YOWIE:
return 0x42;
case EnemyType::YOWIE_ALT:
return 0x02;
return 0x43;
case EnemyType::ZE_BOOTA:
return 0x0A;
return 0x4E;
case EnemyType::ZOL_GIBBON:
return 0x3C;
case EnemyType::ZU:
return 0x49;
case EnemyType::ZU_ALT:
return 0x07;
return 0x4A;
default:
throw runtime_error(string_printf("%s does not have a rare table entry", name_for_enum(enemy_type)));
}
+46 -30
View File
@@ -39,6 +39,16 @@ ItemCreator::ItemCreator(
restrictions(restrictions),
random_crypt(random_seed) {
this->generate_unit_stars_tables();
this->pt->print_enemy_table(stderr);
}
void ItemCreator::set_random_state(uint32_t seed, uint32_t absolute_offset) {
if ((this->random_crypt.seed() != seed) || (this->random_crypt.absolute_offset() > absolute_offset)) {
this->random_crypt = PSOV2Encryption(seed);
}
while (this->random_crypt.absolute_offset() < absolute_offset) {
this->random_crypt.next();
}
}
void ItemCreator::clear_destroyed_entities() {
@@ -132,7 +142,7 @@ ItemData ItemCreator::on_box_item_drop_with_area_norm(uint8_t area_norm) {
area_norm, this->random_crypt.seed(), this->random_crypt.absolute_offset());
ItemData item = this->check_rare_specs_and_create_rare_box_item(area_norm);
if (item.empty()) {
uint8_t item_class = this->get_rand_from_weighted_tables_2d_vertical(this->pt->box_item_class_prob_table(), area_norm);
uint8_t item_class = this->get_rand_from_weighted_tables_2d_vertical(this->pt->box_item_class_prob_table, area_norm);
this->log.info("Item class is %02hhX", item_class);
switch (item_class) {
case 0: // Weapon
@@ -175,11 +185,13 @@ ItemData ItemCreator::on_monster_item_drop_with_area_norm(uint32_t enemy_type, u
}
this->log.info("Enemy type: %" PRIX32 "; random state: %08" PRIX32 " %08" PRIX32, enemy_type, this->random_crypt.seed(), this->random_crypt.absolute_offset());
uint8_t type_drop_prob = this->pt->enemy_type_drop_probs().at(enemy_type);
uint8_t type_drop_prob = this->pt->enemy_type_drop_probs.at(enemy_type);
uint8_t drop_sample = this->rand_int(100);
if (drop_sample >= type_drop_prob) {
this->log.info("Drop not chosen (%hhu >= %hhu)", drop_sample, type_drop_prob);
return ItemData();
} else {
this->log.info("Drop chosen (%hhu < %hhu)", drop_sample, type_drop_prob);
}
ItemData item = this->check_rare_spec_and_create_rare_enemy_item(enemy_type, area_norm);
@@ -198,7 +210,7 @@ ItemData ItemCreator::on_monster_item_drop_with_area_norm(uint32_t enemy_type, u
item_class = 4;
break;
case 2:
item_class = this->pt->enemy_item_classes().at(enemy_type);
item_class = this->pt->enemy_item_classes.at(enemy_type);
break;
default:
throw logic_error("invalid item class determinant");
@@ -224,7 +236,7 @@ ItemData ItemCreator::on_monster_item_drop_with_area_norm(uint32_t enemy_type, u
break;
case 5: // Meseta
item.data1[0] = 0x04;
item.data2d = this->choose_meseta_amount(this->pt->enemy_meseta_ranges(), enemy_type) & 0xFFFF;
item.data2d = this->choose_meseta_amount(this->pt->enemy_meseta_ranges, enemy_type) & 0xFFFF;
break;
default:
return item;
@@ -277,12 +289,16 @@ uint32_t ItemCreator::choose_meseta_amount(
// Note: The original code seems like it has a bug here: it compares to 0xFF
// instead of 0xFFFF (and returns 0xFF if either limit matches 0xFF).
uint32_t ret = 0;
if (((min == 0xFFFF) || (max == 0xFFFF)) || (max < min)) {
return 0xFFFF;
ret = 0xFFFF;
} else if (min != max) {
return this->rand_int((max - min) + 1) + min;
ret = this->rand_int((max - min) + 1) + min;
} else {
ret = min;
}
return min;
this->log.info("Chose %" PRIu32 " Meseta from range [%hu, %hu]", ret, min, max);
return ret;
}
bool ItemCreator::should_allow_meseta_drops() const {
@@ -329,7 +345,7 @@ ItemData ItemCreator::check_rate_and_create_rare_item(const RareItemSet::Expande
item.data1[2] = drop.item_code[2];
switch (item.data1[0]) {
case 0:
if (this->pt->has_rare_bonus_value_prob_table()) {
if (this->pt->has_rare_bonus_value_prob_table) {
this->generate_rare_weapon_bonuses(item, this->rand_int(10));
} else {
this->generate_common_weapon_bonuses(item, area_norm);
@@ -362,13 +378,13 @@ void ItemCreator::generate_rare_weapon_bonuses(ItemData& item, uint32_t random_s
return;
}
if (!this->pt->has_rare_bonus_value_prob_table()) {
if (!this->pt->has_rare_bonus_value_prob_table) {
throw logic_error("generate_rare_weapon_bonuses called for common item table without rare bonus value probability table");
}
for (size_t z = 0; z < 6; z += 2) {
uint8_t bonus_type = this->get_rand_from_weighted_tables_2d_vertical(this->pt->bonus_type_prob_table(), random_sample);
int16_t bonus_value = this->get_rand_from_weighted_tables_2d_vertical(this->pt->bonus_value_prob_table(), 5);
uint8_t bonus_type = this->get_rand_from_weighted_tables_2d_vertical(this->pt->bonus_type_prob_table, random_sample);
int16_t bonus_value = this->get_rand_from_weighted_tables_2d_vertical(this->pt->bonus_value_prob_table, 5);
item.data1[z + 6] = bonus_type;
item.data1[z + 7] = bonus_value * 5 - 10;
// Note: The original code has a special case here, which divides
@@ -386,12 +402,12 @@ void ItemCreator::generate_common_weapon_bonuses(ItemData& item, uint8_t area_no
}
for (size_t row = 0; row < 3; row++) {
uint8_t spec = this->pt->nonrare_bonus_prob_spec().at(row).at(area_norm);
uint8_t spec = this->pt->nonrare_bonus_prob_spec.at(row).at(area_norm);
if (spec == 0xFF) {
this->log.info("Bonus %zu is forbidden", row);
} else {
item.data1[(row * 2) + 6] = this->get_rand_from_weighted_tables_2d_vertical(this->pt->bonus_type_prob_table(), area_norm);
int16_t amount = this->get_rand_from_weighted_tables_2d_vertical(this->pt->bonus_value_prob_table(), spec);
item.data1[(row * 2) + 6] = this->get_rand_from_weighted_tables_2d_vertical(this->pt->bonus_type_prob_table, area_norm);
int16_t amount = this->get_rand_from_weighted_tables_2d_vertical(this->pt->bonus_value_prob_table, spec);
item.data1[(row * 2) + 7] = amount * 5 - 10;
this->log.info("Bonus %zu generated as %02hhX %02hhX from area_norm %02hhX and spec %02hhX", row, item.data1[(row * 2) + 6], item.data1[(row * 2) + 7], area_norm, spec);
}
@@ -560,7 +576,7 @@ void ItemCreator::generate_common_item_variances(uint32_t area_norm, ItemData& i
break;
case 1:
if (item.data1[1] == 3) {
float f1 = 1.0 + this->pt->unit_max_stars_table().at(area_norm);
float f1 = 1.0 + this->pt->unit_max_stars_table.at(area_norm);
float f2 = this->rand_float_0_1_from_crypt();
uint8_t stars = static_cast<uint32_t>(f1 * f2) & 0xFF;
this->log.info("Unit stars: %g * %g = %" PRIu32, f1, f2, stars);
@@ -580,7 +596,7 @@ void ItemCreator::generate_common_item_variances(uint32_t area_norm, ItemData& i
this->generate_common_tool_variances(area_norm, item);
break;
case 4:
item.data2d = this->choose_meseta_amount(this->pt->box_meseta_ranges(), area_norm) & 0xFFFF;
item.data2d = this->choose_meseta_amount(this->pt->box_meseta_ranges, area_norm) & 0xFFFF;
break;
default:
// Note: The original code does the following here:
@@ -596,15 +612,15 @@ void ItemCreator::generate_common_item_variances(uint32_t area_norm, ItemData& i
void ItemCreator::generate_common_armor_or_shield_type_and_variances(char area_norm, ItemData& item) {
this->generate_common_armor_slots_and_bonuses(item);
uint8_t type = this->get_rand_from_weighted_tables_1d(this->pt->armor_shield_type_index_prob_table());
item.data1[2] = area_norm + type + this->pt->armor_or_shield_type_bias();
uint8_t type = this->get_rand_from_weighted_tables_1d(this->pt->armor_shield_type_index_prob_table);
item.data1[2] = area_norm + type + this->pt->armor_or_shield_type_bias;
if (item.data1[2] < 3) {
item.data1[2] = 0;
} else {
item.data1[2] -= 3;
}
this->log.info("Armor/shield type: max(%02hhX + %02hhX + %02hhX - 3, 0) = %02hhX",
area_norm, type, this->pt->armor_or_shield_type_bias(), item.data1[2]);
area_norm, type, this->pt->armor_or_shield_type_bias, item.data1[2]);
}
void ItemCreator::generate_common_armor_slots_and_bonuses(ItemData& item) {
@@ -622,13 +638,13 @@ void ItemCreator::generate_common_armor_slots_and_bonuses(ItemData& item) {
}
void ItemCreator::generate_common_armor_slot_count(ItemData& item) {
item.data1[5] = this->get_rand_from_weighted_tables_1d(this->pt->armor_slot_count_prob_table());
item.data1[5] = this->get_rand_from_weighted_tables_1d(this->pt->armor_slot_count_prob_table);
}
void ItemCreator::generate_common_tool_variances(uint32_t area_norm, ItemData& item) {
item.clear();
uint8_t tool_class = this->get_rand_from_weighted_tables_2d_vertical(this->pt->tool_class_prob_table(), area_norm);
uint8_t tool_class = this->get_rand_from_weighted_tables_2d_vertical(this->pt->tool_class_prob_table, area_norm);
if (this->is_v3() && (tool_class == 0x1A)) {
tool_class = 0x73;
}
@@ -653,7 +669,7 @@ void ItemCreator::generate_common_tool_variances(uint32_t area_norm, ItemData& i
}
if (item.data1[1] == 0x02) { // Tech disk
item.data1[4] = this->get_rand_from_weighted_tables_2d_vertical(this->pt->technique_index_prob_table(), area_norm);
item.data1[4] = this->get_rand_from_weighted_tables_2d_vertical(this->pt->technique_index_prob_table, area_norm);
item.data1[2] = this->generate_tech_disk_level(item.data1[4], area_norm);
this->clear_tool_item_if_invalid(item);
}
@@ -661,7 +677,7 @@ void ItemCreator::generate_common_tool_variances(uint32_t area_norm, ItemData& i
}
uint8_t ItemCreator::generate_tech_disk_level(uint32_t tech_num, uint32_t area_norm) {
const auto& range = this->pt->technique_level_ranges().at(tech_num).at(area_norm);
const auto& range = this->pt->technique_level_ranges.at(tech_num).at(area_norm);
if (((range.min == 0xFF) || (range.max == 0xFF)) || (range.max < range.min)) {
return 0xFF;
} else if (range.min != range.max) {
@@ -685,12 +701,12 @@ void ItemCreator::generate_common_weapon_variances(uint8_t area_norm, ItemData&
weapon_type_prob_table[0] = 0;
memmove(
weapon_type_prob_table.data() + 1,
this->pt->base_weapon_type_prob_table().data(),
this->pt->base_weapon_type_prob_table.data(),
0x0C);
for (size_t z = 1; z < 13; z++) {
// Technically this should be `if (... < 0)`, but whatever
if ((area_norm + this->pt->subtype_base_table().at(z - 1)) & 0x80) {
if ((area_norm + this->pt->subtype_base_table.at(z - 1)) & 0x80) {
weapon_type_prob_table[z] = 0;
}
}
@@ -706,8 +722,8 @@ void ItemCreator::generate_common_weapon_variances(uint8_t area_norm, ItemData&
this->log.info("00 chosen from subtype table; skipping item");
item.clear();
} else {
int8_t subtype_base = this->pt->subtype_base_table().at(item.data1[1] - 1);
uint8_t area_length = this->pt->subtype_area_length_table().at(item.data1[1] - 1);
int8_t subtype_base = this->pt->subtype_base_table.at(item.data1[1] - 1);
uint8_t area_length = this->pt->subtype_area_length_table.at(item.data1[1] - 1);
this->log.info("Subtype table yielded %02hhX; subtype base is %hhd with area length %hhu", item.data1[1], subtype_base, area_length);
if (subtype_base < 0) {
item.data1[2] = (area_norm + subtype_base) / area_length;
@@ -727,7 +743,7 @@ void ItemCreator::generate_common_weapon_variances(uint8_t area_norm, ItemData&
void ItemCreator::generate_common_weapon_grind(ItemData& item, uint8_t offset_within_subtype_range) {
if (item.data1[0] == 0) {
uint8_t offset = clamp<uint8_t>(offset_within_subtype_range, 0, 3);
item.data1[3] = this->get_rand_from_weighted_tables_2d_vertical(this->pt->grind_prob_table(), offset);
item.data1[3] = this->get_rand_from_weighted_tables_2d_vertical(this->pt->grind_prob_table, offset);
this->log.info("Generated grind %02hhX from offset within subtype range %02hhX", item.data1[3], offset_within_subtype_range);
}
}
@@ -740,13 +756,13 @@ void ItemCreator::generate_common_weapon_special(ItemData& item, uint8_t area_no
this->log.info("Item is rare; skipping special generation");
return;
}
uint8_t special_mult = this->pt->special_mult().at(area_norm);
uint8_t special_mult = this->pt->special_mult.at(area_norm);
if (special_mult == 0) {
this->log.info("Special multiplier is zero for area_norm %02hhX; skipping special generation", area_norm);
return;
}
uint8_t det = this->rand_int(100);
uint8_t prob = this->pt->special_percent().at(area_norm);
uint8_t prob = this->pt->special_percent.at(area_norm);
if (det >= prob) {
this->log.info("Special not chosen (%02hhX > %02hhX)", det, prob);
return;
+1
View File
@@ -28,6 +28,7 @@ public:
std::shared_ptr<const BattleRules> restrictions = nullptr);
~ItemCreator() = default;
void set_random_state(uint32_t seed, uint32_t absolute_offset);
void clear_destroyed_entities();
ItemData on_monster_item_drop(uint16_t entity_id, uint32_t enemy_type, uint8_t area);
+2 -2
View File
@@ -77,11 +77,11 @@ void Lobby::create_item_creator() {
case Version::GC_NTE:
case Version::GC_V3:
case Version::XB_V3:
common_item_set = s->common_item_set_v3;
common_item_set = s->common_item_set_v3_v4;
rare_item_set = s->rare_item_sets.at("rare-table-v3");
break;
case Version::BB_V4:
common_item_set = s->common_item_set_v3;
common_item_set = s->common_item_set_v3_v4;
rare_item_set = s->rare_item_sets.at("rare-table-v4");
break;
default:
+6 -5
View File
@@ -1682,12 +1682,13 @@ static void on_entity_drop_item_request(shared_ptr<Client> c, uint8_t command, u
string floor_warning_token = "";
if (l->map) {
const auto& enemy = l->map->enemies.at(cmd.entity_id);
uint32_t expected_rt_index = rare_table_index_for_enemy_type(enemy.type);
if (cmd.rt_index != expected_rt_index) {
effective_rt_index = rare_table_index_for_enemy_type(enemy.type);
// rt_indexes in Episode 4 don't match those sent in the command; we just
// ignore what the client sends.
if ((l->episode != Episode::EP4) && (cmd.rt_index != effective_rt_index)) {
l->log.warning("rt_index %02hhX from command does not match entity\'s expected index %02" PRIX32,
cmd.rt_index, expected_rt_index);
rt_index_warning_token = string_printf("$C6!RT:%02hhX/%02" PRIX32 "$C5 ", cmd.rt_index, expected_rt_index);
effective_rt_index = expected_rt_index;
cmd.rt_index, effective_rt_index);
rt_index_warning_token = string_printf("$C6!RT:%02hhX/%02" PRIX32 "$C5 ", cmd.rt_index, effective_rt_index);
}
if (cmd.floor != enemy.floor) {
l->log.warning("Floor %02hhX from command does not match entity\'s expected floor %02hhX",
+3 -6
View File
@@ -1087,12 +1087,9 @@ void ServerState::load_item_tables() {
auto ct_data_v2 = make_shared<string>(load_file("system/item-tables/ItemCT-v2.afs"));
auto pt_data_v2 = make_shared<string>(load_file("system/item-tables/ItemPT-v2.afs"));
this->common_item_set_v2 = make_shared<AFSV2CommonItemSet>(pt_data_v2, ct_data_v2);
config_log.info("Loading v3 common item table");
auto pt_data_v3 = make_shared<string>(load_file("system/item-tables/ItemPT-gc.gsl"));
this->common_item_set_v3 = make_shared<GSLV3CommonItemSet>(pt_data_v3, true);
// Note: The ItemPT files don't exist in BB, so we use the GC versions of them
// instead. This doesn't include Episode 4 of course, so we use Episode 1
// parameters for Episode 4 implicitly.
config_log.info("Loading v3+v4 common item table");
auto pt_data_v3_v4 = make_shared<string>(load_file("system/item-tables/ItemPT-gc-v4.gsl"));
this->common_item_set_v3_v4 = make_shared<GSLV3V4CommonItemSet>(pt_data_v3_v4, true);
config_log.info("Loading armor table");
auto armor_data = make_shared<string>(load_file("system/item-tables/ArmorRandom-gc.rel"));
+1 -1
View File
@@ -105,7 +105,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
std::shared_ptr<const GSLArchive> bb_data_gsl;
std::unordered_map<std::string, std::shared_ptr<const RareItemSet>> rare_item_sets;
std::shared_ptr<const CommonItemSet> common_item_set_v2;
std::shared_ptr<const CommonItemSet> common_item_set_v3;
std::shared_ptr<const CommonItemSet> common_item_set_v3_v4;
std::shared_ptr<const ArmorRandomSet> armor_random_set;
std::shared_ptr<const ToolRandomSet> tool_random_set;
std::array<std::shared_ptr<const WeaponRandomSet>, 4> weapon_random_sets;
File diff suppressed because it is too large Load Diff