From 582fd84f5ee0e0ff692965dad543b27f0c4c98a3 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Thu, 26 Oct 2023 17:16:04 -0700 Subject: [PATCH] rewrite CommonItemSet to support v2 --- notes/psobb/ItemPT.gsl | Bin 667648 -> 0 bytes src/CommonItemSet.cc | 275 ++++++++++++++-- src/CommonItemSet.hh | 520 +++++++++++++++++-------------- src/ItemCreator.cc | 134 ++++---- src/ItemCreator.hh | 9 +- src/Lobby.cc | 8 +- src/ServerState.cc | 13 +- src/ServerState.hh | 3 +- system/item-tables/ItemCT-v2.afs | Bin 0 -> 688128 bytes system/item-tables/ItemPT-v2.afs | Bin 0 -> 688128 bytes 10 files changed, 619 insertions(+), 343 deletions(-) delete mode 100755 notes/psobb/ItemPT.gsl create mode 100644 system/item-tables/ItemCT-v2.afs create mode 100644 system/item-tables/ItemPT-v2.afs diff --git a/notes/psobb/ItemPT.gsl b/notes/psobb/ItemPT.gsl deleted file mode 100755 index a0ffa9b66b42495e37628f70f0f4d7d6af5e187f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 667648 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 jBuqX#F)?xUXtUW&D1z~S$_h>}2bcrQ0p`GF data) - : gsl(data, true) {} - -const CommonItemSet::Table& CommonItemSet::get_table( - Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const { - // TODO: What should we do for Ep4? - string filename = string_printf( - "ItemPT%s%s%c%1d.rel", - ((mode == GameMode::CHALLENGE) ? "c" : ""), - ((episode == Episode::EP2) ? "l" : ""), - tolower(abbreviation_for_difficulty(difficulty)), - (mode == GameMode::CHALLENGE) ? 0 : secid); - auto data = this->gsl.get(filename); - if (data.second < sizeof(Table)) { - throw runtime_error(string_printf( - "ItemPT entry %s is too small (received %zX bytes, expected %zX bytes)", - filename.c_str(), data.second, sizeof(Table))); +CommonItemSet::Table::Table( + shared_ptr 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) { + if (is_big_endian) { + const auto& be_offsets = r.pget>(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_maxes_offset = be_offsets.unit_maxes_offset.load(); + this->offsets.box_item_class_prob_table_offset = be_offsets.box_item_class_prob_table_offset.load(); + } else { + this->offsets = r.pget>(r.pget_u32l(this->r.size() - 0x10)); + } +} + +const parray& CommonItemSet::Table::base_weapon_type_prob_table() const { + return this->r.pget>(this->offsets.base_weapon_type_prob_table_offset); +} +const parray& CommonItemSet::Table::subtype_base_table() const { + return this->r.pget>(this->offsets.subtype_base_table_offset); +} +const parray& CommonItemSet::Table::subtype_area_length_table() const { + return this->r.pget>(this->offsets.subtype_area_length_table_offset); +} +const parray, 9>& CommonItemSet::Table::grind_prob_table() const { + return this->r.pget, 9>>(this->offsets.grind_prob_table_offset); +} +const parray& CommonItemSet::Table::armor_shield_type_index_prob_table() const { + return this->r.pget>(this->offsets.armor_shield_type_index_prob_table_offset); +} +const parray& CommonItemSet::Table::armor_slot_count_prob_table() const { + return this->r.pget>(this->offsets.armor_slot_count_prob_table_offset); +} +const parray, 0x64>& CommonItemSet::Table::enemy_meseta_ranges() const { + if (!this->parsed_enemy_meseta_ranges_populated) { + if (this->is_big_endian) { + const auto& data = r.pget, 0x64>>(this->offsets.enemy_meseta_ranges_offset); + for (size_t z = 0; z < data.size(); z++) { + this->parsed_enemy_meseta_ranges[z] = Range{data[z].min, data[z].max}; + } + } else { + const auto& data = r.pget, 0x64>>(this->offsets.enemy_meseta_ranges_offset); + for (size_t z = 0; z < data.size(); z++) { + this->parsed_enemy_meseta_ranges[z] = Range{data[z].min, data[z].max}; + } + } + this->parsed_enemy_meseta_ranges_populated = true; + } + return this->parsed_enemy_meseta_ranges; +} +const parray& CommonItemSet::Table::enemy_type_drop_probs() const { + return this->r.pget>(this->offsets.enemy_type_drop_probs_offset); +} +const parray& CommonItemSet::Table::enemy_item_classes() const { + return this->r.pget>(this->offsets.enemy_item_classes_offset); +} +const parray, 0x0A>& CommonItemSet::Table::box_meseta_ranges() const { + if (!this->parsed_box_meseta_ranges_populated) { + if (this->is_big_endian) { + const auto& data = r.pget, 0x0A>>(this->offsets.box_meseta_ranges_offset); + for (size_t z = 0; z < data.size(); z++) { + this->parsed_box_meseta_ranges[z] = Range{data[z].min, data[z].max}; + } + } else { + const auto& data = r.pget, 0x0A>>(this->offsets.box_meseta_ranges_offset); + for (size_t z = 0; z < data.size(); z++) { + this->parsed_box_meseta_ranges[z] = Range{data[z].min, data[z].max}; + } + } + 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, 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, 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, 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, 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->parsed_bonus_value_prob_table_populated = true; + } + return this->parsed_bonus_value_prob_table; +} +const parray, 3>& CommonItemSet::Table::nonrare_bonus_prob_spec() const { + return this->r.pget, 3>>(this->offsets.nonrare_bonus_prob_spec_offset); +} +const parray, 6>& CommonItemSet::Table::bonus_type_prob_table() const { + return this->r.pget, 6>>(this->offsets.bonus_type_prob_table_offset); +} +const parray& CommonItemSet::Table::special_mult() const { + return this->r.pget>(this->offsets.special_mult_offset); +} +const parray& CommonItemSet::Table::special_percent() const { + return this->r.pget>(this->offsets.special_percent_offset); +} +const parray, 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, 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, 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]; + } + } + } + this->parsed_tool_class_prob_table_populated = true; + } + return this->parsed_tool_class_prob_table; +} +const parray, 0x13>& CommonItemSet::Table::technique_index_prob_table() const { + return this->r.pget, 0x13>>(this->offsets.technique_index_prob_table_offset); +} +const parray, 0x0A>, 0x13>& CommonItemSet::Table::technique_level_ranges() const { + return this->r.pget, 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& CommonItemSet::Table::unit_maxes_table() const { + return this->r.pget>(this->offsets.unit_maxes_offset); +} +const parray, 7>& CommonItemSet::Table::box_item_class_prob_table() const { + return this->r.pget, 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) { + // Bits: -----EEEMMDDSSSS + return (((static_cast(episode) << 8) & 0x0700) | + ((static_cast(mode) << 6) & 0x00C0) | + ((static_cast(difficulty) << 4) & 0x0030) | + (static_cast(secid) & 0x000F)); +} + +shared_ptr CommonItemSet::get_table( + Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const { + try { + return this->tables.at(this->key_for_table(episode, mode, difficulty, secid)); + } catch (const out_of_range&) { + throw runtime_error(string_printf("common item table not available for episode=%s, mode=%s, difficulty=%hu, secid=%hu", + name_for_episode(episode), name_for_mode(mode), difficulty, secid)); + } +} + +AFSV2CommonItemSet::AFSV2CommonItemSet( + std::shared_ptr pt_afs_data, + std::shared_ptr ct_afs_data) { + // ItemPT.afs has 40 entries; the first 10 are for Normal, then Hard, etc. + AFSArchive pt_afs(pt_afs_data); + for (size_t difficulty = 0; difficulty < 4; difficulty++) { + 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); + shared_ptr table(new Table(pt_afs_data, 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); + } + } + + // ItemCT.afs also has 40 entries, but only the 0th, 10th, 20th, and 30th are + // used (section_id is ignored) + AFSArchive ct_afs(ct_afs_data); + for (size_t difficulty = 0; difficulty < 4; difficulty++) { + auto r = ct_afs.get_reader(difficulty * 10); + shared_ptr
table(new Table(ct_afs_data, 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 gsl_data, bool is_big_endian) { + GSLArchive gsl(gsl_data, is_big_endian); + + vector episodes = {Episode::EP1, Episode::EP2}; + 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); + shared_ptr
table(new Table(gsl_data, 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); + shared_ptr
table(new 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); + } + } } - return *reinterpret_cast*>(data.first); } RELFileSet::RELFileSet(std::shared_ptr data) diff --git a/src/CommonItemSet.hh b/src/CommonItemSet.hh index 87725da2..17db9b35 100644 --- a/src/CommonItemSet.hh +++ b/src/CommonItemSet.hh @@ -8,6 +8,284 @@ #include "StaticGameData.hh" #include "Text.hh" +class CommonItemSet { +public: + class Table { + public: + Table() = delete; + Table(std::shared_ptr owned_data, const StringReader& r, bool big_endian, bool is_v3); + + template + struct Range { + IntT min; + IntT max; + } __attribute__((packed)); + + const parray& base_weapon_type_prob_table() const; + const parray& subtype_base_table() const; + const parray& subtype_area_length_table() const; + const parray, 9>& grind_prob_table() const; + const parray& armor_shield_type_index_prob_table() const; + const parray& armor_slot_count_prob_table() const; + const parray, 0x64>& enemy_meseta_ranges() const; + const parray& enemy_type_drop_probs() const; + const parray& enemy_item_classes() const; + const parray, 0x0A>& box_meseta_ranges() const; + bool has_rare_bonus_value_prob_table() const; + const parray, 0x17>& bonus_value_prob_table() const; + const parray, 3>& nonrare_bonus_prob_spec() const; + const parray, 6>& bonus_type_prob_table() const; + const parray& special_mult() const; + const parray& special_percent() const; + const parray, 0x1C>& tool_class_prob_table() const; + const parray, 0x13>& technique_index_prob_table() const; + const parray, 0x0A>, 0x13>& technique_level_ranges() const; + uint8_t armor_or_shield_type_bias() const; + const parray& unit_maxes_table() const; + const parray, 7>& box_item_class_prob_table() const; + + private: + template + struct Offsets { + using U16T = typename std::conditional::type; + using U32T = typename std::conditional::type; + + // This data structure uses index probability tables in multiple places. An + // index probability table is a table where each entry holds the probability + // that that entry's index is used. For example, if the armor slot count + // probability table contains [77, 17, 5, 1, 0], this means there is a 77% + // chance of no slots, 17% chance of 1 slot, 5% chance of 2 slots, 1% chance + // of 3 slots, and no chance of 4 slots. The values in index probability + // tables do not have to add up to 100; the game sums all of them and + // chooses a random number less than that maximum. + + // The area (floor) number is used in many places as well. Unlike the normal + // area numbers, which start with Pioneer 2, the area numbers in this + // structure start with Forest 1, and boss areas are treated as the first + // area of the next section (so De Rol Le has Mines 1 drops, for example). + // Final boss areas are treated as the last non-boss area (so Dark Falz + // boxes are like Ruins 3 boxes). We refer to these adjusted area numbers as + // (area - 1). + + // This index probability table determines the types of non-rare weapons. + // The indexes in this table correspond to the non-rare weapon types 01 + // through 0C (Saber through Wand). + // V2/GC: -> parray + /* 00 */ U32T base_weapon_type_prob_table_offset; + + // This table specifies the base subtype for each weapon type. Negative + // values here mean that the weapon cannot be found in the first N areas (so + // -2, for example, means that the weapon never appears in Forest 1 or 2 at + // all). Nonnegative values here mean the subtype can be found in all areas, + // and specify the base subtype (usually in the range [0, 4]). The subtype + // of weapon that actually appears depends on this value and a value from + // the following table. + // V2/GC: -> parray + /* 04 */ U32T subtype_base_table_offset; + + // This table specifies how many areas each weapon subtype appears in. For + // example, if Sword (subtype 02, which is index 1 in this table and the + // table above) has a subtype base of -2 and a subtype area length of 4, + // then Sword items can be found when area - 1 is 2, 3, 4, or 5 (Cave 1 + // through Mine 1), and Gigush (the next sword subtype) can be found in Mine + // 1 through Ruins 3. + // V2/GC: -> parray + /* 08 */ U32T subtype_area_length_table_offset; + + // This index probability table specifies how likely each possible grind + // value is. The table is indexed as [grind][subtype_area_index], where the + // subtype area index is how many areas the player is beyond the first area + // in which the subtype can first be found (clamped to [0, 3]). To continue + // the example above, in Cave 3, subtype_area_index would be 2, since Swords + // can first be found two areas earlier in Cave 1. + // For example, this table could look like this: + // [64 1E 19 14] // Chance of getting a grind +0 + // [00 1E 17 0F] // Chance of getting a grind +1 + // [00 14 14 0E] // Chance of getting a grind +2 + // ... + // C1 C2 C3 M1 // (Episode 1 area values from the example for reference) + // V2/GC: -> parray, 9> + /* 0C */ U32T grind_prob_table_offset; + + // TODO: Figure out exactly how this table is used. Anchor: 80106D34 + // V2/GC: -> parray + /* 10 */ U32T armor_shield_type_index_prob_table_offset; + + // This index probability table specifies how common each possible slot + // count is for armor drops. + // V2/GC: -> parray + /* 14 */ U32T armor_slot_count_prob_table_offset; + + // This array (indexed by enemy_id) specifies the range of meseta values + // that each enemy can drop. + // V2/GC: -> parray, 0x64> + /* 18 */ U32T enemy_meseta_ranges_offset; + + // Each byte in this table (indexed by enemy_type) represents the percent + // chance that the enemy drops anything at all. (This check is done before + // the rare drop check, so the chance of getting a rare item from an enemy + // is essentially this probability multiplied by the rare drop rate.) + // V2/GC: -> parray + /* 1C */ U32T enemy_type_drop_probs_offset; + + // Each byte in this table (indexed by enemy_type) represents the class of + // item that the enemy can drop. The values are: + // 00 = weapon + // 01 = armor + // 02 = shield + // 03 = unit + // 04 = tool + // 05 = meseta + // Anything else = no item + // V2/GC: -> parray + /* 20 */ U32T enemy_item_classes_offset; + + // This table (indexed by area - 1) specifies the ranges of meseta values + // that can drop from boxes. + // V2/GC: -> parray, 0x0A> + /* 24 */ U32T box_meseta_ranges_offset; + + // This array specifies the chance that a rare weapon will have each + // possible bonus value. This is indexed as [(bonus_value - 10 / 5)][spec], + // so the first row refers the probability of getting a -10% bonus, the next + // row is the chance of getting -5%, etc., all the way up to +100%. For + // non-rare items, spec is determined randomly based on the following field; + // for rare items, spec is always 5. + // V2: -> parray, 0x17> + // GC: -> parray, 0x17> + /* 28 */ U32T bonus_value_prob_table_offset; + + // This array specifies the value of spec to be used in the above lookup for + // non-rare items. This is NOT an index probability table; this is a direct + // lookup with indexes [bonus_index][area - 1]. A value of 0xFF in any byte + // of this array prevents any weapon from having a bonus in that slot. + // For example, the array might look like this: + // [00 00 00 01 01 01 01 02 02 02] + // [FF FF FF 00 00 00 01 01 01 01] + // [FF FF FF FF FF FF FF FF FF 00] + // F1 F2 C1 C2 C3 M1 M2 R1 R2 R3 // (Episode 1 areas, for reference) + // In this example, spec is 0, 1, or 2 in all cases where a weapon can have + // a bonus. In Forest 1 and 2 and Cave 1, weapons may have at most one + // bonus; in all other areas except Ruins 3, they can have at most two + // bonuses, and in Ruins 3, they can have up to three bonuses. + // V2/GC: // -> parray, 3> + /* 2C */ U32T nonrare_bonus_prob_spec_offset; + + // This array specifies the chance that a weapon will have each bonus type. + // The table is indexed as [bonus_type][area - 1] for non-rare items; for + // rare items, a random value in the range [0, 9] is used instead of + // (area - 1). + // For example, the table might look like this: + // [46 46 3F 3E 3E 3D 3C 3C 3A 3A] // Chance of getting no bonus + // [14 14 0A 0A 09 02 02 04 05 05] // Chance of getting Native bonus + // [0A 0A 12 11 11 09 09 08 08 08] // Chance of getting A.Beast bonus + // [00 00 09 0A 0B 13 12 08 09 09] // Chance of getting Machine bonus + // [00 00 00 01 01 08 0A 13 13 13] // Chance of getting Dark bonus + // [00 00 00 00 00 01 01 01 01 01] // Chance of getting Hit bonus + // F1 F2 C1 C2 C3 M1 M2 R1 R2 R3 // (Episode 1 areas, for reference) + // V2/GC: -> parray, 6> + /* 30 */ U32T bonus_type_prob_table_offset; + + // This array (indexed by area - 1) specifies a multiplier of used in + // special ability determination. It seems this uses the star values from + // ItemPMT, but not yet clear exactly in what way. + // TODO: Figure out exactly what this does. Anchor: 80106FEC + // V2/GC: -> parray + /* 34 */ U32T special_mult_offset; + + // This array (indexed by area - 1) specifies the probability that any + // non-rare weapon will have a special ability. + // V2/GC: -> parray + /* 38 */ U32T special_percent_offset; + + // This index probability table is indexed by [tool_class][area - 1]. The + // tool class refers to an entry in ItemPMT, which links it to the actual + // item code. + // V2/GC: -> parray, 0x1C> + /* 3C */ U32T tool_class_prob_table_offset; + + // This index probability table determines how likely each technique is to + // appear. The table is indexed as [technique_num][area - 1]. + // V2/GC: -> parray, 0x13> + /* 40 */ U32T technique_index_prob_table_offset; + + // This table specifies the ranges for technique disk levels. The table is + // indexed as [technique_num][area - 1]. If either min or max in the range + // is 0xFF, or if max < min, technique disks are not dropped for that + // technique and area pair. + // V2/GC: -> parray, 0x0A>, 0x13> + /* 44 */ U32T technique_level_ranges_offset; + + /* 48 */ uint8_t armor_or_shield_type_bias; + /* 49 */ parray unused1; + + // These values specify maximum indexes into another array which is + // generated at runtime. The values here are multiplied by a random float in + // the range [0, n] to look up the value in the secondary array, which is + // what ends up determining the unit type. + // TODO: Figure out and document the exact logic here. Anchor: 80106364 + // V2/GC: -> parray + /* 4C */ U32T unit_maxes_offset; + + // This index probability table determines which type of items drop from + // boxes. The table is indexed as [item_class][area - 1], with item_class as + // the result value (that is, in the example below, the game looks at a + // single column and sums the values going down, then the chosen item class + // is one of the row indexes based on the weight values in the column.) The + // resulting item_class value has the same meaning as in enemy_item_classes + // above. + // For example, this array might look like the following: + // [07 07 08 08 06 07 08 09 09 0A] // Chances per area of a weapon drop + // [02 02 02 02 03 02 02 02 03 03] // Chances per area of an armor drop + // [02 02 02 02 03 02 02 02 03 03] // Chances per area of a shield drop + // [00 00 03 03 03 04 03 04 05 05] // Chances per area of a unit drop + // [11 11 12 12 12 12 12 12 12 12] // Chances per area of a tool drop + // [32 32 32 32 32 32 32 32 32 32] // Chances per area of a meseta drop + // [16 16 11 11 11 11 11 0F 0C 0B] // Chances per area of an empty box + // F1 F2 C1 C2 C3 M1 M2 R1 R2 R3 // (Episode 1 areas, for reference) + // V2/GC: -> parray, 7> + /* 50 */ U32T box_item_class_prob_table_offset; + + // There are several unused fields here. + } __attribute__((packed)); + + std::shared_ptr owned_data; + StringReader r; + bool is_big_endian; + bool is_v3; + + Offsets offsets; + + mutable parray, 0x64> parsed_enemy_meseta_ranges; + mutable bool parsed_enemy_meseta_ranges_populated = false; + mutable parray, 0x0A> parsed_box_meseta_ranges; + mutable bool parsed_box_meseta_ranges_populated = false; + mutable parray, 0x17> parsed_bonus_value_prob_table; + mutable bool parsed_bonus_value_prob_table_populated = false; + mutable parray, 0x1C> parsed_tool_class_prob_table; + mutable bool parsed_tool_class_prob_table_populated = false; + }; + + std::shared_ptr get_table(Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const; + +protected: + CommonItemSet() = default; + + static uint16_t key_for_table(Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid); + + std::unordered_map> tables; +}; + +class AFSV2CommonItemSet : public CommonItemSet { +public: + AFSV2CommonItemSet(std::shared_ptr pt_afs_data, std::shared_ptr ct_afs_data); +}; + +class GSLV3CommonItemSet : public CommonItemSet { +public: + GSLV3CommonItemSet(std::shared_ptr gsl_data, bool is_big_endian); +}; + // Note: There are clearly better ways of doing this, but this implementation // closely follows what the original code in the client does. template @@ -51,248 +329,6 @@ struct ProbabilityTable { } }; -class CommonItemSet { -public: - template - struct Range { - IntT min; - IntT max; - } __attribute__((packed)); - - // The Table structure below describes the format of a single ItemPT entry - // (which corresponds to a single difficulty, episode, section ID, and - // challenge flag). ItemPT entries (within ItemPT.gsl) are named like this: - // string_printf("ItemPT%s%s%c%1d.rel", - // (is_challenge ? "c" : ""), - // (is_ep2 ? "l" : ""), - // char_for_difficulty(difficulty), // One of "nhvu" - // section_id); - template - struct Table { - using U16T = typename std::conditional::type; - using U32T = typename std::conditional::type; - // This data structure uses index probability tables in multiple places. An - // index probability table is a table where each entry holds the probability - // that that entry's index is used. For example, if the armor slot count - // probability table contains [77, 17, 5, 1, 0], this means there is a 77% - // chance of no slots, 17% chance of 1 slot, 5% chance of 2 slots, 1% chance - // of 3 slots, and no chance of 4 slots. The values in index probability - // tables do not have to add up to 100; the game sums all of them and - // chooses a random number less than that maximum. - - // The area (floor) number is used in many places as well. Unlike the normal - // area numbers, which start with Pioneer 2, the area numbers in this - // structure start with Forest 1, and boss areas are treated as the first - // area of the next section (so De Rol Le has Mines 1 drops, for example). - // Final boss areas are treated as the last non-boss area (so Dark Falz - // boxes are like Ruins 3 boxes). We refer to these adjusted area numbers as - // (area - 1). - - // This index probability table determines the types of non-rare weapons. - // The indexes in this table correspond to the non-rare weapon types 01 - // through 0C (Saber through Wand). - /* 0000 */ parray base_weapon_type_prob_table; - - // This table specifies the base subtype for each weapon type. Negative - // values here mean that the weapon cannot be found in the first N areas (so - // -2, for example, means that the weapon never appears in Forest 1 or 2 at - // all). Nonnegative values here mean the subtype can be found in all areas, - // and specify the base subtype (usually in the range [0, 4]). The subtype - // of weapon that actually appears depends on this value and a value from - // the following table. - /* 000C */ parray subtype_base_table; - - // This table specifies how many areas each weapon subtype appears in. For - // example, if Sword (subtype 02, which is index 1 in this table and the - // table above) has a subtype base of -2 and a subtype area length of 4, - // then Sword items can be found when area - 1 is 2, 3, 4, or 5 (Cave 1 - // through Mine 1), and Gigush (the next sword subtype) can be found in Mine - // 1 through Ruins 3. - /* 0018 */ parray subtype_area_length_table; - - // This index probability table specifies how likely each possible grind - // value is. The table is indexed as [grind][subtype_area_index], where the - // subtype area index is how many areas the player is beyond the first area - // in which the subtype can first be found (clamped to [0, 3]). To continue - // the example above, in Cave 3, subtype_area_index would be 2, since Swords - // can first be found two areas earlier in Cave 1. - // For example, this table could look like this: - // [64 1E 19 14] // Chance of getting a grind +0 - // [00 1E 17 0F] // Chance of getting a grind +1 - // [00 14 14 0E] // Chance of getting a grind +2 - // ... - // C1 C2 C3 M1 // (Episode 1 area values from the example for reference) - /* 0024 */ parray, 9> grind_prob_tables; - - // This array specifies the chance that a rare weapon will have each - // possible bonus value. This is indexed as [(bonus_value - 10 / 5)][spec], - // so the first row refers the probability of getting a -10% bonus, the next - // row is the chance of getting -5%, etc., all the way up to +100%. For - // non-rare items, spec is determined randomly based on the following field; - // for rare items, spec is always 5. - /* 0048 */ parray, 0x17> bonus_value_prob_tables; - - // This array specifies the value of spec to be used in the above lookup for - // non-rare items. This is NOT an index probability table; this is a direct - // lookup with indexes [bonus_index][area - 1]. A value of 0xFF in any byte - // of this array prevents any weapon from having a bonus in that slot. - // For example, the array might look like this: - // [00 00 00 01 01 01 01 02 02 02] - // [FF FF FF 00 00 00 01 01 01 01] - // [FF FF FF FF FF FF FF FF FF 00] - // F1 F2 C1 C2 C3 M1 M2 R1 R2 R3 // (Episode 1 areas, for reference) - // In this example, spec is 0, 1, or 2 in all cases where a weapon can have - // a bonus. In Forest 1 and 2 and Cave 1, weapons may have at most one - // bonus; in all other areas except Ruins 3, they can have at most two - // bonuses, and in Ruins 3, they can have up to three bonuses. - /* 015C */ parray, 3> nonrare_bonus_prob_spec; - - // This array specifies the chance that a weapon will have each bonus type. - // The table is indexed as [bonus_type][area - 1] for non-rare items; for - // rare items, a random value in the range [0, 9] is used instead of - // (area - 1). - // For example, the table might look like this: - // [46 46 3F 3E 3E 3D 3C 3C 3A 3A] // Chance of getting no bonus - // [14 14 0A 0A 09 02 02 04 05 05] // Chance of getting Native bonus - // [0A 0A 12 11 11 09 09 08 08 08] // Chance of getting A.Beast bonus - // [00 00 09 0A 0B 13 12 08 09 09] // Chance of getting Machine bonus - // [00 00 00 01 01 08 0A 13 13 13] // Chance of getting Dark bonus - // [00 00 00 00 00 01 01 01 01 01] // Chance of getting Hit bonus - // F1 F2 C1 C2 C3 M1 M2 R1 R2 R3 // (Episode 1 areas, for reference) - /* 017A */ parray, 6> bonus_type_prob_tables; - - // This array (indexed by area - 1) specifies a multiplier of used in - // special ability determination. It seems this uses the star values from - // ItemPMT, but not yet clear exactly in what way. - // TODO: Figure out exactly what this does. Anchor: 80106FEC - /* 01B6 */ parray special_mult; - - // This array (indexed by area - 1) specifies the probability that any - // non-rare weapon will have a special ability. - /* 01C0 */ parray special_percent; - - // TODO: Figure out exactly how this table is used. Anchor: 80106D34 - /* 01CA */ parray armor_shield_type_index_prob_table; - - // This index probability table specifies how common each possible slot - // count is for armor drops. - /* 01CF */ parray armor_slot_count_prob_table; - - // These values specify maximum indexes into another array which is - // generated at runtime. The values here are multiplied by a random float in - // the range [0, n] to look up the value in the secondary array, which is - // what ends up determining the unit type. - // TODO: Figure out and document the exact logic here. Anchor: 80106364 - /* 01D4 */ parray unit_maxes; - - // This index probability table is indexed by [tool_class][area - 1]. The - // tool class refers to an entry in ItemPMT, which links it to the actual - // item code. - /* 01DE */ parray, 0x1C> tool_class_prob_table; - - // This index probability table determines how likely each technique is to - // appear. The table is indexed as [technique_num][area - 1]. - /* 040E */ parray, 0x13> technique_index_prob_table; - - // This table specifies the ranges for technique disk levels. The table is - // indexed as [technique_num][area - 1]. If either min or max in the range - // is 0xFF, or if max < min, technique disks are not dropped for that - // technique and area pair. - /* 04CC */ parray, 0x0A>, 0x13> technique_level_ranges; - - // Each byte in this table (indexed by enemy_type) represents the percent - // chance that the enemy drops anything at all. (This check is done before - // the rare drop check, so the chance of getting a rare item from an enemy - // is essentially this probability multiplied by the rare drop rate.) - /* 0648 */ parray enemy_type_drop_probs; - - // This array (indexed by enemy_id) specifies the range of meseta values - // that each enemy can drop. - /* 06AC */ parray, 0x64> enemy_meseta_ranges; - - // Each byte in this table (indexed by enemy_type) represents the class of - // item that the enemy can drop. The values are: - // 00 = weapon - // 01 = armor - // 02 = shield - // 03 = unit - // 04 = tool - // 05 = meseta - // Anything else = no item - /* 083C */ parray enemy_item_classes; - - // This table (indexed by area - 1) specifies the ranges of meseta values - // that can drop from boxes. - /* 08A0 */ parray, 0x0A> box_meseta_ranges; - - // This index probability table determines which type of items drop from - // boxes. The table is indexed as [item_class][area - 1], with item_class as - // the result value (that is, in the example below, the game looks at a - // single column and sums the values going down, then the chosen item class - // is one of the row indexes based on the weight values in the column.) The - // resulting item_class value has the same meaning as in enemy_item_classes - // above. - // For example, this array might look like the following: - // [07 07 08 08 06 07 08 09 09 0A] // Chances per area of a weapon drop - // [02 02 02 02 03 02 02 02 03 03] // Chances per area of an armor drop - // [02 02 02 02 03 02 02 02 03 03] // Chances per area of a shield drop - // [00 00 03 03 03 04 03 04 05 05] // Chances per area of a unit drop - // [11 11 12 12 12 12 12 12 12 12] // Chances per area of a tool drop - // [32 32 32 32 32 32 32 32 32 32] // Chances per area of a meseta drop - // [16 16 11 11 11 11 11 0F 0C 0B] // Chances per area of an empty box - // F1 F2 C1 C2 C3 M1 M2 R1 R2 R3 // (Episode 1 areas, for reference) - /* 08C8 */ parray, 7> box_item_class_prob_tables; - - /* 090E */ parray unused1; - - /* 0910 */ U32T base_weapon_type_prob_table_offset; - /* 0914 */ U32T subtype_base_table_offset; - /* 0918 */ U32T subtype_area_length_table_offset; - /* 091C */ U32T grind_prob_tables_offset; - /* 0920 */ U32T armor_shield_type_index_prob_table_offset; - /* 0924 */ U32T armor_slot_count_prob_table_offset; - /* 0928 */ U32T enemy_meseta_ranges_offset; - /* 092C */ U32T enemy_type_drop_probs_offset; - /* 0930 */ U32T enemy_item_classes_offset; - /* 0934 */ U32T box_meseta_ranges_offset; - /* 0938 */ U32T bonus_value_prob_tables_offset; - /* 093C */ U32T nonrare_bonus_prob_spec_offset; - /* 0940 */ U32T bonus_type_prob_tables_offset; - /* 0944 */ U32T special_mult_offset; - /* 0948 */ U32T special_percent_offset; - /* 094C */ U32T tool_class_prob_table_offset; - /* 0950 */ U32T technique_index_prob_table_offset; - /* 0954 */ U32T technique_level_ranges_offset; - /* 0958 */ uint8_t armor_or_shield_type_bias; - /* 0959 */ parray unused2; - /* 095C */ U32T unit_maxes_offset; - /* 0960 */ U32T box_item_class_prob_tables_offset; - /* 0964 */ U32T unused_offset2; - /* 0968 */ U32T unused_offset3; - /* 096C */ U32T unused_offset4; - /* 0970 */ U32T unused_offset5; - /* 0974 */ U32T unused_offset6; - /* 0978 */ U32T unused_offset7; - /* 097C */ U32T unused_offset8; - /* 0980 */ U16T unknown_f1[0x20]; - /* 09C0 */ U32T unknown_f1_offset; - /* 09C4 */ U32T unknown_f2[3]; - /* 09D0 */ U32T offset_table_offset; - /* 09D4 */ U32T unknown_f3[3]; - /* 09E0 (end of structure) */ - - void print(FILE* stream) const; - } __attribute__((packed)); - - CommonItemSet(std::shared_ptr data); - - const Table& get_table( - Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const; - -private: - GSLArchive gsl; -}; - class RELFileSet { public: template diff --git a/src/ItemCreator.cc b/src/ItemCreator.cc index 367995d2..cb293aec 100644 --- a/src/ItemCreator.cc +++ b/src/ItemCreator.cc @@ -27,15 +27,13 @@ ItemCreator::ItemCreator( mode(mode), difficulty(difficulty), section_id(section_id), - common_item_set(common_item_set), rare_item_set(rare_item_set), armor_random_set(armor_random_set), tool_random_set(tool_random_set), weapon_random_set(weapon_random_set), tekker_adjustment_set(tekker_adjustment_set), item_parameter_table(item_parameter_table), - pt(&this->common_item_set->get_table( - this->episode, this->mode, this->difficulty, this->section_id)), + pt(common_item_set->get_table(this->episode, this->mode, this->difficulty, this->section_id)), restrictions(restrictions), random_crypt(random_seed) {} @@ -118,7 +116,7 @@ ItemData ItemCreator::on_box_item_drop_with_norm_area(uint8_t area_norm) { 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_tables, area_norm); + this->pt->box_item_class_prob_table(), area_norm); switch (item_class) { case 0: // Weapon item.data1[0] = 0; @@ -151,21 +149,21 @@ ItemData ItemCreator::on_box_item_drop_with_norm_area(uint8_t area_norm) { return item; } -ItemData ItemCreator::on_monster_item_drop_with_norm_area(uint32_t enemy_type, uint8_t norm_area) { +ItemData ItemCreator::on_monster_item_drop_with_norm_area(uint32_t enemy_type, uint8_t area_norm) { if (enemy_type > 0x58) { this->log.warning("Invalid enemy type: %" PRIX32, enemy_type); return ItemData(); } this->log.info("Enemy type: %" PRIX32, enemy_type); - uint8_t type_drop_prob = this->pt->enemy_type_drop_probs[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 vs. %hhu)", drop_sample, type_drop_prob); return ItemData(); } - ItemData item = this->check_rare_spec_and_create_rare_enemy_item(enemy_type); + ItemData item = this->check_rare_spec_and_create_rare_enemy_item(enemy_type, area_norm); if (item.empty()) { uint32_t item_class_determinant = this->should_allow_meseta_drops() @@ -181,7 +179,7 @@ ItemData ItemCreator::on_monster_item_drop_with_norm_area(uint32_t enemy_type, u item_class = 4; break; case 2: - item_class = this->pt->enemy_item_classes[enemy_type]; + item_class = this->pt->enemy_item_classes().at(enemy_type); break; default: throw logic_error("invalid item class determinant"); @@ -208,14 +206,14 @@ ItemData ItemCreator::on_monster_item_drop_with_norm_area(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; } if (item.data1[0] != 0x04) { - this->generate_common_item_variances(norm_area, item); + this->generate_common_item_variances(area_norm, item); } } @@ -231,7 +229,7 @@ ItemData ItemCreator::check_rare_specs_and_create_rare_box_item(uint8_t area_nor auto rare_specs = this->rare_item_set->get_box_specs( this->mode, this->episode, this->difficulty, this->section_id, area_norm + 1); for (const auto& spec : rare_specs) { - item = this->check_rate_and_create_rare_item(spec); + item = this->check_rate_and_create_rare_item(spec, area_norm); if (!item.empty()) { this->log.info("Box spec %08" PRIX32 " produced item %02hhX%02hhX%02hhX", spec.probability, spec.item_code[0], spec.item_code[1], spec.item_code[2]); @@ -254,7 +252,7 @@ float ItemCreator::rand_float_0_1_from_crypt() { template uint32_t ItemCreator::choose_meseta_amount( - const parray, NumRanges> ranges, + const parray, NumRanges> ranges, size_t table_index) { uint16_t min = ranges[table_index].min; uint16_t max = ranges[table_index].max; @@ -273,7 +271,7 @@ bool ItemCreator::should_allow_meseta_drops() const { return (this->mode != GameMode::CHALLENGE); } -ItemData ItemCreator::check_rare_spec_and_create_rare_enemy_item(uint32_t enemy_type) { +ItemData ItemCreator::check_rare_spec_and_create_rare_enemy_item(uint32_t enemy_type, uint8_t area_norm) { ItemData item; if (this->are_rare_drops_allowed() && (enemy_type > 0) && (enemy_type < 0x58)) { // Note: In the original implementation, enemies can only have one possible @@ -283,7 +281,7 @@ ItemData ItemCreator::check_rare_spec_and_create_rare_enemy_item(uint32_t enemy_ auto rare_specs = this->rare_item_set->get_enemy_specs( this->mode, this->episode, this->difficulty, this->section_id, enemy_type); for (const auto& spec : rare_specs) { - item = this->check_rate_and_create_rare_item(spec); + item = this->check_rate_and_create_rare_item(spec, area_norm); if (!item.empty()) { this->log.info("Enemy spec %08" PRIX32 " produced item %02hhX%02hhX%02hhX", spec.probability, spec.item_code[0], spec.item_code[1], spec.item_code[2]); @@ -296,8 +294,7 @@ ItemData ItemCreator::check_rare_spec_and_create_rare_enemy_item(uint32_t enemy_ return item; } -ItemData ItemCreator::check_rate_and_create_rare_item( - const RareItemSet::ExpandedDrop& drop) { +ItemData ItemCreator::check_rate_and_create_rare_item(const RareItemSet::ExpandedDrop& drop, uint8_t area_norm) { if (drop.probability == 0) { return ItemData(); } @@ -314,7 +311,11 @@ ItemData ItemCreator::check_rate_and_create_rare_item( item.data1[2] = drop.item_code[2]; switch (item.data1[0]) { case 0: - this->generate_rare_weapon_bonuses(item, this->rand_int(10)); + 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); + } this->set_item_unidentified_flag_if_not_challenge(item); break; case 1: @@ -338,17 +339,18 @@ ItemData ItemCreator::check_rate_and_create_rare_item( return item; } -void ItemCreator::generate_rare_weapon_bonuses( - ItemData& item, uint32_t random_sample) { +void ItemCreator::generate_rare_weapon_bonuses(ItemData& item, uint32_t random_sample) { if (item.data1[0] != 0) { return; } + 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_tables, random_sample); - int16_t bonus_value = this->get_rand_from_weighted_tables_2d_vertical( - this->pt->bonus_value_prob_tables, 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 @@ -362,23 +364,24 @@ void ItemCreator::generate_rare_weapon_bonuses( void ItemCreator::generate_common_weapon_bonuses( ItemData& item, uint8_t area_norm) { - if (item.data1[0] == 0) { - for (size_t row = 0; row < 3; row++) { - uint8_t spec = this->pt->nonrare_bonus_prob_spec[row][area_norm]; - if (spec != 0xFF) { - item.data1[(row * 2) + 6] = this->get_rand_from_weighted_tables_2d_vertical( - this->pt->bonus_type_prob_tables, area_norm); - int16_t amount = this->get_rand_from_weighted_tables_2d_vertical( - this->pt->bonus_value_prob_tables, spec); - item.data1[(row * 2) + 7] = amount * 5 - 10; - } - // Note: The original code has a special case here, which divides - // item.data1[z + 7] by 5 and multiplies it by 5 again if bonus_type is 5 - // (Hit). Why this is done is unclear, because item.data1[z + 7] must - // already be a multiple of 5. - } - this->deduplicate_weapon_bonuses(item); + if (item.data1[0] != 0) { + return; } + + for (size_t row = 0; row < 3; row++) { + uint8_t spec = this->pt->nonrare_bonus_prob_spec().at(row).at(area_norm); + if (spec != 0xFF) { + 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; + } + // Note: The original code has a special case here, which divides + // item.data1[z + 7] by 5 and multiplies it by 5 again if bonus_type is 5 + // (Hit). Why this is done is unclear, because item.data1[z + 7] must + // already be a multiple of 5. + } + + this->deduplicate_weapon_bonuses(item); } void ItemCreator::deduplicate_weapon_bonuses(ItemData& item) const { @@ -523,7 +526,7 @@ void ItemCreator::generate_common_item_variances(uint32_t norm_area, ItemData& i break; case 1: if (item.data1[1] == 3) { - float f1 = 1.0 + this->pt->unit_maxes[norm_area]; + float f1 = 1.0 + this->pt->unit_maxes_table().at(norm_area); float f2 = this->rand_float_0_1_from_crypt(); this->generate_common_unit_variances(static_cast(f1 * f2) & 0xFF, item); if (item.data1[2] == 0xFF) { @@ -540,7 +543,7 @@ void ItemCreator::generate_common_item_variances(uint32_t norm_area, ItemData& i this->generate_common_tool_variances(norm_area, item); break; case 4: - item.data2d = this->choose_meseta_amount(this->pt->box_meseta_ranges, norm_area) & 0xFFFF; + item.data2d = this->choose_meseta_amount(this->pt->box_meseta_ranges(), norm_area) & 0xFFFF; break; default: // Note: The original code does the following here: @@ -557,9 +560,8 @@ 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 { @@ -582,22 +584,20 @@ 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 (tool_class == 0x1A) { tool_class = 0x73; } this->generate_common_tool_type(tool_class, item); 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); } @@ -605,15 +605,13 @@ 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) { - uint8_t min = this->pt->technique_level_ranges[tech_num][area_norm].min; - uint8_t max = this->pt->technique_level_ranges[tech_num][area_norm].max; - - if (((min == 0xFF) || (max == 0xFF)) || (max < min)) { + 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 (min != max) { - return this->rand_int((max - min) + 1) + min; + } else if (range.min != range.max) { + return this->rand_int((range.max - range.min) + 1) + range.min; } - return min; + return range.min; } void ItemCreator::generate_common_tool_type(uint8_t tool_class, ItemData& item) const { @@ -638,12 +636,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[z - 1]) & 0x80) { + if ((area_norm + this->pt->subtype_base_table().at(z - 1)) & 0x80) { weapon_type_prob_table[z] = 0; } } @@ -652,8 +650,8 @@ void ItemCreator::generate_common_weapon_variances(uint8_t area_norm, ItemData& if (item.data1[1] == 0) { item.clear(); } else { - int8_t subtype_base = this->pt->subtype_base_table[item.data1[1] - 1]; - uint8_t area_length = this->pt->subtype_area_length_table[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); if (subtype_base < 0) { item.data1[2] = (area_norm + subtype_base) / area_length; this->generate_common_weapon_grind( @@ -673,8 +671,7 @@ void ItemCreator::generate_common_weapon_grind( ItemData& item, uint8_t offset_within_subtype_range) { if (item.data1[0] == 0) { uint8_t offset = clamp(offset_within_subtype_range, 0, 3); - item.data1[3] = this->get_rand_from_weighted_tables_2d_vertical( - this->pt->grind_prob_tables, offset); + item.data1[3] = this->get_rand_from_weighted_tables_2d_vertical(this->pt->grind_prob_table(), offset); } } @@ -686,11 +683,11 @@ void ItemCreator::generate_common_weapon_special( if (this->item_parameter_table->is_item_rare(item)) { return; } - uint8_t special_mult = this->pt->special_mult[area_norm]; + uint8_t special_mult = this->pt->special_mult().at(area_norm); if (special_mult == 0) { return; } - if (this->rand_int(100) >= this->pt->special_percent[area_norm]) { + if (this->rand_int(100) >= this->pt->special_percent().at(area_norm)) { return; } item.data1[4] = this->choose_weapon_special( @@ -807,8 +804,7 @@ void ItemCreator::generate_common_unit_variances(uint8_t det, ItemData& item) { // 0, 1, 2, or 3. An input table of 40 40 80 would return 2 50% of the time, and // 0 or 1 each 25% of the time. template -IntT ItemCreator::get_rand_from_weighted_tables( - const IntT* tables, size_t offset, size_t num_values, size_t stride) { +IntT ItemCreator::get_rand_from_weighted_tables(const IntT* tables, size_t offset, size_t num_values, size_t stride) { uint64_t rand_max = 0; for (size_t x = 0; x != num_values; x++) { rand_max += tables[x * stride + offset]; @@ -834,10 +830,8 @@ IntT ItemCreator::get_rand_from_weighted_tables_1d(const parray& tables } template -IntT ItemCreator::get_rand_from_weighted_tables_2d_vertical( - const parray, Y>& tables, size_t offset) { - return ItemCreator::get_rand_from_weighted_tables(tables[0].data(), - offset, Y, X); +IntT ItemCreator::get_rand_from_weighted_tables_2d_vertical(const parray, Y>& tables, size_t offset) { + return ItemCreator::get_rand_from_weighted_tables(tables[0].data(), offset, Y, X); } vector ItemCreator::generate_armor_shop_contents(size_t player_level) { diff --git a/src/ItemCreator.hh b/src/ItemCreator.hh index 2ca0bfc4..69314ea9 100644 --- a/src/ItemCreator.hh +++ b/src/ItemCreator.hh @@ -49,14 +49,13 @@ private: GameMode mode; uint8_t difficulty; uint8_t section_id; - std::shared_ptr common_item_set; std::shared_ptr rare_item_set; std::shared_ptr armor_random_set; std::shared_ptr tool_random_set; std::shared_ptr weapon_random_set; std::shared_ptr tekker_adjustment_set; std::shared_ptr item_parameter_table; - const CommonItemSet::Table* pt; + std::shared_ptr pt; std::shared_ptr restrictions; parray unit_weights_table1; @@ -78,14 +77,14 @@ private: template uint32_t choose_meseta_amount( - const parray, NumRanges> ranges, + const parray, NumRanges> ranges, size_t table_index); bool should_allow_meseta_drops() const; - ItemData check_rare_spec_and_create_rare_enemy_item(uint32_t enemy_type); + ItemData check_rare_spec_and_create_rare_enemy_item(uint32_t enemy_type, uint8_t area_norm); ItemData check_rare_specs_and_create_rare_box_item(uint8_t area_norm); - ItemData check_rate_and_create_rare_item(const RareItemSet::ExpandedDrop& drop); + ItemData check_rate_and_create_rare_item(const RareItemSet::ExpandedDrop& drop, uint8_t area_norm); void generate_rare_weapon_bonuses(ItemData& item, uint32_t random_sample); void deduplicate_weapon_bonuses(ItemData& item) const; diff --git a/src/Lobby.cc b/src/Lobby.cc index d8e4f62e..9f3c5224 100644 --- a/src/Lobby.cc +++ b/src/Lobby.cc @@ -47,16 +47,20 @@ void Lobby::create_item_creator() { auto s = this->require_server_state(); shared_ptr rare_item_set; + shared_ptr common_item_set; if (this->base_version == GameVersion::BB) { + common_item_set = s->common_item_set_v3; rare_item_set = s->rare_item_sets.at("rare-table-v4"); } else if (this->base_version == GameVersion::GC || this->base_version == GameVersion::XB) { + common_item_set = s->common_item_set_v3; rare_item_set = s->rare_item_sets.at("rare-table-v3"); } else { - // TODO: Should there be a separate table for V1 eventually? + // TODO: Should there be separate tables for V1 eventually? + common_item_set = s->common_item_set_v2; rare_item_set = s->rare_item_sets.at("rare-table-v2"); } this->item_creator.reset(new ItemCreator( - s->common_item_set, + common_item_set, rare_item_set, s->armor_random_set, s->tool_random_set, diff --git a/src/ServerState.cc b/src/ServerState.cc index 16a24de2..4449c11e 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -968,13 +968,16 @@ void ServerState::load_item_tables() { this->rare_item_sets.emplace("rare-table-v4", new RELRareItemSet(data)); } - // Note: These files don't exist in BB, so we use the GC versions of them + config_log.info("Loading v2 common item table"); + shared_ptr ct_data_v2(new string(load_file("system/item-tables/ItemCT-v2.afs"))); + shared_ptr pt_data_v2(new string(load_file("system/item-tables/ItemPT-v2.afs"))); + this->common_item_set_v2.reset(new AFSV2CommonItemSet(pt_data_v2, ct_data_v2)); + config_log.info("Loading v3 common item table"); + shared_ptr pt_data_v3(new string(load_file("system/item-tables/ItemPT-gc.gsl"))); + this->common_item_set_v3.reset(new 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 common item table"); - shared_ptr pt_data(new string(load_file( - "system/item-tables/ItemPT-gc.gsl"))); - this->common_item_set.reset(new CommonItemSet(pt_data)); config_log.info("Loading armor table"); shared_ptr armor_data(new string(load_file( diff --git a/src/ServerState.hh b/src/ServerState.hh index 91aae008..47fa24a9 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -100,7 +100,8 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr battle_params; std::shared_ptr bb_data_gsl; std::unordered_map> rare_item_sets; - std::shared_ptr common_item_set; + std::shared_ptr common_item_set_v2; + std::shared_ptr common_item_set_v3; std::shared_ptr armor_random_set; std::shared_ptr tool_random_set; std::array, 4> weapon_random_sets; diff --git a/system/item-tables/ItemCT-v2.afs b/system/item-tables/ItemCT-v2.afs new file mode 100644 index 0000000000000000000000000000000000000000..eb1718aa87e9d2d3b6c54a11683437e7c7b94b8a GIT binary patch literal 688128 zcmeI*U1%I~EE-tNN*Ws;WO-t?qw4Pd#;P>c?ixxUOs_D;j+#8}H4= z-^<36*|?RBf0B(~$;N+^jn8G{ceC+_+4!Hb@zrenWj6j}Ha3-P``LJJHvV2V*3KpS z*UH8}$;Pi_2VYBv5d8~-sIn>gEkHr|_!zn6_CvvDgM|0El~ zl8qBX9uPnP0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ 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 F{C|WWyp;d| literal 0 HcmV?d00001 diff --git a/system/item-tables/ItemPT-v2.afs b/system/item-tables/ItemPT-v2.afs new file mode 100644 index 0000000000000000000000000000000000000000..47a15b0aa0f8f91b8c7bf6a5f7b8106ca87b0888 GIT binary patch literal 688128 zcmeI*Z)hBMp6Bsjb#--hm(*%YYRi%>x8*3~(K1#h>C9N0j3#3*i^#>fTU{ms;|ES2 zG)J;5CS;z7B^efD5(s(VSvcJ?8HRA!%WfDZ;Rv~e4PkMH!#leRCLEXD!w+)9F=u8V zw0q zI*bi@LI42-5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY** z5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0 z009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{ z1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009IL zKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~ z0R#|0009ILKmY**5I_I{1Q0*~0R#|0009ILKmY**5I_I{1Q0*~0R#|0U~dXkOJU?y zOQo)A7`orh|I7b%zq7}``o&@~(37u6YqT-shU&HWQLELQiz4TOvP4RH`1Vk;x+sdO zu~R(?oU2#6ovT&5gP>OJ3IbP+l&U8!6h8$v=4?)JR7qo zPhFu@2dK9>Ye7D z)3$EA|EQE>g)@cMV!#XTb)^?I$1U%uQf(VGS6AO~V{Y6{xM^q0Kd1hN}xla~9ua8aMk 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 T|2yHVU>^`b009IxEAamTksaGi literal 0 HcmV?d00001