diff --git a/CMakeLists.txt b/CMakeLists.txt index d4775cce..d3d08b64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,6 +125,7 @@ set(SOURCES src/ServerShell.cc src/ServerState.cc src/ShellCommands.cc + src/ShopRandomSets.cc src/SignalWatcher.cc src/StaticGameData.cc src/TeamIndex.cc diff --git a/src/CommonItemSet.cc b/src/CommonItemSet.cc index 47c16f10..85077906 100644 --- a/src/CommonItemSet.cc +++ b/src/CommonItemSet.cc @@ -950,94 +950,3 @@ JSONCommonItemSet::JSONCommonItemSet(const phosg::JSON& json) { } } } - -RELFileSet::RELFileSet(std::shared_ptr data) : data(data), r(*this->data) {} - -ArmorRandomSet::ArmorRandomSet(std::shared_ptr data) : RELFileSet(data) { - // For some reason the footer tables are doubly indirect in this file - uint32_t specs_offset_offset = this->r.pget_u32b(data->size() - 0x10); - uint32_t specs_offset = this->r.pget_u32b(specs_offset_offset); - this->tables = &this->r.pget>(specs_offset); -} - -std::pair -ArmorRandomSet::get_armor_table(size_t index) const { - return this->get_table(this->tables->at(0), index); -} - -std::pair -ArmorRandomSet::get_shield_table(size_t index) const { - return this->get_table(this->tables->at(1), index); -} - -std::pair -ArmorRandomSet::get_unit_table(size_t index) const { - return this->get_table(this->tables->at(2), index); -} - -ToolRandomSet::ToolRandomSet(std::shared_ptr data) : RELFileSet(data) { - uint32_t specs_offset = r.pget_u32b(data->size() - 0x10); - this->common_recovery_table_spec = &r.pget(r.pget_u32b(specs_offset)); - this->rare_recovery_table_spec = &r.pget(r.pget_u32b(specs_offset + sizeof(uint32_t)), 2 * sizeof(TableSpec)); - this->tech_disk_table_spec = this->rare_recovery_table_spec + 1; - this->tech_disk_level_table_spec = &r.pget(r.pget_u32b(specs_offset + 2 * sizeof(uint32_t))); -} - -std::pair ToolRandomSet::get_common_recovery_table(size_t index) const { - return this->get_table(*this->common_recovery_table_spec, index); -} - -std::pair -ToolRandomSet::get_rare_recovery_table(size_t index) const { - return this->get_table(*this->rare_recovery_table_spec, index); -} - -std::pair -ToolRandomSet::get_tech_disk_table(size_t index) const { - return this->get_table(*this->tech_disk_table_spec, index); -} - -std::pair -ToolRandomSet::get_tech_disk_level_table(size_t index) const { - return this->get_table(*this->tech_disk_level_table_spec, index); -} - -WeaponRandomSet::WeaponRandomSet(std::shared_ptr data) : RELFileSet(data) { - uint32_t offsets_offset = this->r.pget_u32b(data->size() - 0x10); - this->offsets = &this->r.pget(offsets_offset); -} - -std::pair -WeaponRandomSet::get_weapon_type_table(size_t index) const { - const auto& spec = this->r.pget(this->offsets->weapon_type_table + index * sizeof(TableSpec)); - const auto* data = &this->r.pget(spec.offset, spec.entries_per_table * sizeof(WeightTableEntry8)); - return std::make_pair(data, spec.entries_per_table); -} - -const parray* -WeaponRandomSet::get_bonus_type_table(size_t which, size_t index) const { - uint32_t base_offset = which ? this->offsets->bonus_type_table2 : this->offsets->bonus_type_table1; - return &this->r.pget>(base_offset + sizeof(parray) * index); -} - -const WeaponRandomSet::RangeTableEntry* -WeaponRandomSet::get_bonus_range(size_t which, size_t index) const { - uint32_t base_offset = which ? this->offsets->bonus_range_table2 : this->offsets->bonus_range_table1; - return &this->r.pget(base_offset + sizeof(RangeTableEntry) * index); -} - -const parray* -WeaponRandomSet::get_special_mode_table(size_t index) const { - return &this->r.pget>( - this->offsets->special_mode_table + sizeof(parray) * index); -} - -const WeaponRandomSet::RangeTableEntry* -WeaponRandomSet::get_standard_grind_range(size_t index) const { - return &this->r.pget(this->offsets->standard_grind_range_table + sizeof(RangeTableEntry) * index); -} - -const WeaponRandomSet::RangeTableEntry* -WeaponRandomSet::get_favored_grind_range(size_t index) const { - return &this->r.pget(this->offsets->favored_grind_range_table + sizeof(RangeTableEntry) * index); -} diff --git a/src/CommonItemSet.hh b/src/CommonItemSet.hh index b1c5e894..e226f676 100644 --- a/src/CommonItemSet.hh +++ b/src/CommonItemSet.hh @@ -284,114 +284,3 @@ class JSONCommonItemSet : public CommonItemSet { public: explicit JSONCommonItemSet(const phosg::JSON& json); }; - -class RELFileSet { -public: - template - struct WeightTableEntry { - ValueT value; - WeightT weight; - phosg::JSON json() const { - return phosg::JSON::dict({{"Weight", this->weight}, {"Value", this->value}}); - } - static WeightTableEntry from_json(const phosg::JSON& json) { - return WeightTableEntry{json.get_int("Weight"), json.get_int("Value")}; - } - } __attribute__((packed)); - using WeightTableEntry8 = WeightTableEntry; - using WeightTableEntry32 = WeightTableEntry; - check_struct_size(WeightTableEntry8, 2); - check_struct_size(WeightTableEntry32, 8); - -protected: - std::shared_ptr data; - phosg::StringReader r; - - struct TableSpec { - be_uint32_t offset; - uint8_t entries_per_table; - parray unused; - } __packed_ws__(TableSpec, 8); - - RELFileSet(std::shared_ptr data); - - template - std::pair get_table(const TableSpec& spec, size_t index) const { - const T* entries = &r.pget( - spec.offset + index * spec.entries_per_table * sizeof(T), spec.entries_per_table * sizeof(T)); - return std::make_pair(entries, spec.entries_per_table); - } -}; - -class ArmorRandomSet : public RELFileSet { -public: - // This class parses and accesses data from ArmorRandom.rel - ArmorRandomSet(std::shared_ptr data); - - std::pair get_armor_table(size_t index) const; - std::pair get_shield_table(size_t index) const; - std::pair get_unit_table(size_t index) const; - -private: - const parray* tables; -}; - -class ToolRandomSet : public RELFileSet { -public: - // This class parses and accesses data from ToolRandom.rel - ToolRandomSet(std::shared_ptr data); - - struct TechDiskLevelEntry { - enum class Mode : uint8_t { - LEVEL_1 = 0, - PLAYER_LEVEL_DIVISOR = 1, - RANDOM_IN_RANGE = 2, - }; - Mode mode; - uint8_t player_level_divisor_or_min_level; - uint8_t max_level; - } __packed_ws__(TechDiskLevelEntry, 3); - - std::pair get_common_recovery_table(size_t index) const; - std::pair get_rare_recovery_table(size_t index) const; - std::pair get_tech_disk_table(size_t index) const; - std::pair get_tech_disk_level_table(size_t index) const; - -private: - const TableSpec* common_recovery_table_spec; - const TableSpec* rare_recovery_table_spec; - const TableSpec* tech_disk_table_spec; - const TableSpec* tech_disk_level_table_spec; -}; - -class WeaponRandomSet : public RELFileSet { -public: - // This class parses and accesses data from WeaponRandom*.rel - WeaponRandomSet(std::shared_ptr data); - - struct RangeTableEntry { - be_uint32_t min; - be_uint32_t max; - } __packed_ws__(RangeTableEntry, 8); - - std::pair get_weapon_type_table(size_t index) const; - const parray* get_bonus_type_table(size_t which, size_t index) const; - const RangeTableEntry* get_bonus_range(size_t which, size_t index) const; - const parray* get_special_mode_table(size_t index) const; - const RangeTableEntry* get_standard_grind_range(size_t index) const; - const RangeTableEntry* get_favored_grind_range(size_t index) const; - -private: - struct Offsets { - be_uint32_t weapon_type_table; // [{c, o -> (table)}](10) - be_uint32_t bonus_type_table1; // [[{u32 value, u32 weight}](6)](9) - be_uint32_t bonus_type_table2; // [[{u32 value, u32 weight}](6)](9) - be_uint32_t bonus_range_table1; // [{u32 min_index, u32 max_index}](9) - be_uint32_t bonus_range_table2; // [{u32 min_index, u32 max_index}](9) - be_uint32_t special_mode_table; // [[{u32 value, u32 weight}](3)](8) - be_uint32_t standard_grind_range_table; // [{u32 min, u32 max}](6) - be_uint32_t favored_grind_range_table; // [{u32 min, u32 max}](6) - } __packed_ws__(Offsets, 0x20); - - const Offsets* offsets; -}; diff --git a/src/ItemCreator.cc b/src/ItemCreator.cc index 81b6b765..43f9b1a9 100644 --- a/src/ItemCreator.cc +++ b/src/ItemCreator.cc @@ -15,6 +15,23 @@ struct ProbabilityTable { ProbabilityTable() : count(0) {} + ProbabilityTable(const std::vector>& table) : ProbabilityTable() { + for (const auto& entry : table) { + for (size_t y = 0; y < entry.weight; y++) { + this->push(entry.value); + } + } + } + + template + ProbabilityTable(const std::array, Count>& table) : ProbabilityTable() { + for (const auto& entry : table) { + for (size_t y = 0; y < entry.weight; y++) { + this->push(entry.value); + } + } + } + void push(ItemT item) { if (this->count == MaxCount) { throw std::runtime_error("push to full probability table"); @@ -52,9 +69,9 @@ struct ProbabilityTable { ItemCreator::ItemCreator( 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 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, std::shared_ptr stack_limits, @@ -1080,13 +1097,7 @@ void ItemCreator::generate_armor_shop_armors(std::vector& shop, Episod } size_t table_index = this->get_table_index_for_armor_shop(player_level); - ProbabilityTable pt; - auto src_table = this->armor_random_set->get_armor_table(table_index); - for (size_t z = 0; z < src_table.second; z++) { - for (size_t y = 0; y < src_table.first[z].weight; y++) { - pt.push(src_table.first[z].value); - } - } + ProbabilityTable pt{this->armor_random_set->armor_table.at(table_index)}; pt.shuffle(this->rand_crypt); for (size_t items_generated = 0; items_generated < num_items;) { @@ -1124,13 +1135,7 @@ void ItemCreator::generate_armor_shop_shields(std::vector& shop, size_ } size_t table_index = this->get_table_index_for_armor_shop(player_level); - ProbabilityTable pt; - auto src_table = this->armor_random_set->get_shield_table(table_index); - for (size_t z = 0; z < src_table.second; z++) { - for (size_t y = 0; y < src_table.first[z].weight; y++) { - pt.push(src_table.first[z].value); - } - } + ProbabilityTable pt{this->armor_random_set->shield_table.at(table_index)}; pt.shuffle(this->rand_crypt); for (size_t items_generated = 0; items_generated < num_items;) { @@ -1167,13 +1172,7 @@ void ItemCreator::generate_armor_shop_units(std::vector& shop, size_t } size_t table_index = this->get_table_index_for_armor_shop(player_level); - ProbabilityTable pt; - auto src_table = this->armor_random_set->get_unit_table(table_index); - for (size_t z = 0; z < src_table.second; z++) { - for (size_t y = 0; y < src_table.first[z].weight; y++) { - pt.push(src_table.first[z].value); - } - } + ProbabilityTable pt{this->armor_random_set->unit_table.at(table_index)}; pt.shuffle(this->rand_crypt); for (size_t items_generated = 0; items_generated < num_items;) { @@ -1211,10 +1210,6 @@ size_t ItemCreator::get_table_index_for_tool_shop(size_t player_level) { } } -static const std::vector> tool_item_defs{ - {0x00, 0x00}, {0x00, 0x01}, {0x00, 0x02}, {0x01, 0x00}, {0x01, 0x01}, {0x01, 0x02}, {0x06, 0x00}, {0x06, 0x01}, - {0x03, 0x00}, {0x04, 0x00}, {0x05, 0x00}, {0x07, 0x00}, {0x08, 0x00}, {0x09, 0x00}, {0x0A, 0x00}, {0xFF, 0xFF}}; - void ItemCreator::generate_common_tool_shop_recovery_items(std::vector& shop, size_t player_level) { size_t table_index; if (player_level < 11) { @@ -1231,17 +1226,15 @@ void ItemCreator::generate_common_tool_shop_recovery_items(std::vector table_index = 5; } - auto table = this->tool_random_set->get_common_recovery_table(table_index); - for (size_t z = 0; z < table.second; z++) { - uint8_t type = table.first[z]; - if (type == 0x0F) { + for (const auto& entry : this->tool_random_set->common_recovery_table.at(table_index)) { + if (entry == 0x0F) { continue; } auto& item = shop.emplace_back(); item.data1[0] = 3; - item.data1[1] = tool_item_defs[type].first; - item.data1[2] = tool_item_defs[type].second; + item.data1[1] = ToolShopRandomSet::item_defs[entry].first; + item.data1[2] = ToolShopRandomSet::item_defs[entry].second; } } @@ -1251,15 +1244,8 @@ void ItemCreator::generate_rare_tool_shop_recovery_items(std::vector& } static constexpr size_t num_items = 2; - ProbabilityTable pt; size_t table_index = this->get_table_index_for_tool_shop(player_level); - auto table = this->tool_random_set->get_rare_recovery_table(table_index); - for (size_t z = 0; z < table.second; z++) { - const auto& e = table.first[z]; - for (size_t y = 0; y < e.weight; y++) { - pt.push(e.value); - } - } + ProbabilityTable pt{this->tool_random_set->rare_recovery_table.at(table_index)}; pt.shuffle(this->rand_crypt); size_t effective_num_items = num_items; @@ -1273,8 +1259,8 @@ void ItemCreator::generate_rare_tool_shop_recovery_items(std::vector& } else { ItemData item; item.data1[0] = 3; - item.data1[1] = tool_item_defs[type].first; - item.data1[2] = tool_item_defs[type].second; + item.data1[1] = ToolShopRandomSet::item_defs[type].first; + item.data1[2] = ToolShopRandomSet::item_defs[type].second; if (this->shop_does_not_contain_duplicate_item_by_data1_0_1_2(shop, item)) { shop.emplace_back(std::move(item)); items_generated++; @@ -1294,28 +1280,16 @@ void ItemCreator::generate_tool_shop_tech_disks(std::vector& shop, siz } size_t table_index = this->get_table_index_for_tool_shop(player_level); - auto table = this->tool_random_set->get_tech_disk_table(table_index); - - ProbabilityTable pt; - for (size_t z = 0; z < table.second; z++) { - const auto& e = table.first[z]; - for (size_t y = 0; y < e.weight; y++) { - pt.push(e.value); - } - } + ProbabilityTable pt{this->tool_random_set->tech_disk_table.at(table_index)}; pt.shuffle(this->rand_crypt); - static const std::array tech_num_map = { - 0x00, 0x03, 0x06, 0x0F, 0x10, 0x0D, 0x0A, 0x0B, 0x0C, 0x01, 0x04, 0x07, - 0x0E, 0x11, 0x02, 0x05, 0x08, 0x09, 0x12}; - size_t items_generated = 0; while (items_generated < num_items) { uint8_t tech_num_index = pt.pop(); ItemData item; item.data1[0] = 3; item.data1[1] = 2; - item.data1[4] = tech_num_map.at(tech_num_index); + item.data1[4] = ToolShopRandomSet::tech_num_map.at(tech_num_index); this->choose_tech_disk_level_for_tool_shop(item, player_level, tech_num_index); if (this->shop_does_not_contain_duplicate_tech_disk(shop, item)) { shop.emplace_back(std::move(item)); @@ -1326,22 +1300,22 @@ void ItemCreator::generate_tool_shop_tech_disks(std::vector& shop, siz void ItemCreator::choose_tech_disk_level_for_tool_shop(ItemData& item, size_t player_level, uint8_t tech_num_index) { size_t table_index = this->get_table_index_for_tool_shop(player_level); - auto table = this->tool_random_set->get_tech_disk_level_table(table_index); - if (tech_num_index >= table.second) { + auto table = this->tool_random_set->tech_disk_level_table.at(table_index); + if (tech_num_index >= table.size()) { throw std::runtime_error("technique number out of range"); } - const auto& e = table.first[tech_num_index]; + const auto& e = table[tech_num_index]; switch (e.mode) { - case ToolRandomSet::TechDiskLevelEntry::Mode::LEVEL_1: + case ToolShopRandomSet::TechDiskLevelEntry::Mode::LEVEL_1: item.data1[2] = 0; break; - case ToolRandomSet::TechDiskLevelEntry::Mode::PLAYER_LEVEL_DIVISOR: + case ToolShopRandomSet::TechDiskLevelEntry::Mode::PLAYER_LEVEL_DIVISOR: item.data1[2] = std::clamp( (std::min(player_level, 99) / e.player_level_divisor_or_min_level) - 1, 0, 14); break; - case ToolRandomSet::TechDiskLevelEntry::Mode::RANDOM_IN_RANGE: { - // Note: This logic does not give a uniform distribution - if the minimumlevel is not zero (level 1), then the + case ToolShopRandomSet::TechDiskLevelEntry::Mode::RANDOM_IN_RANGE: { + // Note: This logic does not give a uniform distribution - if the minimum level is not zero (level 1), then the // minimum level is more likely than all the other levels. This behavior matches the client's logic, though it's // unclear if this nonuniformity was intentional. int16_t min_level = std::max(e.player_level_divisor_or_min_level - 1, 0); @@ -1394,119 +1368,25 @@ std::vector ItemCreator::generate_weapon_shop_contents(size_t player_l } } - ProbabilityTable pt; - auto table = this->weapon_random_set->get_weapon_type_table(table_index); - for (size_t z = 0; z < table.second; z++) { - const auto& e = table.first[z]; - for (size_t y = 0; y < e.weight; y++) { - pt.push(e.value); - } - } + ProbabilityTable pt{this->weapon_random_set->weapon_type_weight_tables.at(table_index).at(section_id)}; pt.shuffle(this->rand_crypt); std::vector shop; while (shop.size() < num_items) { ItemData item; + const std::pair* def; uint8_t which = pt.pop(); if (which == 0x39) { - static const std::vector> defs{ - {0x28, 0x00}, {0x2A, 0x00}, {0x2B, 0x00}, {0x35, 0x00}, {0x52, 0x00}, {0x48, 0x00}, {0x64, 0x00}, - {0x59, 0x00}, {0x8A, 0x00}, {0x99, 0x00}}; - const auto& def = defs.at(this->section_id); - item.data1[0] = 0; - item.data1[1] = def.first; - item.data1[2] = def.second; - + def = &WeaponShopRandomSet::type_defs_39.at(this->section_id); } else if (which == 0x3A) { - static const std::vector> defs{ - {0x99, 0x00}, {0x64, 0x00}, {0x8A, 0x00}, {0x28, 0x00}, {0x59, 0x00}, {0x2B, 0x00}, {0x52, 0x00}, - {0x2A, 0x00}, {0x48, 0x00}, {0x35, 0x00}}; - const auto& def = defs.at(this->section_id); - item.data1[0] = 0; - item.data1[1] = def.first; - item.data1[2] = def.second; - + def = &WeaponShopRandomSet::type_defs_3A.at(this->section_id); } else { - static const std::vector> defs({ - /* 00 */ {0x01, 0x00}, - /* 01 */ {0x01, 0x01}, - /* 02 */ {0x01, 0x02}, - /* 03 */ {0x01, 0x03}, - /* 04 */ {0x01, 0x04}, - /* 05 */ {0x03, 0x00}, - /* 06 */ {0x03, 0x01}, - /* 07 */ {0x03, 0x02}, - /* 08 */ {0x03, 0x03}, - /* 09 */ {0x03, 0x04}, - /* 0A */ {0x02, 0x00}, - /* 0B */ {0x02, 0x01}, - /* 0C */ {0x02, 0x02}, - /* 0D */ {0x02, 0x03}, - /* 0E */ {0x02, 0x04}, - /* 0F */ {0x05, 0x00}, - /* 10 */ {0x05, 0x01}, - /* 11 */ {0x05, 0x02}, - /* 12 */ {0x05, 0x03}, - /* 13 */ {0x05, 0x04}, - /* 14 */ {0x04, 0x00}, - /* 15 */ {0x04, 0x01}, - /* 16 */ {0x04, 0x02}, - /* 17 */ {0x04, 0x03}, - /* 18 */ {0x04, 0x04}, - /* 19 */ {0x06, 0x00}, - /* 1A */ {0x06, 0x01}, - /* 1B */ {0x06, 0x02}, - /* 1C */ {0x06, 0x03}, - /* 1D */ {0x06, 0x04}, - /* 1E */ {0x07, 0x00}, - /* 1F */ {0x07, 0x01}, - /* 20 */ {0x07, 0x02}, - /* 21 */ {0x07, 0x03}, - /* 22 */ {0x07, 0x04}, - /* 23 */ {0x08, 0x00}, - /* 24 */ {0x08, 0x01}, - /* 25 */ {0x08, 0x02}, - /* 26 */ {0x08, 0x03}, - /* 27 */ {0x08, 0x04}, - /* 28 */ {0x09, 0x00}, - /* 29 */ {0x09, 0x01}, - /* 2A */ {0x09, 0x02}, - /* 2B */ {0x09, 0x03}, - /* 2C */ {0x09, 0x04}, - /* 2D */ {0x0A, 0x00}, - /* 2E */ {0x0A, 0x01}, - /* 2F */ {0x0A, 0x02}, - /* 30 */ {0x0A, 0x03}, - /* 31 */ {0x0B, 0x00}, - /* 32 */ {0x0B, 0x01}, - /* 33 */ {0x0B, 0x02}, - /* 34 */ {0x0B, 0x03}, - /* 35 */ {0x0C, 0x00}, - /* 36 */ {0x0C, 0x01}, - /* 37 */ {0x0C, 0x02}, - /* 38 */ {0x0C, 0x03}, - /* 39 */ {0xFF, 0xFF}, // Special-cased above - /* 3A */ {0xFF, 0xFF}, // Special-cased above - /* 3B */ {0x01, 0x05}, - /* 3C */ {0x02, 0x05}, - /* 3D */ {0x06, 0x05}, - /* 3E */ {0x08, 0x05}, - /* 3F */ {0x0A, 0x04}, - /* 40 */ {0x0C, 0x04}, - /* 41 */ {0x0B, 0x04}, - /* 42 */ {0x01, 0x06}, - /* 43 */ {0x03, 0x05}, - /* 44 */ {0x07, 0x05}, - /* 45 */ {0x0A, 0x05}, - /* 46 */ {0x0C, 0x05}, - /* 47 */ {0x0B, 0x05}, - }); - const auto& def = defs.at(which); - item.data1[0] = 0; - item.data1[1] = def.first; - item.data1[2] = def.second; + def = &WeaponShopRandomSet::type_defs.at(which); } + item.data1[0] = 0; + item.data1[1] = def->first; + item.data1[2] = def->second; this->generate_weapon_shop_item_grind(item, player_level); this->generate_weapon_shop_item_special(item, player_level); @@ -1542,16 +1422,15 @@ void ItemCreator::generate_weapon_shop_item_grind(ItemData& item, size_t player_ uint8_t favored_weapon = TekkerAdjustmentSet::favored_weapon_type_for_section_id(this->section_id); bool is_favored = (favored_weapon != 0xFF) && (item.data1[1] == favored_weapon); - const auto* range = is_favored - ? this->weapon_random_set->get_favored_grind_range(table_index) - : this->weapon_random_set->get_standard_grind_range(table_index); + const auto& range = is_favored + ? this->weapon_random_set->favored_grind_range_table.at(table_index) + : this->weapon_random_set->default_grind_range_table.at(table_index); const auto& weapon_def = this->item_parameter_table->get_weapon(item.data1[1], item.data1[2]); - item.data1[3] = std::clamp(this->rand_int(range->max + 1), range->min, weapon_def.max_grind); + item.data1[3] = std::clamp(this->rand_int(range.max + 1), range.min, weapon_def.max_grind); } void ItemCreator::generate_weapon_shop_item_special(ItemData& item, size_t player_level) { - ProbabilityTable pt; size_t table_index; if (player_level < 11) { @@ -1572,13 +1451,8 @@ void ItemCreator::generate_weapon_shop_item_special(ItemData& item, size_t playe table_index = 7; } - const auto* table = this->weapon_random_set->get_special_mode_table(table_index); - for (size_t z = 0; z < table->size(); z++) { - const auto& e = table->at(z); - for (size_t y = 0; y < e.weight; y++) { - pt.push(e.value); - } - } + ProbabilityTable pt{this->weapon_random_set->special_mode_table.at(table_index)}; + pt.shuffle(this->rand_crypt); // Note: The original code shuffles pt and then pops a single value from it. For simplicity, we just sample a single // value instead. @@ -1597,9 +1471,6 @@ void ItemCreator::generate_weapon_shop_item_special(ItemData& item, size_t playe } } -static const std::array bonus_values = { - -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50}; - void ItemCreator::generate_weapon_shop_item_bonus1(ItemData& item, size_t player_level) { size_t table_index; if (player_level < 4) { @@ -1622,14 +1493,8 @@ void ItemCreator::generate_weapon_shop_item_bonus1(ItemData& item, size_t player table_index = 8; } - const auto* type_table = this->weapon_random_set->get_bonus_type_table(0, table_index); - ProbabilityTable pt; - for (size_t z = 0; z < type_table->size(); z++) { - const auto& e = type_table->at(z); - for (size_t y = 0; y < e.weight; y++) { - pt.push(e.value); - } - } + ProbabilityTable pt{this->weapon_random_set->bonus_type_table1.at(table_index)}; + pt.shuffle(this->rand_crypt); // Note: The original code shuffles pt and then pops a single value from it. For simplicity, we just sample a single // value instead. @@ -1637,8 +1502,8 @@ void ItemCreator::generate_weapon_shop_item_bonus1(ItemData& item, size_t player if (item.data1[6] == 0) { item.data1[7] = 0; } else { - const auto* range = this->weapon_random_set->get_bonus_range(0, table_index); - item.data1[7] = bonus_values.at(std::max(this->rand_int(range->max + 1), range->min)); + const auto& range = this->weapon_random_set->bonus_range_table1.at(table_index); + item.data1[7] = WeaponShopRandomSet::bonus_values.at(std::max(this->rand_int(range.max + 1), range.min)); } } @@ -1664,14 +1529,7 @@ void ItemCreator::generate_weapon_shop_item_bonus2(ItemData& item, size_t player table_index = 8; } - const auto* type_table = this->weapon_random_set->get_bonus_type_table(1, table_index); - ProbabilityTable pt; - for (size_t z = 0; z < type_table->size(); z++) { - const auto& e = type_table->at(z); - for (size_t y = 0; y < e.weight; y++) { - pt.push(e.value); - } - } + ProbabilityTable pt{this->weapon_random_set->bonus_type_table2.at(table_index)}; pt.shuffle(this->rand_crypt); do { @@ -1681,8 +1539,8 @@ void ItemCreator::generate_weapon_shop_item_bonus2(ItemData& item, size_t player if (item.data1[8] == 0) { item.data1[9] = 0; } else { - const auto* range = this->weapon_random_set->get_bonus_range(1, table_index); - item.data1[9] = bonus_values.at(std::max(this->rand_int(range->max + 1), range->min)); + const auto& range = this->weapon_random_set->bonus_range_table2.at(table_index); + item.data1[9] = WeaponShopRandomSet::bonus_values.at(std::max(this->rand_int(range.max + 1), range.min)); } } diff --git a/src/ItemCreator.hh b/src/ItemCreator.hh index d01de519..e476a9db 100644 --- a/src/ItemCreator.hh +++ b/src/ItemCreator.hh @@ -7,6 +7,7 @@ #include "PSOEncryption.hh" #include "PlayerSubordinates.hh" #include "RareItemSet.hh" +#include "ShopRandomSets.hh" #include "StaticGameData.hh" #include "TekkerAdjustmentSet.hh" @@ -20,9 +21,9 @@ public: ItemCreator( 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 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, std::shared_ptr stack_limits, @@ -81,9 +82,9 @@ private: Difficulty difficulty; uint8_t section_id; 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 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; std::shared_ptr common_item_set; diff --git a/src/Main.cc b/src/Main.cc index 47f69374..aae73137 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -2433,7 +2433,7 @@ Action a_encode_mag_metadata_table( Action a_decode_tekker_adjustment_set( "decode-tekker-adjustment-set", "\ - decode-tekker-adjustment-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS...]\n\ + decode-tekker-adjustment-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\ Converts a JudgeItem.rel file into a JSON tekker adjustment set. Use\n\ --big-endian if the .rel file is from PSO GC.\n", +[](phosg::Arguments& args) { @@ -2446,7 +2446,7 @@ Action a_decode_tekker_adjustment_set( Action a_encode_tekker_adjustment_set( "encode-tekker-adjustment-set", "\ - encode-tekker-adjustment-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS...]\n\ + encode-tekker-adjustment-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\ Converts a JSON tekker adjustment set into a JudgeItem.rel file compatible\n\ with the game client. Use --big-endian if the .rel file is for PSO GC.\n", +[](phosg::Arguments& args) { @@ -2454,6 +2454,78 @@ Action a_encode_tekker_adjustment_set( write_output_data(args, table.serialize_binary(args.get("big-endian")), nullptr); }); +Action a_decode_armor_shop_random_set( + "decode-armor-shop-random-set", "\ + decode-armor-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\ + Converts a ArmorRandom.rel file into a JSON armor shop random set. Use\n\ + --big-endian if the .rel file is from PSO GC.\n", + +[](phosg::Arguments& args) { + auto input_data = read_input_data(args); + ArmorShopRandomSet table(input_data, args.get("big-endian")); + auto json = table.json(); + auto serialized = json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS); + write_output_data(args, serialized, nullptr); + }); + +Action a_encode_armor_shop_random_set( + "encode-armor-shop-random-set", "\ + encode-armor-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\ + Converts a JSON armo shop random set into an ArmorRandom.rel file\n\ + compatible with the game client. Use --big-endian if the .rel file is for\n\ + PSO GC.\n", + +[](phosg::Arguments& args) { + ArmorShopRandomSet table(phosg::JSON::parse(read_input_data(args))); + write_output_data(args, table.serialize_binary(args.get("big-endian")), nullptr); + }); + +Action a_decode_tool_shop_random_set( + "decode-tool-shop-random-set", "\ + decode-tool-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\ + Converts a ToolRandom.rel file into a JSON tool shop random set. Use\n\ + --big-endian if the .rel file is from PSO GC.\n", + +[](phosg::Arguments& args) { + auto input_data = read_input_data(args); + ToolShopRandomSet table(input_data, args.get("big-endian")); + auto json = table.json(); + auto serialized = json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS); + write_output_data(args, serialized, nullptr); + }); + +Action a_encode_tool_shop_random_set( + "encode-tool-shop-random-set", "\ + encode-tool-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\ + Converts a JSON armo shop random set into an ToolRandom.rel file\n\ + compatible with the game client. Use --big-endian if the .rel file is for\n\ + PSO GC.\n", + +[](phosg::Arguments& args) { + ToolShopRandomSet table(phosg::JSON::parse(read_input_data(args))); + write_output_data(args, table.serialize_binary(args.get("big-endian")), nullptr); + }); + +Action a_decode_weapon_shop_random_set( + "decode-weapon-shop-random-set", "\ + decode-weapon-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\ + Converts a WeaponRandom.rel file into a JSON weapon shop random set. Use\n\ + --big-endian if the .rel file is from PSO GC.\n", + +[](phosg::Arguments& args) { + auto input_data = read_input_data(args); + WeaponShopRandomSet table(input_data, args.get("big-endian")); + auto json = table.json(); + auto serialized = json.serialize(phosg::JSON::SerializeOption::FORMAT | phosg::JSON::SerializeOption::SORT_DICT_KEYS); + write_output_data(args, serialized, nullptr); + }); + +Action a_encode_weapon_shop_random_set( + "encode-weapon-shop-random-set", "\ + encode-weapon-shop-random-set [INPUT-FILENAME [OUTPUT-FILENAME]] [OPTIONS]\n\ + Converts a JSON armo shop random set into an WeaponRandom.rel file\n\ + compatible with the game client. Use --big-endian if the .rel file is for\n\ + PSO GC.\n", + +[](phosg::Arguments& args) { + WeaponShopRandomSet table(phosg::JSON::parse(read_input_data(args))); + write_output_data(args, table.serialize_binary(args.get("big-endian")), nullptr); + }); + Action a_decode_level_table( "decode-level-table", nullptr, +[](phosg::Arguments& args) { @@ -2704,7 +2776,7 @@ Action a_name_all_items( Action a_print_level_stats( "show-level-tables", "\ show-level-tables\n\ - Print the level tables for each version in a semi-human-reatable format.\n", + Print the level tables for each version in a semi-human-readable format.\n", +[](phosg::Arguments& args) { auto s = std::make_shared(get_config_filename(args)); s->load_config_early(); @@ -2790,7 +2862,7 @@ Action a_print_level_stats( Action a_show_item_parameter_tables( "show-item-parameter-tables", "\ show-item-parameter-tables\n\ - Print the item parameter tables for each version in a semi-human-reatable\n\ + Print the item parameter tables for each version in a semi-human-readable\n\ format.\n", +[](phosg::Arguments& args) { auto s = std::make_shared(get_config_filename(args)); @@ -2804,6 +2876,27 @@ Action a_show_item_parameter_tables( } }); +Action a_show_shop_random_sets( + "show-shop-random-sets", "\ + show-shop-random-sets\n\ + Print the tekker and shop generation tables in a semi-human-readable\n\ + format.\n", + +[](phosg::Arguments& args) { + auto s = std::make_shared(get_config_filename(args)); + s->load_all(false); + s->tekker_adjustment_set->print(stdout); + s->armor_random_set->print(stdout); + s->tool_random_set->print(stdout); + phosg::fwrite_fmt(stdout, "(Normal) "); + s->weapon_random_set(Difficulty::NORMAL)->print(stdout); + phosg::fwrite_fmt(stdout, "(Hard) "); + s->weapon_random_set(Difficulty::HARD)->print(stdout); + phosg::fwrite_fmt(stdout, "(Very Hard) "); + s->weapon_random_set(Difficulty::VERY_HARD)->print(stdout); + phosg::fwrite_fmt(stdout, "(Ultimate) "); + s->weapon_random_set(Difficulty::ULTIMATE)->print(stdout); + }); + Action a_show_ep3_cards( "show-ep3-cards", "\ show-ep3-cards\n\ diff --git a/src/ServerState.cc b/src/ServerState.cc index 03eff3f9..44f00d67 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -2108,24 +2108,24 @@ void ServerState::load_drop_tables() { } config_log.info_f("Loading armor table"); - auto armor_data = std::make_shared(phosg::load_file("system/tables/ArmorRandom-gc-v3.rel")); - auto new_armor_random_set = std::make_shared(armor_data); + auto armor_json = phosg::JSON::parse(phosg::load_file("system/tables/armor-shop-random-set.json")); + auto new_armor_random_set = std::make_shared(armor_json); config_log.info_f("Loading tool table"); - auto tool_data = std::make_shared(phosg::load_file("system/tables/ToolRandom-gc-v3.rel")); - auto new_tool_random_set = std::make_shared(tool_data); + auto tool_json = phosg::JSON::parse(phosg::load_file("system/tables/tool-shop-random-set.json")); + auto new_tool_random_set = std::make_shared(tool_json); config_log.info_f("Loading weapon tables"); - std::array, 4> new_weapon_random_sets; + std::array, 4> new_weapon_random_sets; const char* filenames[4] = { - "system/tables/WeaponRandomNormal-gc-v3.rel", - "system/tables/WeaponRandomHard-gc-v3.rel", - "system/tables/WeaponRandomVeryHard-gc-v3.rel", - "system/tables/WeaponRandomUltimate-gc-v3.rel", + "system/tables/weapon-shop-random-set-normal.json", + "system/tables/weapon-shop-random-set-hard.json", + "system/tables/weapon-shop-random-set-very-hard.json", + "system/tables/weapon-shop-random-set-ultimate.json", }; for (size_t z = 0; z < 4; z++) { - auto weapon_data = std::make_shared(phosg::load_file(filenames[z])); - new_weapon_random_sets[z] = std::make_shared(weapon_data); + new_weapon_random_sets[z] = std::make_shared( + phosg::JSON::parse(phosg::load_file(filenames[z]))); } config_log.info_f("Loading tekker adjustment set"); diff --git a/src/ServerState.hh b/src/ServerState.hh index ad2145e1..b51ff0eb 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -27,6 +27,7 @@ #include "MagMetadataTable.hh" #include "Menu.hh" #include "Quest.hh" +#include "ShopRandomSets.hh" #include "TeamIndex.hh" #include "TekkerAdjustmentSet.hh" #include "WordSelectTable.hh" @@ -213,9 +214,9 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr bb_data_gsl; std::unordered_map> common_item_sets; std::unordered_map> rare_item_sets; - std::shared_ptr armor_random_set; - std::shared_ptr tool_random_set; - std::array, 4> weapon_random_sets; // Keyed oin difficulty + std::shared_ptr armor_random_set; + std::shared_ptr tool_random_set; + std::array, 4> weapon_random_sets; // Keyed on difficulty std::shared_ptr tekker_adjustment_set; std::array, NUM_VERSIONS> item_parameter_tables; std::shared_ptr item_translation_table; @@ -364,7 +365,7 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr set_data_table(Version version, Episode episode, GameMode mode, Difficulty difficulty) const; - inline std::shared_ptr weapon_random_set(Difficulty difficulty) const { + inline std::shared_ptr weapon_random_set(Difficulty difficulty) const { return this->weapon_random_sets.at(static_cast(difficulty)); } inline std::shared_ptr rare_enemy_rates(Difficulty difficulty) const { diff --git a/src/ShopRandomSets.cc b/src/ShopRandomSets.cc new file mode 100644 index 00000000..ef3bf273 --- /dev/null +++ b/src/ShopRandomSets.cc @@ -0,0 +1,834 @@ +#include "ShopRandomSets.hh" + +#include "CommonFileFormats.hh" +#include "StaticGameData.hh" +#include "Types.hh" + +template +struct TableSpecT { + U32T offset; + uint8_t row_size; + parray unused; +} __packed_ws_be__(TableSpecT, 8); + +template +void print_table_2d(FILE* stream, const std::vector>& table) { + for (size_t z = 0; z < table.size(); z++) { + phosg::fwrite_fmt(stream, " {:02X}:", z); + for (const auto& cell : table[z]) { + if constexpr (std::is_integral_v) { + phosg::fwrite_fmt(stream, " {:02X}", cell); + } else { + // It should be ShopRandomSetBase::IntPairT<...> + phosg::fwrite_fmt(stream, " {:02X} @ {:03}", cell.first, cell.second); + } + } + phosg::fwrite_fmt(stream, "\n"); + } +} + +template +std::vector> parse_table_t( + const phosg::StringReader& r, uint32_t offset, size_t row_size, const std::set start_offsets) { + auto end_offset_it = start_offsets.upper_bound(offset); + if (end_offset_it == start_offsets.end()) { + throw std::runtime_error("Cannot determine table end offset"); + } + uint32_t end_offset = *end_offset_it; + + size_t row_bytes = row_size * sizeof(StoredT); + size_t row_count = (end_offset - offset) / row_bytes; + auto sub_r = r.sub(offset, row_bytes * row_count); + + std::vector> ret; + while (ret.size() < row_count) { + auto& row = ret.emplace_back(); + while (row.size() < row_size) { + row.emplace_back(sub_r.get()); + } + } + return ret; +} + +template +TableSpecT serialize_table_t(RELFileWriter& rel, const std::vector>& table) { + if (table.empty()) { + throw std::runtime_error("Table is empty"); + } + + TableSpecT ret; + ret.offset = rel.w.size(); + ret.row_size = table[0].size(); + for (const auto& row : table) { + if (row.size() != ret.row_size) { + throw std::runtime_error("Table has different row sizes"); + } + for (const auto& cell : row) { + rel.template put(cell); + } + } + return ret; +} + +template +std::array, RowCount> parse_fixed_table_t(const phosg::StringReader& r, uint32_t offset) { + auto sub_r = r.sub(offset, RowSize * RowCount * sizeof(StoredT)); + std::array, RowCount> ret; + for (size_t y = 0; y < RowCount; y++) { + for (size_t x = 0; x < RowSize; x++) { + ret[y][x] = sub_r.get(); + } + } + return ret; +} + +template +uint32_t serialize_fixed_table_t( + RELFileWriter& rel, const std::array, RowCount>& table) { + uint32_t ret = rel.w.size(); + for (const auto& row : table) { + for (const auto& cell : row) { + rel.template put(cell); + } + } + return ret; +} + +template +std::vector> table_for_json_t(const phosg::JSON& table_json) { + std::vector> ret; + for (const auto& row_json : table_json.as_list()) { + auto& row = ret.emplace_back(); + for (const auto& cell_json : row_json->as_list()) { + if constexpr (std::is_integral_v) { + row.emplace_back(cell_json->as_int()); + } else { + row.emplace_back(*cell_json); + } + } + } + return ret; +} + +template +phosg::JSON json_for_table_t(const std::vector>& table) { + auto table_json = phosg::JSON::list(); + for (const auto& row : table) { + auto row_json = phosg::JSON::list(); + for (const auto& cell : row) { + if constexpr (std::is_integral_v) { + row_json.emplace_back(cell); + } else { + row_json.emplace_back(cell.json()); + } + } + table_json.emplace_back(std::move(row_json)); + } + return table_json; +} + +template +std::array fixed_table_for_json_t(const phosg::JSON& table_json) { + std::array ret; + for (size_t y = 0; y < Count; y++) { + ret[y] = table_json.at(y); + } + return ret; +} + +template +std::array, RowCount> fixed_table_for_json_t(const phosg::JSON& table_json) { + std::array, RowCount> ret; + for (size_t y = 0; y < RowCount; y++) { + const auto& row_json = table_json.at(y); + for (size_t x = 0; x < RowSize; x++) { + ret[y][x] = row_json.at(x); + } + } + return ret; +} + +template +phosg::JSON json_for_fixed_table_t(const std::array& table) { + auto ret = phosg::JSON::list(); + for (const auto& cell : table) { + ret.emplace_back(cell.json()); + } + return ret; +} + +template +phosg::JSON json_for_fixed_table_t(const std::array, RowCount>& table) { + auto table_json = phosg::JSON::list(); + for (const auto& row : table) { + auto row_json = phosg::JSON::list(); + for (const auto& cell : row) { + row_json.emplace_back(cell.json()); + } + table_json.emplace_back(std::move(row_json)); + } + return table_json; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Armor shop set + +template +struct ArmorSubRootT { + TableSpecT armor_table; // -> WeightTableEntry8[...][...] + TableSpecT shield_table; // -> WeightTableEntry8[...][...] + TableSpecT unit_table; // -> WeightTableEntry8[...][...] +} __packed_ws_be__(ArmorSubRootT, 0x18); + +template +struct ArmorRootT { + U32T subroot; // -> ArmorSubRootT +} __packed_ws_be__(ArmorRootT, 4); + +ArmorShopRandomSet::ArmorShopRandomSet(const void* data, size_t size, bool big_endian) { + if (big_endian) { + this->parse_t(data, size); + } else { + this->parse_t(data, size); + } +} + +ArmorShopRandomSet::ArmorShopRandomSet(const std::string& data, bool big_endian) + : ArmorShopRandomSet(data.data(), data.size(), big_endian) {} + +ArmorShopRandomSet::ArmorShopRandomSet(const phosg::JSON& json) { + this->armor_table = table_for_json_t>(json.at("ArmorTable")); + this->shield_table = table_for_json_t>(json.at("ShieldTable")); + this->unit_table = table_for_json_t>(json.at("UnitTable")); +} + +template +void ArmorShopRandomSet::parse_t(const void* data, size_t size) { + std::set start_offsets; + + phosg::StringReader r(data, size); + uint32_t root_offset = r.pget>(size - 0x10); + start_offsets.emplace(root_offset); + const auto& root = r.pget>(root_offset); + start_offsets.emplace(root.subroot); + const auto& subroot = r.pget>(root.subroot); + start_offsets.emplace(subroot.armor_table.offset); + start_offsets.emplace(subroot.shield_table.offset); + start_offsets.emplace(subroot.unit_table.offset); + + this->armor_table = parse_table_t>( + r, subroot.armor_table.offset, subroot.armor_table.row_size, start_offsets); + this->shield_table = parse_table_t>( + r, subroot.shield_table.offset, subroot.shield_table.row_size, start_offsets); + this->unit_table = parse_table_t>( + r, subroot.unit_table.offset, subroot.unit_table.row_size, start_offsets); +} + +template +std::string ArmorShopRandomSet::serialize_binary_t() const { + RELFileWriter rel; + + ArmorSubRootT subroot; + subroot.armor_table = serialize_table_t>(rel, this->armor_table); + subroot.shield_table = serialize_table_t>(rel, this->shield_table); + subroot.unit_table = serialize_table_t>(rel, this->unit_table); + + ArmorRootT root; + rel.align(4); + root.subroot = rel.put(subroot); + rel.relocations.emplace(rel.w.size() - 0x18); + rel.relocations.emplace(rel.w.size() - 0x10); + rel.relocations.emplace(rel.w.size() - 0x08); + + uint32_t root_offset = rel.put(root); + rel.relocations.emplace(rel.w.size() - 4); + + return rel.finalize(root_offset); +} + +std::string ArmorShopRandomSet::serialize_binary(bool big_endian) const { + return big_endian ? this->serialize_binary_t() : this->serialize_binary_t(); +} + +phosg::JSON ArmorShopRandomSet::json() const { + return phosg::JSON::dict({ + {"ArmorTable", json_for_table_t(this->armor_table)}, + {"ShieldTable", json_for_table_t(this->shield_table)}, + {"UnitTable", json_for_table_t(this->unit_table)}, + }); +} + +void ArmorShopRandomSet::print(FILE* stream) const { + phosg::fwrite_fmt(stream, "ArmorShopRandomSet\n"); + phosg::fwrite_fmt(stream, " Armor table\n"); + print_table_2d(stream, this->armor_table); + phosg::fwrite_fmt(stream, " Shield table\n"); + print_table_2d(stream, this->shield_table); + phosg::fwrite_fmt(stream, " Unit table\n"); + print_table_2d(stream, this->unit_table); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tool shop set + +ToolShopRandomSet::TechDiskLevelEntry::TechDiskLevelEntry(const phosg::JSON& json) { + if (json.contains("PlayerLevelDivisor")) { + this->mode = Mode::PLAYER_LEVEL_DIVISOR; + this->player_level_divisor_or_min_level = json.get_int("PlayerLevelDivisor"); + this->max_level = 0; + } else if (json.contains("MinLevel")) { + this->mode = Mode::RANDOM_IN_RANGE; + this->player_level_divisor_or_min_level = json.get_int("MinLevel"); + this->max_level = json.get_int("MaxLevel"); + } else { + this->mode = Mode::LEVEL_1; + this->player_level_divisor_or_min_level = 0; + this->max_level = 0; + } +} + +phosg::JSON ToolShopRandomSet::TechDiskLevelEntry::json() const { + switch (this->mode) { + case Mode::LEVEL_1: + return phosg::JSON::dict(); + case Mode::PLAYER_LEVEL_DIVISOR: + return phosg::JSON::dict({{"PlayerLevelDivisor", this->player_level_divisor_or_min_level}}); + case Mode::RANDOM_IN_RANGE: + return phosg::JSON::dict({{"MinLevel", this->player_level_divisor_or_min_level}, {"MaxLevel", this->max_level}}); + default: + throw std::runtime_error("Invalid TechDiskLevelEntry mode"); + } +} + +template +struct ToolSubRootT { + TableSpecT rare_recovery_table; // -> WeightTableEntry8[...][...] + TableSpecT tech_disk_table; // -> WeightTableEntry8[...][...] +} __packed_ws_be__(ToolSubRootT, 0x10); + +template +struct ToolRootT { + U32T common_recovery_table; // -> TableSpecT -> WeightTableEntry8[...][...] + U32T subroot; // -> ToolSubRootT + U32T tech_disk_level_table; // -> TableSpecT -> TechDiskLevelEntry[...][...] +} __packed_ws_be__(ToolRootT, 0x0C); + +ToolShopRandomSet::ToolShopRandomSet(const void* data, size_t size, bool big_endian) { + if (big_endian) { + this->parse_t(data, size); + } else { + this->parse_t(data, size); + } +} + +const std::vector> ToolShopRandomSet::item_defs{ + {0x00, 0x00}, // 00 -> Monomate + {0x00, 0x01}, // 01 -> Dimate + {0x00, 0x02}, // 02 -> Trimate + {0x01, 0x00}, // 03 -> Monofluid + {0x01, 0x01}, // 04 -> Difluid + {0x01, 0x02}, // 05 -> Trifluid + {0x06, 0x00}, // 06 -> Antidote + {0x06, 0x01}, // 07 -> Antiparalysis + {0x03, 0x00}, // 08 -> Sol Atomizer + {0x04, 0x00}, // 09 -> Moon Atomizer + {0x05, 0x00}, // 0A -> Star Atomizer + {0x07, 0x00}, // 0B -> Telepipe + {0x08, 0x00}, // 0C -> Trap Vision + {0x09, 0x00}, // 0D -> Scape Doll + {0x0A, 0x00}, // 0E -> Monogrinder + {0xFF, 0xFF}, // 0F -> Nothing +}; + +const std::array ToolShopRandomSet::tech_num_map{ + 0x00, 0x03, 0x06, 0x0F, 0x10, 0x0D, 0x0A, 0x0B, 0x0C, 0x01, 0x04, 0x07, 0x0E, 0x11, 0x02, 0x05, 0x08, 0x09, 0x12}; + +ToolShopRandomSet::ToolShopRandomSet(const std::string& data, bool big_endian) + : ToolShopRandomSet(data.data(), data.size(), big_endian) {} + +ToolShopRandomSet::ToolShopRandomSet(const phosg::JSON& json) { + this->common_recovery_table = table_for_json_t(json.at("CommonRecoveryTable")); + this->rare_recovery_table = table_for_json_t>(json.at("RareRecoveryTable")); + this->tech_disk_table = table_for_json_t>(json.at("TechDiskTable")); + this->tech_disk_level_table = table_for_json_t(json.at("TechDiskLevelTable")); +} + +template +void ToolShopRandomSet::parse_t(const void* data, size_t size) { + std::set start_offsets; + + phosg::StringReader r(data, size); + uint32_t root_offset = r.pget>(size - 0x10); + start_offsets.emplace(root_offset); + + const auto& root = r.pget>(root_offset); + start_offsets.emplace(root.common_recovery_table); + start_offsets.emplace(root.subroot); + start_offsets.emplace(root.tech_disk_level_table); + + const auto& common_recovery_table_spec = r.pget>(root.common_recovery_table); + start_offsets.emplace(common_recovery_table_spec.offset); + + const auto& subroot = r.pget>(root.subroot); + start_offsets.emplace(subroot.rare_recovery_table.offset); + start_offsets.emplace(subroot.tech_disk_table.offset); + + const auto& tech_disk_level_table_spec = r.pget>(root.tech_disk_level_table); + start_offsets.emplace(tech_disk_level_table_spec.offset); + + this->common_recovery_table = parse_table_t( + r, common_recovery_table_spec.offset, common_recovery_table_spec.row_size, start_offsets); + this->rare_recovery_table = parse_table_t>( + r, subroot.rare_recovery_table.offset, subroot.rare_recovery_table.row_size, start_offsets); + this->tech_disk_table = parse_table_t>( + r, subroot.tech_disk_table.offset, subroot.tech_disk_table.row_size, start_offsets); + this->tech_disk_level_table = parse_table_t( + r, tech_disk_level_table_spec.offset, tech_disk_level_table_spec.row_size, start_offsets); +} + +template +std::string ToolShopRandomSet::serialize_binary_t() const { + RELFileWriter rel; + + ToolSubRootT subroot; + auto common_recovery_table_spec = serialize_table_t(rel, this->common_recovery_table); + subroot.rare_recovery_table = serialize_table_t>(rel, this->rare_recovery_table); + subroot.tech_disk_table = serialize_table_t>(rel, this->tech_disk_table); + auto tech_disk_level_table_spec = serialize_table_t(rel, this->tech_disk_level_table); + + rel.align(4); + ToolRootT root; + root.subroot = rel.put(subroot); + rel.relocations.emplace(rel.w.size() - 0x10); + rel.relocations.emplace(rel.w.size() - 0x08); + root.common_recovery_table = rel.put(common_recovery_table_spec); + rel.relocations.emplace(rel.w.size() - 0x08); + root.tech_disk_level_table = rel.put(tech_disk_level_table_spec); + rel.relocations.emplace(rel.w.size() - 0x08); + + uint32_t root_offset = rel.put(root); + rel.relocations.emplace(rel.w.size() - 0x0C); + rel.relocations.emplace(rel.w.size() - 0x08); + rel.relocations.emplace(rel.w.size() - 0x04); + + return rel.finalize(root_offset); +} + +std::string ToolShopRandomSet::serialize_binary(bool big_endian) const { + return big_endian ? this->serialize_binary_t() : this->serialize_binary_t(); +} + +phosg::JSON ToolShopRandomSet::json() const { + return phosg::JSON::dict({ + {"CommonRecoveryTable", json_for_table_t(this->common_recovery_table)}, + {"RareRecoveryTable", json_for_table_t(this->rare_recovery_table)}, + {"TechDiskTable", json_for_table_t(this->tech_disk_table)}, + {"TechDiskLevelTable", json_for_table_t(this->tech_disk_level_table)}, + }); +} + +void ToolShopRandomSet::print(FILE* stream) const { + phosg::fwrite_fmt(stream, "ToolShopRandomSet\n"); + + phosg::fwrite_fmt(stream, " Common recovery table\n"); + for (size_t z = 0; z < this->common_recovery_table.size(); z++) { + phosg::fwrite_fmt(stream, " {:02X}:", z); + for (uint8_t cell : this->common_recovery_table[z]) { + if (cell == 0x0F) { + phosg::fwrite_fmt(stream, " {:02X} (------)", cell); + } else { + const auto& def = this->item_defs.at(cell); + phosg::fwrite_fmt(stream, " {:02X} (03{:02X}{:02X})", cell, def.first, def.second); + } + } + phosg::fwrite_fmt(stream, "\n"); + } + + phosg::fwrite_fmt(stream, " Rare recovery table\n"); + for (size_t z = 0; z < this->rare_recovery_table.size(); z++) { + phosg::fwrite_fmt(stream, " {:02X}:", z); + for (const auto& cell : this->rare_recovery_table[z]) { + if (cell.value == 0x0F) { + phosg::fwrite_fmt(stream, " {:02X} (------) @ {:03}", cell.value, cell.weight); + } else { + const auto& def = this->item_defs.at(cell.value); + phosg::fwrite_fmt(stream, " {:02X} (03{:02X}{:02X}) @ {:03}", cell.value, def.first, def.second, cell.weight); + } + } + phosg::fwrite_fmt(stream, "\n"); + } + + phosg::fwrite_fmt(stream, " Tech disk table\n"); + for (size_t z = 0; z < this->tech_disk_table.size(); z++) { + phosg::fwrite_fmt(stream, " {:02X}:", z); + for (const auto& cell : this->tech_disk_table[z]) { + phosg::fwrite_fmt(stream, " {:02X}({:>8}) @ {:03}", + cell.first, name_for_technique(this->tech_num_map.at(cell.value)), cell.second); + } + phosg::fwrite_fmt(stream, "\n"); + } + + phosg::fwrite_fmt(stream, " Tech disk level table\n"); + phosg::fwrite_fmt(stream, " "); + for (const auto& tech_num : this->tech_num_map) { + phosg::fwrite_fmt(stream, " {:<9}", name_for_technique(tech_num)); + } + phosg::fwrite_fmt(stream, "\n"); + for (size_t z = 0; z < this->tech_disk_level_table.size(); z++) { + phosg::fwrite_fmt(stream, " {:02X}:", z); + for (const auto& cell : this->tech_disk_level_table[z]) { + switch (cell.mode) { + case TechDiskLevelEntry::Mode::LEVEL_1: + phosg::fwrite_fmt(stream, " LEVEL_1 "); + break; + case TechDiskLevelEntry::Mode::PLAYER_LEVEL_DIVISOR: + phosg::fwrite_fmt(stream, " PLV / {:03}", cell.player_level_divisor_or_min_level); + break; + case TechDiskLevelEntry::Mode::RANDOM_IN_RANGE: + phosg::fwrite_fmt(stream, " {:03} - {:03}", cell.player_level_divisor_or_min_level, cell.max_level); + break; + } + } + phosg::fwrite_fmt(stream, "\n"); + } + std::vector> tech_disk_level_table; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Weapon shop set + +template +struct RangeTableEntryT { + U32T min; + U32T max; +} __packed_ws_be__(RangeTableEntryT, 8); + +template +struct WeaponRootT { + U32T weapon_type_table; // {c, o -> (table)}[...(offsets)] + U32T bonus_type_table1; // {u32 value, u32 weight}[9][6] + U32T bonus_type_table2; // {u32 value, u32 weight}[9][6] + U32T bonus_range_table1; // {u32 min_index, u32 max_index}[9] + U32T bonus_range_table2; // {u32 min_index, u32 max_index}[9] + U32T special_mode_table; // {u32 value, u32 weight}[8][3] + U32T default_grind_range_table; // {u32 min, u32 max}[6] + U32T favored_grind_range_table; // {u32 min, u32 max}[6] +} __packed_ws_be__(WeaponRootT, 0x20); + +const std::array, 0x48> WeaponShopRandomSet::type_defs({ + /* 00 */ {0x01, 0x00}, // Saber + /* 01 */ {0x01, 0x01}, // Brand + /* 02 */ {0x01, 0x02}, // Buster + /* 03 */ {0x01, 0x03}, // Pallasch + /* 04 */ {0x01, 0x04}, // Gladius + /* 05 */ {0x03, 0x00}, // Dagger + /* 06 */ {0x03, 0x01}, // Knife + /* 07 */ {0x03, 0x02}, // Blade + /* 08 */ {0x03, 0x03}, // Edge + /* 09 */ {0x03, 0x04}, // Ripper + /* 0A */ {0x02, 0x00}, // Sword + /* 0B */ {0x02, 0x01}, // Gigush + /* 0C */ {0x02, 0x02}, // Breaker + /* 0D */ {0x02, 0x03}, // Claymore + /* 0E */ {0x02, 0x04}, // Calibur + /* 0F */ {0x05, 0x00}, // Slicer + /* 10 */ {0x05, 0x01}, // Spinner + /* 11 */ {0x05, 0x02}, // Cutter + /* 12 */ {0x05, 0x03}, // Sawcer + /* 13 */ {0x05, 0x04}, // Diska + /* 14 */ {0x04, 0x00}, // Partisan + /* 15 */ {0x04, 0x01}, // Halbert + /* 16 */ {0x04, 0x02}, // Glaive + /* 17 */ {0x04, 0x03}, // Berdys + /* 18 */ {0x04, 0x04}, // Gungnir + /* 19 */ {0x06, 0x00}, // Handgun + /* 1A */ {0x06, 0x01}, // Autogun + /* 1B */ {0x06, 0x02}, // Lockgun + /* 1C */ {0x06, 0x03}, // Railgun + /* 1D */ {0x06, 0x04}, // Raygun + /* 1E */ {0x07, 0x00}, // Rifle + /* 1F */ {0x07, 0x01}, // Sniper + /* 20 */ {0x07, 0x02}, // Blaster + /* 21 */ {0x07, 0x03}, // Beam + /* 22 */ {0x07, 0x04}, // Laser + /* 23 */ {0x08, 0x00}, // Mechgun + /* 24 */ {0x08, 0x01}, // Assault + /* 25 */ {0x08, 0x02}, // Repeater + /* 26 */ {0x08, 0x03}, // Gatling + /* 27 */ {0x08, 0x04}, // Vulcan + /* 28 */ {0x09, 0x00}, // Shot + /* 29 */ {0x09, 0x01}, // Spread + /* 2A */ {0x09, 0x02}, // Cannon + /* 2B */ {0x09, 0x03}, // Launcher + /* 2C */ {0x09, 0x04}, // Arms + /* 2D */ {0x0A, 0x00}, // Cane + /* 2E */ {0x0A, 0x01}, // Stick + /* 2F */ {0x0A, 0x02}, // Mace + /* 30 */ {0x0A, 0x03}, // Club + /* 31 */ {0x0B, 0x00}, // Rod + /* 32 */ {0x0B, 0x01}, // Pole + /* 33 */ {0x0B, 0x02}, // Pillar + /* 34 */ {0x0B, 0x03}, // Striker + /* 35 */ {0x0C, 0x00}, // Wand + /* 36 */ {0x0C, 0x01}, // Staff + /* 37 */ {0x0C, 0x02}, // Baton + /* 38 */ {0x0C, 0x03}, // Scepter + /* 39 */ {0xFF, 0xFF}, // Special-cased in type_defs_39 (depends on section ID) + /* 3A */ {0xFF, 0xFF}, // Special-cased in type_defs_3A (depends on section ID) + /* 3B */ {0x01, 0x05}, // DB'S SABER + /* 3C */ {0x02, 0x05}, // FLOWEN'S SWORD + /* 3D */ {0x06, 0x05}, // VARISTA + /* 3E */ {0x08, 0x05}, // M&A60 VISE + /* 3F */ {0x0A, 0x04}, // CLUB OF LACONIUM + /* 40 */ {0x0C, 0x04}, // FIRE SCEPTER:AGNI + /* 41 */ {0x0B, 0x04}, // BATTLE VERGE + /* 42 */ {0x01, 0x06}, // KALADBOLG + /* 43 */ {0x03, 0x05}, // BLADE DANCE + /* 44 */ {0x07, 0x05}, // VISK-235W + /* 45 */ {0x0A, 0x05}, // MACE OF ADAMAN + /* 46 */ {0x0C, 0x05}, // ICE STAFF:DAGON + /* 47 */ {0x0B, 0x05}, // BRAVE HAMMER +}); + +const std::array, 10> WeaponShopRandomSet::type_defs_39({ + // Indexed by section_id + {0x28, 0x00}, // HARISEN BATTLE FAN + {0x2A, 0x00}, // AKIKO'S WOK + {0x2B, 0x00}, // TOY HAMMER + {0x35, 0x00}, // CRAZY TUNE + {0x52, 0x00}, // FLOWER CANE + {0x48, 0x00}, // SAMBA MARACAS + {0x64, 0x00}, // CHAMELEON SCYTHE + {0x59, 0x00}, // BROOM + {0x8A, 0x00}, // SANGE + {0x99, 0x00}, // ANGEL HARP +}); + +const std::array, 10> WeaponShopRandomSet::type_defs_3A({ + // Indexed by section_id + {0x99, 0x00}, // ANGEL HARP + {0x64, 0x00}, // CHAMELEON SCYTHE + {0x8A, 0x00}, // SANGE + {0x28, 0x00}, // HARISEN BATTLE FAN + {0x59, 0x00}, // BROOM + {0x2B, 0x00}, // TOY HAMMER + {0x52, 0x00}, // FLOWER CANE + {0x2A, 0x00}, // AKIKO'S WOK + {0x48, 0x00}, // SAMBA MARACAS + {0x35, 0x00}, // CRAZY TUNE +}); + +const std::array WeaponShopRandomSet::bonus_values{ + -50, -45, -40, -35, -30, -25, -20, -15, -10, -5, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50}; + +WeaponShopRandomSet::WeaponShopRandomSet(const void* data, size_t size, bool big_endian) { + if (big_endian) { + this->parse_t(data, size); + } else { + this->parse_t(data, size); + } +} + +WeaponShopRandomSet::WeaponShopRandomSet(const std::string& data, bool big_endian) + : WeaponShopRandomSet(data.data(), data.size(), big_endian) {} + +WeaponShopRandomSet::WeaponShopRandomSet(const phosg::JSON& json) { + for (const auto& it : json.get_list("WeaponTypeWeightTables")) { + this->weapon_type_weight_tables.emplace_back(table_for_json_t>(*it)); + } + this->bonus_type_table1 = fixed_table_for_json_t, 9, 6>(json.at("BonusTypeTable1")); + this->bonus_type_table2 = fixed_table_for_json_t, 9, 6>(json.at("BonusTypeTable2")); + this->bonus_range_table1 = fixed_table_for_json_t, 9>(json.at("BonusRangeTable1")); + this->bonus_range_table2 = fixed_table_for_json_t, 9>(json.at("BonusRangeTable2")); + this->special_mode_table = fixed_table_for_json_t, 8, 3>(json.at("SpecialModeTable")); + this->default_grind_range_table = fixed_table_for_json_t, 6>(json.at("DefaultDringRangeTable")); + this->favored_grind_range_table = fixed_table_for_json_t, 6>(json.at("FavoredDringRangeTable")); +} + +template +void WeaponShopRandomSet::parse_t(const void* data, size_t size) { + std::set start_offsets; + + phosg::StringReader r(data, size); + uint32_t root_offset = r.pget>(size - 0x10); + start_offsets.emplace(root_offset); + + const auto& root = r.pget>(root_offset); + start_offsets.emplace(root.weapon_type_table); + start_offsets.emplace(root.bonus_type_table1); + start_offsets.emplace(root.bonus_type_table2); + start_offsets.emplace(root.bonus_range_table1); + start_offsets.emplace(root.bonus_range_table2); + start_offsets.emplace(root.special_mode_table); + start_offsets.emplace(root.default_grind_range_table); + start_offsets.emplace(root.favored_grind_range_table); + + // Count the weapon types tables + if (start_offsets.upper_bound(root.weapon_type_table) == start_offsets.end()) { + throw std::runtime_error("Weapon type table is out of range"); + } + for (uint32_t offset = root.weapon_type_table; + offset < *start_offsets.upper_bound(root.weapon_type_table); + offset += sizeof(TableSpecT)) { + start_offsets.emplace(r.pget>(offset).offset); + } + size_t num_weapon_types_tables = + (*start_offsets.upper_bound(root.weapon_type_table) - root.weapon_type_table) / sizeof(TableSpecT); + + while (this->weapon_type_weight_tables.size() < num_weapon_types_tables) { + const auto& spec = r.pget>( + root.weapon_type_table + this->weapon_type_weight_tables.size() * sizeof(TableSpecT)); + this->weapon_type_weight_tables.emplace_back(parse_table_t>( + r, spec.offset, spec.row_size, start_offsets)); + } + + auto parse_fixed_table_into_1d = [&](std::array, Count>& ret, uint32_t offset) -> void { + auto sub_r = r.sub(offset, sizeof(IntPairT) * Count); + for (size_t z = 0; z < Count; z++) { + ret[z] = sub_r.get>>(); + } + }; + auto parse_fixed_table_into_2d = [&](std::array, RowSize>, RowCount>& ret, uint32_t offset) -> void { + auto sub_r = r.sub(offset, sizeof(IntPairT) * RowSize * RowCount); + for (size_t y = 0; y < RowCount; y++) { + for (size_t x = 0; x < RowSize; x++) { + ret[y][x] = sub_r.get>>(); + } + } + }; + + parse_fixed_table_into_2d(this->bonus_type_table1, root.bonus_type_table1); + parse_fixed_table_into_2d(this->bonus_type_table2, root.bonus_type_table2); + parse_fixed_table_into_1d(this->bonus_range_table1, root.bonus_range_table1); + parse_fixed_table_into_1d(this->bonus_range_table2, root.bonus_range_table2); + parse_fixed_table_into_2d(this->special_mode_table, root.special_mode_table); + parse_fixed_table_into_1d(this->default_grind_range_table, root.default_grind_range_table); + parse_fixed_table_into_1d(this->favored_grind_range_table, root.favored_grind_range_table); +} + +template +std::string WeaponShopRandomSet::serialize_binary_t() const { + RELFileWriter rel; + + WeaponRootT root; + std::vector> weapon_type_weight_table_specs; + weapon_type_weight_table_specs.reserve(this->weapon_type_weight_tables.size()); + for (const auto& table : this->weapon_type_weight_tables) { + weapon_type_weight_table_specs.emplace_back(serialize_table_t>(rel, table)); + } + + root.weapon_type_table = rel.w.size(); + for (const auto& spec : weapon_type_weight_table_specs) { + rel.put(spec); + rel.relocations.emplace(rel.w.size() - 8); + } + + auto serialize_fixed_table_1d = [&](const std::array, Count>& table) -> uint32_t { + uint32_t ret = rel.w.size(); + for (size_t z = 0; z < Count; z++) { + rel.template put>>(table[z]); + } + return ret; + }; + auto serialize_fixed_table_2d = [&](const std::array, RowSize>, RowCount>& table) -> uint32_t { + uint32_t ret = rel.w.size(); + for (size_t y = 0; y < RowCount; y++) { + for (size_t x = 0; x < RowSize; x++) { + rel.template put>>(table[y][x]); + } + } + return ret; + }; + + rel.align(4); + root.bonus_type_table1 = serialize_fixed_table_2d(this->bonus_type_table1); + root.bonus_type_table2 = serialize_fixed_table_2d(this->bonus_type_table2); + root.special_mode_table = serialize_fixed_table_2d(this->special_mode_table); + root.bonus_range_table1 = serialize_fixed_table_1d(this->bonus_range_table1); + root.bonus_range_table2 = serialize_fixed_table_1d(this->bonus_range_table2); + root.default_grind_range_table = serialize_fixed_table_1d(this->default_grind_range_table); + root.favored_grind_range_table = serialize_fixed_table_1d(this->favored_grind_range_table); + + rel.align(4); + uint32_t root_offset = rel.put(root); + for (size_t z = 1; z <= 8; z++) { + rel.relocations.emplace(rel.w.size() - (z * 4)); + } + + return rel.finalize(root_offset); +} + +std::string WeaponShopRandomSet::serialize_binary(bool big_endian) const { + return big_endian ? this->serialize_binary_t() : this->serialize_binary_t(); +} + +phosg::JSON WeaponShopRandomSet::json() const { + auto weapon_type_weight_tables_json = phosg::JSON::list(); + for (const auto& table : this->weapon_type_weight_tables) { + weapon_type_weight_tables_json.emplace_back(json_for_table_t(table)); + } + + return phosg::JSON::dict({ + {"WeaponTypeWeightTables", std::move(weapon_type_weight_tables_json)}, + {"BonusTypeTable1", json_for_fixed_table_t(this->bonus_type_table1)}, + {"BonusTypeTable2", json_for_fixed_table_t(this->bonus_type_table2)}, + {"BonusRangeTable1", json_for_fixed_table_t(this->bonus_range_table1)}, + {"BonusRangeTable2", json_for_fixed_table_t(this->bonus_range_table2)}, + {"SpecialModeTable", json_for_fixed_table_t(this->special_mode_table)}, + {"DefaultDringRangeTable", json_for_fixed_table_t(this->default_grind_range_table)}, + {"FavoredDringRangeTable", json_for_fixed_table_t(this->favored_grind_range_table)}, + }); +} + +void WeaponShopRandomSet::print(FILE* stream) const { + phosg::fwrite_fmt(stream, "WeaponShopRandomSet\n"); + for (size_t table_index = 0; table_index < this->weapon_type_weight_tables.size(); table_index++) { + phosg::fwrite_fmt(stream, " Weapon type weight table {}\n", table_index); + for (size_t z = 0; z < this->weapon_type_weight_tables[table_index].size(); z++) { + phosg::fwrite_fmt(stream, " {:02X} ({:<10}):", z, name_for_section_id(z)); + for (const auto& cell : this->weapon_type_weight_tables[table_index][z]) { + if (cell.value == 0x39) { + phosg::fwrite_fmt(stream, " {:02X} (SECID1) @ {:03}", cell.value, cell.weight); + } else if (cell.value == 0x3A) { + phosg::fwrite_fmt(stream, " {:02X} (SECID2) @ {:03}", cell.value, cell.weight); + } else { + const auto& def = this->type_defs.at(cell.value); + phosg::fwrite_fmt(stream, " {:02X} (00{:02X}{:02X}) @ {:03}", cell.value, def.first, def.second, cell.weight); + } + } + phosg::fwrite_fmt(stream, "\n"); + } + } + + auto print_fixed_table_1d = [&](const std::array, Count>& table) -> void { + for (size_t z = 0; z < Count; z++) { + const auto& cell = table[z]; + phosg::fwrite_fmt(stream, " {:02}: {:03}-{:03}\n", z, cell.first, cell.second); + } + }; + auto print_fixed_table_2d = [&](const std::array, RowSize>, RowCount>& table) -> void { + for (size_t y = 0; y < RowCount; y++) { + phosg::fwrite_fmt(stream, " {:02X}:", y); + for (size_t x = 0; x < RowSize; x++) { + const auto& cell = table[y][x]; + phosg::fwrite_fmt(stream, " {:02} @ {:03}", cell.first, cell.second); + } + phosg::fwrite_fmt(stream, "\n"); + } + }; + + phosg::fwrite_fmt(stream, " Bonus type table 1\n"); + print_fixed_table_2d(this->bonus_type_table1); + phosg::fwrite_fmt(stream, " Bonus type table 2\n"); + print_fixed_table_2d(this->bonus_type_table2); + phosg::fwrite_fmt(stream, " Bonus range table 1\n"); + print_fixed_table_1d(this->bonus_range_table1); + phosg::fwrite_fmt(stream, " Bonus range table 2\n"); + print_fixed_table_1d(this->bonus_range_table2); + phosg::fwrite_fmt(stream, " Special mode table\n"); + print_fixed_table_2d(this->special_mode_table); + phosg::fwrite_fmt(stream, " Default grind range table\n"); + print_fixed_table_1d(this->default_grind_range_table); + phosg::fwrite_fmt(stream, " Favored grind range table\n"); + print_fixed_table_1d(this->favored_grind_range_table); +} diff --git a/src/ShopRandomSets.hh b/src/ShopRandomSets.hh new file mode 100644 index 00000000..4a0fb991 --- /dev/null +++ b/src/ShopRandomSets.hh @@ -0,0 +1,128 @@ +#pragma once + +#include +#include +#include + +#include "StaticGameData.hh" +#include "Text.hh" +#include "Types.hh" + +struct ShopRandomSetBase { + template + struct IntPairT { + union { + T first; + T value; + T min; + } __attribute__((packed)); + union { + T second; + T weight; + T max; + } __attribute__((packed)); + + IntPairT() = default; + + template + IntPairT(const IntPairT& v) : first(v.first), second(v.second) {} + + IntPairT(const phosg::JSON& v) : first(v.get_int(0)), second(v.get_int(1)) {} + + phosg::JSON json() const { + return phosg::JSON::list({this->first, this->second}); + } + } __attribute__((packed)); +}; + +struct ArmorShopRandomSet : ShopRandomSetBase { + // This struct parses and accesses data from ArmorRandom.rel + ArmorShopRandomSet(const void* data, size_t size, bool big_endian); + ArmorShopRandomSet(const std::string& data, bool big_endian); + explicit ArmorShopRandomSet(const phosg::JSON& json); + + template + void parse_t(const void* data, size_t size); + template + std::string serialize_binary_t() const; + + std::string serialize_binary(bool big_endian) const; + phosg::JSON json() const; + + void print(FILE* stream) const; + + std::vector>> armor_table; + std::vector>> shield_table; + std::vector>> unit_table; +}; + +struct ToolShopRandomSet : ShopRandomSetBase { + // This struct parses and accesses data from ToolRandom.rel + struct TechDiskLevelEntry { + enum class Mode : uint8_t { + LEVEL_1 = 0, + PLAYER_LEVEL_DIVISOR = 1, + RANDOM_IN_RANGE = 2, + }; + Mode mode; + uint8_t player_level_divisor_or_min_level; + uint8_t max_level; + + TechDiskLevelEntry() = default; + explicit TechDiskLevelEntry(const phosg::JSON& json); + phosg::JSON json() const; + } __packed_ws__(TechDiskLevelEntry, 3); + + ToolShopRandomSet(const void* data, size_t size, bool big_endian); + ToolShopRandomSet(const std::string& data, bool big_endian); + explicit ToolShopRandomSet(const phosg::JSON& json); + + template + void parse_t(const void* data, size_t size); + template + std::string serialize_binary_t() const; + + std::string serialize_binary(bool big_endian) const; + phosg::JSON json() const; + + void print(FILE* stream) const; + + static const std::vector> item_defs; + static const std::array tech_num_map; + + std::vector> common_recovery_table; + std::vector>> rare_recovery_table; + std::vector>> tech_disk_table; + std::vector> tech_disk_level_table; +}; + +struct WeaponShopRandomSet : ShopRandomSetBase { + // This struct parses and accesses data from WeaponRandom*.rel + WeaponShopRandomSet(const void* data, size_t size, bool big_endian); + WeaponShopRandomSet(const std::string& data, bool big_endian); + explicit WeaponShopRandomSet(const phosg::JSON& json); + + template + void parse_t(const void* data, size_t size); + template + std::string serialize_binary_t() const; + + std::string serialize_binary(bool big_endian) const; + phosg::JSON json() const; + + void print(FILE* stream) const; + + static const std::array, 0x48> type_defs; + static const std::array, 10> type_defs_39; + static const std::array, 10> type_defs_3A; + static const std::array bonus_values; + + std::vector>>> weapon_type_weight_tables; // [table_index][section_id][entry_index] + std::array, 6>, 9> bonus_type_table1; // [table_index][entry_index] + std::array, 6>, 9> bonus_type_table2; // [table_index][entry_index] + std::array, 9> bonus_range_table1; // [table_index] + std::array, 9> bonus_range_table2; // [table_index] + std::array, 3>, 8> special_mode_table; // [table_index][entry_index] + std::array, 6> default_grind_range_table; // [table_index] + std::array, 6> favored_grind_range_table; // [table_index] +}; diff --git a/src/TekkerAdjustmentSet.cc b/src/TekkerAdjustmentSet.cc index 0358a24d..d37be1f7 100644 --- a/src/TekkerAdjustmentSet.cc +++ b/src/TekkerAdjustmentSet.cc @@ -355,3 +355,36 @@ phosg::JSON TekkerAdjustmentSet::json() const { return ret; } + +void TekkerAdjustmentSet::print(FILE* stream) const { + phosg::fwrite_fmt(stream, "TekkerAdjustmentSet\n"); + + auto print_table = [stream](const std::array& table, const std::unordered_map& luck_table) -> void { + for (size_t section_id = 0; section_id < 10; section_id++) { + phosg::fwrite_fmt(stream, " {:<10}:", name_for_section_id(section_id)); + std::vector> sorted_probs; + for (const auto& [delta, prob] : table[section_id].probs) { + sorted_probs.emplace_back(delta, prob); + } + std::sort(sorted_probs.begin(), sorted_probs.end()); + for (const auto& [delta, prob] : sorted_probs) { + int8_t luck = luck_table.at(delta); + phosg::fwrite_fmt(stream, " {:>2} @ {:>2} ({:>2})", delta, prob, luck); + } + phosg::fwrite_fmt(stream, "\n"); + } + }; + + phosg::fwrite_fmt(stream, " Favored special deltas:\n"); + print_table(this->favored_special_delta_table, this->special_luck_table); + phosg::fwrite_fmt(stream, " Default special deltas:\n"); + print_table(this->default_special_delta_table, this->special_luck_table); + phosg::fwrite_fmt(stream, " Favored grind deltas:\n"); + print_table(this->favored_grind_delta_table, this->grind_luck_table); + phosg::fwrite_fmt(stream, " Default grind deltas:\n"); + print_table(this->default_grind_delta_table, this->grind_luck_table); + phosg::fwrite_fmt(stream, " Favored bonus deltas:\n"); + print_table(this->favored_bonus_delta_table, this->bonus_luck_table); + phosg::fwrite_fmt(stream, " Default bonus deltas:\n"); + print_table(this->default_bonus_delta_table, this->bonus_luck_table); +} diff --git a/src/TekkerAdjustmentSet.hh b/src/TekkerAdjustmentSet.hh index 11c34be5..db26a15b 100644 --- a/src/TekkerAdjustmentSet.hh +++ b/src/TekkerAdjustmentSet.hh @@ -26,11 +26,13 @@ struct TekkerAdjustmentSet { std::string serialize_binary(bool big_endian) const; phosg::JSON json() const; + void print(FILE* stream) const; + static uint8_t favored_weapon_type_for_section_id(uint8_t section_id); struct Table { std::unordered_map probs; - size_t total; + size_t total = 0; }; std::array favored_special_delta_table; diff --git a/system/tables/armor-shop-random-set.json b/system/tables/armor-shop-random-set.json new file mode 100644 index 00000000..1c0bad33 --- /dev/null +++ b/system/tables/armor-shop-random-set.json @@ -0,0 +1,363 @@ +{ + "ArmorTable": [ + [ + [0, 33], + [1, 33], + [2, 15], + [3, 10], + [4, 5], + [5, 0], + [6, 4], + [7, 0], + [8, 0], + [9, 0], + [10, 0], + [11, 0], + [12, 0], + [13, 0], + [14, 0], + [15, 0], + [16, 0], + [17, 0], + [18, 0], + [19, 0], + [20, 0] + ], + [ + [0, 5], + [1, 5], + [2, 10], + [3, 15], + [4, 19], + [5, 19], + [6, 10], + [7, 10], + [8, 5], + [9, 0], + [10, 0], + [11, 2], + [12, 0], + [13, 0], + [14, 0], + [15, 0], + [16, 0], + [17, 0], + [18, 0], + [19, 0], + [20, 0] + ], + [ + [0, 0], + [1, 0], + [2, 0], + [3, 0], + [4, 5], + [5, 5], + [6, 10], + [7, 15], + [8, 19], + [9, 19], + [10, 10], + [11, 10], + [12, 5], + [13, 0], + [14, 0], + [15, 2], + [16, 0], + [17, 0], + [18, 0], + [19, 0], + [20, 0] + ], + [ + [0, 0], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0], + [6, 2], + [7, 4], + [8, 5], + [9, 5], + [10, 10], + [11, 12], + [12, 14], + [13, 14], + [14, 10], + [15, 9], + [16, 5], + [17, 5], + [18, 3], + [19, 2], + [20, 0] + ], + [ + [0, 0], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0], + [6, 0], + [7, 2], + [8, 5], + [9, 5], + [10, 5], + [11, 6], + [12, 10], + [13, 13], + [14, 14], + [15, 14], + [16, 14], + [17, 7], + [18, 3], + [19, 1], + [20, 1] + ] + ], + "ShieldTable": [ + [ + [0, 34], + [1, 34], + [2, 15], + [3, 15], + [4, 0], + [5, 0], + [6, 2], + [7, 0], + [8, 0], + [9, 0], + [10, 0], + [11, 0], + [12, 0], + [13, 0], + [14, 0], + [15, 0], + [16, 0], + [17, 0] + ], + [ + [0, 5], + [1, 10], + [2, 14], + [3, 18], + [4, 18], + [5, 14], + [6, 10], + [7, 5], + [8, 2], + [9, 2], + [10, 2], + [11, 0], + [12, 0], + [13, 0], + [14, 0], + [15, 0], + [16, 0], + [17, 0] + ], + [ + [0, 0], + [1, 0], + [2, 0], + [3, 6], + [4, 10], + [5, 15], + [6, 19], + [7, 18], + [8, 14], + [9, 9], + [10, 4], + [11, 3], + [12, 0], + [13, 2], + [14, 0], + [15, 0], + [16, 0], + [17, 0] + ], + [ + [0, 0], + [1, 0], + [2, 0], + [3, 0], + [4, 5], + [5, 5], + [6, 5], + [7, 10], + [8, 14], + [9, 15], + [10, 14], + [11, 11], + [12, 9], + [13, 6], + [14, 4], + [15, 0], + [16, 2], + [17, 0] + ], + [ + [0, 0], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 4], + [6, 5], + [7, 5], + [8, 8], + [9, 11], + [10, 13], + [11, 13], + [12, 14], + [13, 14], + [14, 9], + [15, 2], + [16, 1], + [17, 1] + ] + ], + "UnitTable": [ + [ + [0, 0], + [4, 0], + [8, 0], + [12, 0], + [16, 0], + [24, 0], + [33, 0], + [36, 0], + [39, 0], + [42, 0], + [45, 0], + [1, 0], + [5, 0], + [9, 0], + [13, 0], + [17, 0], + [25, 0], + [34, 0], + [37, 0], + [40, 0], + [43, 0], + [46, 0], + [51, 0], + [54, 0], + [57, 0], + [67, 0] + ], + [ + [0, 10], + [4, 10], + [8, 10], + [12, 10], + [16, 10], + [24, 10], + [33, 10], + [36, 10], + [39, 10], + [42, 5], + [45, 5], + [1, 0], + [5, 0], + [9, 0], + [13, 0], + [17, 0], + [25, 0], + [34, 0], + [37, 0], + [40, 0], + [43, 0], + [46, 0], + [51, 0], + [54, 0], + [57, 0], + [67, 0] + ], + [ + [0, 10], + [4, 10], + [8, 10], + [12, 10], + [16, 10], + [24, 10], + [33, 10], + [36, 10], + [39, 10], + [42, 5], + [45, 5], + [1, 0], + [5, 0], + [9, 0], + [13, 0], + [17, 0], + [25, 0], + [34, 0], + [37, 0], + [40, 0], + [43, 0], + [46, 0], + [51, 0], + [54, 0], + [57, 0], + [67, 0] + ], + [ + [0, 4], + [4, 4], + [8, 4], + [12, 4], + [16, 4], + [24, 4], + [33, 4], + [36, 4], + [39, 4], + [42, 5], + [45, 5], + [1, 5], + [5, 5], + [9, 5], + [13, 5], + [17, 5], + [25, 5], + [34, 5], + [37, 5], + [40, 5], + [43, 3], + [46, 3], + [51, 1], + [54, 1], + [57, 1], + [67, 0] + ], + [ + [0, 3], + [4, 3], + [8, 3], + [12, 3], + [16, 3], + [24, 3], + [33, 4], + [36, 4], + [39, 4], + [42, 4], + [45, 5], + [1, 5], + [5, 5], + [9, 5], + [13, 5], + [17, 5], + [25, 5], + [34, 5], + [37, 5], + [40, 5], + [43, 5], + [46, 5], + [51, 2], + [54, 2], + [57, 2], + [67, 0] + ] + ] +} \ No newline at end of file diff --git a/system/tables/tool-shop-random-set.json b/system/tables/tool-shop-random-set.json new file mode 100644 index 00000000..7fde08a0 --- /dev/null +++ b/system/tables/tool-shop-random-set.json @@ -0,0 +1,251 @@ +{ + "CommonRecoveryTable": [ + [0, 15, 3, 15, 6, 7, 9, 11, 12, 15, 15], + [0, 1, 3, 4, 6, 7, 9, 11, 12, 15, 15], + [0, 1, 3, 4, 6, 7, 9, 11, 12, 15, 15], + [15, 1, 15, 4, 6, 7, 9, 11, 12, 15, 15], + [15, 1, 15, 4, 6, 7, 9, 11, 12, 15, 15], + [15, 1, 15, 4, 6, 7, 9, 11, 12, 15, 15] + ], + "RareRecoveryTable": [ + [ + [2, 0], + [5, 0], + [8, 0], + [10, 0], + [13, 0], + [14, 0], + [15, 100] + ], + [ + [2, 25], + [5, 25], + [8, 24], + [10, 0], + [13, 0], + [14, 1], + [15, 25] + ], + [ + [2, 37], + [5, 37], + [8, 10], + [10, 4], + [13, 0], + [14, 2], + [15, 10] + ], + [ + [2, 42], + [5, 42], + [8, 9], + [10, 4], + [13, 0], + [14, 3], + [15, 0] + ], + [ + [2, 41], + [5, 41], + [8, 9], + [10, 5], + [13, 0], + [14, 4], + [15, 0] + ] + ], + "TechDiskLevelTable": [ + [ + {"PlayerLevelDivisor": 3}, + {"PlayerLevelDivisor": 4}, + {"PlayerLevelDivisor": 6}, + {"PlayerLevelDivisor": 3}, + {"PlayerLevelDivisor": 35}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {} + ], + [ + {"PlayerLevelDivisor": 6}, + {"PlayerLevelDivisor": 7}, + {"PlayerLevelDivisor": 8}, + {"PlayerLevelDivisor": 6}, + {"PlayerLevelDivisor": 35}, + {"PlayerLevelDivisor": 9}, + {"PlayerLevelDivisor": 7}, + {"PlayerLevelDivisor": 6}, + {"PlayerLevelDivisor": 8}, + {"PlayerLevelDivisor": 9}, + {"PlayerLevelDivisor": 12}, + {"PlayerLevelDivisor": 8}, + {}, + {}, + {}, + {}, + {} + ], + [ + {"MaxLevel": 10, "MinLevel": 3}, + {"MaxLevel": 10, "MinLevel": 3}, + {"MaxLevel": 10, "MinLevel": 3}, + {"MaxLevel": 10, "MinLevel": 3}, + {"PlayerLevelDivisor": 35}, + {"PlayerLevelDivisor": 8}, + {"PlayerLevelDivisor": 7}, + {"PlayerLevelDivisor": 5}, + {"PlayerLevelDivisor": 6}, + {"PlayerLevelDivisor": 8}, + {"PlayerLevelDivisor": 9}, + {"PlayerLevelDivisor": 10}, + {}, + {}, + {"PlayerLevelDivisor": 10}, + {"PlayerLevelDivisor": 11}, + {"PlayerLevelDivisor": 12} + ], + [ + {"MaxLevel": 12, "MinLevel": 4}, + {"MaxLevel": 12, "MinLevel": 4}, + {"MaxLevel": 12, "MinLevel": 4}, + {"MaxLevel": 12, "MinLevel": 4}, + {"PlayerLevelDivisor": 35}, + {"MaxLevel": 12, "MinLevel": 4}, + {"MaxLevel": 12, "MinLevel": 4}, + {"MaxLevel": 12, "MinLevel": 4}, + {"MaxLevel": 12, "MinLevel": 4}, + {"MaxLevel": 10, "MinLevel": 3}, + {"MaxLevel": 10, "MinLevel": 3}, + {"MaxLevel": 10, "MinLevel": 3}, + {}, + {}, + {"PlayerLevelDivisor": 12}, + {"PlayerLevelDivisor": 13}, + {"PlayerLevelDivisor": 11} + ], + [ + {"MaxLevel": 14, "MinLevel": 5}, + {"MaxLevel": 14, "MinLevel": 5}, + {"MaxLevel": 14, "MinLevel": 5}, + {"MaxLevel": 14, "MinLevel": 5}, + {"PlayerLevelDivisor": 35}, + {"MaxLevel": 14, "MinLevel": 5}, + {"MaxLevel": 14, "MinLevel": 5}, + {"MaxLevel": 14, "MinLevel": 5}, + {"MaxLevel": 14, "MinLevel": 5}, + {"MaxLevel": 13, "MinLevel": 4}, + {"MaxLevel": 13, "MinLevel": 4}, + {"MaxLevel": 13, "MinLevel": 4}, + {}, + {}, + {"MaxLevel": 13, "MinLevel": 3}, + {"MaxLevel": 13, "MinLevel": 3}, + {"MaxLevel": 13, "MinLevel": 3} + ] + ], + "TechDiskTable": [ + [ + [0, 18], + [1, 18], + [2, 18], + [3, 18], + [4, 18], + [5, 2], + [6, 3], + [7, 3], + [8, 2], + [9, 0], + [10, 0], + [11, 0], + [12, 0], + [13, 0], + [14, 0], + [15, 0], + [16, 0] + ], + [ + [0, 7], + [1, 7], + [2, 7], + [3, 7], + [4, 7], + [5, 6], + [6, 6], + [7, 6], + [8, 7], + [9, 8], + [10, 8], + [11, 8], + [12, 5], + [13, 5], + [14, 2], + [15, 2], + [16, 2] + ], + [ + [0, 5], + [1, 5], + [2, 5], + [3, 6], + [4, 5], + [5, 5], + [6, 5], + [7, 5], + [8, 5], + [9, 5], + [10, 5], + [11, 5], + [12, 6], + [13, 6], + [14, 9], + [15, 9], + [16, 9] + ], + [ + [0, 6], + [1, 6], + [2, 6], + [3, 5], + [4, 5], + [5, 6], + [6, 6], + [7, 6], + [8, 6], + [9, 6], + [10, 6], + [11, 6], + [12, 6], + [13, 6], + [14, 6], + [15, 6], + [16, 6] + ], + [ + [0, 6], + [1, 6], + [2, 6], + [3, 5], + [4, 5], + [5, 6], + [6, 6], + [7, 6], + [8, 6], + [9, 6], + [10, 6], + [11, 6], + [12, 6], + [13, 6], + [14, 6], + [15, 6], + [16, 6] + ] + ] +} \ No newline at end of file diff --git a/system/tables/weapon-shop-random-set-hard.json b/system/tables/weapon-shop-random-set-hard.json new file mode 100644 index 00000000..690cbe5d --- /dev/null +++ b/system/tables/weapon-shop-random-set-hard.json @@ -0,0 +1,1062 @@ +{ + "BonusRangeTable1": [ + [10, 14], + [10, 14], + [10, 15], + [10, 15], + [10, 16], + [10, 16], + [10, 16], + [10, 17], + [10, 19] + ], + "BonusRangeTable2": [ + [10, 13], + [10, 14], + [10, 14], + [10, 14], + [10, 15], + [10, 15], + [10, 16], + [10, 16], + [10, 19] + ], + "BonusTypeTable1": [ + [ + [0, 60], + [1, 20], + [2, 20], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 50], + [1, 15], + [2, 20], + [3, 15], + [4, 0], + [5, 0] + ], + [ + [0, 30], + [1, 10], + [2, 15], + [3, 25], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 15], + [3, 20], + [4, 30], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 25], + [3, 20], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 30], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 20], + [3, 20], + [4, 25], + [5, 0] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 20], + [4, 25], + [5, 5] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 20], + [4, 20], + [5, 10] + ] + ], + "BonusTypeTable2": [ + [ + [0, 100], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 100], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 65], + [1, 5], + [2, 10], + [3, 10], + [4, 10], + [5, 0] + ], + [ + [0, 45], + [1, 10], + [2, 10], + [3, 15], + [4, 20], + [5, 0] + ], + [ + [0, 30], + [1, 25], + [2, 20], + [3, 10], + [4, 10], + [5, 5] + ], + [ + [0, 15], + [1, 15], + [2, 20], + [3, 25], + [4, 20], + [5, 5] + ], + [ + [0, 15], + [1, 15], + [2, 20], + [3, 25], + [4, 15], + [5, 10] + ], + [ + [0, 10], + [1, 15], + [2, 20], + [3, 20], + [4, 20], + [5, 15] + ], + [ + [0, 10], + [1, 20], + [2, 15], + [3, 15], + [4, 20], + [5, 20] + ] + ], + "DefaultDringRangeTable": [ + [0, 2], + [0, 3], + [0, 4], + [0, 6], + [0, 8], + [0, 10] + ], + "FavoredDringRangeTable": [ + [0, 1], + [0, 3], + [1, 5], + [2, 8], + [3, 11], + [3, 16] + ], + "SpecialModeTable": [ + [ + [0, 85], + [1, 15], + [2, 0] + ], + [ + [0, 70], + [1, 30], + [2, 0] + ], + [ + [0, 50], + [1, 50], + [2, 0] + ], + [ + [0, 20], + [1, 80], + [2, 0] + ], + [ + [0, 20], + [1, 60], + [2, 20] + ], + [ + [0, 20], + [1, 40], + [2, 40] + ], + [ + [0, 10], + [1, 30], + [2, 60] + ], + [ + [0, 10], + [1, 10], + [2, 80] + ] + ], + "WeaponTypeWeightTables": [ + [ + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ], + [ + [0, 20], + [5, 20], + [25, 15], + [30, 25], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 20], + [30, 20], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ], + [ + [0, 20], + [5, 20], + [25, 25], + [30, 10], + [45, 25] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 5], + [45, 30] + ], + [ + [0, 25], + [5, 5], + [25, 25], + [30, 20], + [45, 25] + ], + [ + [0, 20], + [5, 25], + [25, 20], + [30, 10], + [45, 25] + ], + [ + [0, 20], + [5, 20], + [25, 20], + [30, 20], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ] + ], + [ + [ + [1, 10], + [10, 7], + [15, 0], + [20, 12], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 7], + [6, 7], + [40, 16], + [31, 7] + ], + [ + [1, 10], + [10, 0], + [15, 7], + [20, 7], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 7], + [6, 12], + [40, 7], + [31, 16] + ], + [ + [1, 10], + [10, 16], + [15, 7], + [20, 7], + [26, 10], + [35, 0], + [46, 10], + [49, 7], + [53, 7], + [6, 7], + [40, 7], + [31, 12] + ], + [ + [1, 10], + [10, 7], + [15, 7], + [20, 16], + [26, 10], + [35, 7], + [46, 10], + [49, 12], + [53, 0], + [6, 7], + [40, 7], + [31, 7] + ], + [ + [1, 10], + [10, 3], + [15, 7], + [20, 3], + [26, 10], + [35, 17], + [46, 10], + [49, 7], + [53, 7], + [6, 12], + [40, 7], + [31, 7] + ], + [ + [1, 10], + [10, 7], + [15, 7], + [20, 12], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 16], + [6, 7], + [40, 7], + [31, 0] + ], + [ + [1, 10], + [10, 7], + [15, 14], + [20, 7], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 7], + [6, 0], + [40, 14], + [31, 7] + ], + [ + [1, 10], + [10, 10], + [15, 7], + [20, 7], + [26, 10], + [35, 7], + [46, 10], + [49, 0], + [53, 10], + [6, 15], + [40, 7], + [31, 7] + ], + [ + [1, 9], + [10, 8], + [15, 8], + [20, 8], + [26, 9], + [35, 8], + [46, 9], + [49, 8], + [53, 8], + [6, 8], + [40, 9], + [31, 8] + ], + [ + [1, 10], + [10, 7], + [15, 16], + [20, 7], + [26, 10], + [35, 12], + [46, 10], + [49, 7], + [53, 7], + [6, 7], + [40, 0], + [31, 7] + ] + ], + [ + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 7], + [40, 16], + [15, 0], + [20, 12], + [31, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 3] + ], + [ + [1, 5], + [10, 0], + [6, 6], + [26, 5], + [35, 4], + [49, 7], + [40, 7], + [15, 7], + [20, 7], + [31, 16], + [46, 10], + [53, 7], + [2, 5], + [11, 0], + [7, 6], + [27, 5], + [36, 3] + ], + [ + [1, 5], + [10, 8], + [6, 4], + [26, 5], + [35, 0], + [49, 7], + [40, 7], + [15, 7], + [20, 7], + [31, 12], + [46, 10], + [53, 7], + [2, 5], + [11, 8], + [7, 3], + [27, 5], + [36, 0] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 12], + [40, 7], + [15, 7], + [20, 16], + [31, 7], + [46, 10], + [53, 0], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 3] + ], + [ + [1, 5], + [10, 2], + [6, 6], + [26, 5], + [35, 9], + [49, 7], + [40, 7], + [15, 7], + [20, 3], + [31, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 1], + [7, 6], + [27, 5], + [36, 8] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 7], + [40, 7], + [15, 7], + [20, 12], + [31, 0], + [46, 10], + [53, 16], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 3] + ], + [ + [1, 5], + [10, 4], + [6, 0], + [26, 5], + [35, 4], + [49, 7], + [40, 14], + [15, 14], + [20, 7], + [31, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 3], + [7, 0], + [27, 5], + [36, 3] + ], + [ + [1, 5], + [10, 5], + [6, 8], + [26, 5], + [35, 4], + [49, 0], + [40, 7], + [15, 7], + [20, 7], + [31, 7], + [46, 10], + [53, 10], + [2, 5], + [11, 5], + [7, 7], + [27, 5], + [36, 3] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 8], + [40, 8], + [15, 9], + [20, 8], + [31, 8], + [46, 9], + [53, 8], + [2, 4], + [11, 4], + [7, 4], + [27, 4], + [36, 4] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 6], + [49, 7], + [40, 0], + [15, 16], + [20, 7], + [31, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 6] + ] + ], + [ + [ + [2, 6], + [11, 4], + [6, 4], + [15, 0], + [20, 5], + [26, 6], + [40, 8], + [36, 4], + [31, 4], + [46, 5], + [53, 4], + [49, 4], + [3, 5], + [12, 3], + [7, 3], + [16, 0], + [21, 5], + [27, 5], + [41, 7], + [37, 3], + [32, 3], + [47, 5], + [54, 3], + [50, 4], + [57, 0] + ], + [ + [2, 6], + [11, 0], + [6, 5], + [15, 4], + [20, 4], + [26, 6], + [40, 4], + [36, 4], + [31, 8], + [46, 5], + [53, 4], + [49, 4], + [3, 5], + [12, 0], + [7, 5], + [16, 3], + [21, 3], + [27, 5], + [41, 3], + [37, 3], + [32, 7], + [47, 6], + [54, 3], + [50, 3], + [57, 0] + ], + [ + [2, 6], + [11, 8], + [6, 4], + [15, 4], + [20, 4], + [26, 6], + [40, 4], + [36, 0], + [31, 5], + [46, 5], + [53, 4], + [49, 4], + [3, 5], + [12, 7], + [7, 3], + [16, 3], + [21, 3], + [27, 5], + [41, 4], + [37, 0], + [32, 5], + [47, 5], + [54, 3], + [50, 3], + [57, 0] + ], + [ + [2, 6], + [11, 4], + [6, 4], + [15, 4], + [20, 8], + [26, 6], + [40, 4], + [36, 4], + [31, 4], + [46, 5], + [53, 0], + [49, 5], + [3, 6], + [12, 3], + [7, 3], + [16, 3], + [21, 7], + [27, 5], + [41, 3], + [37, 3], + [32, 3], + [47, 5], + [54, 0], + [50, 5], + [57, 0] + ], + [ + [2, 6], + [11, 1], + [6, 6], + [15, 4], + [20, 1], + [26, 6], + [40, 4], + [36, 8], + [31, 4], + [46, 5], + [53, 4], + [49, 4], + [3, 5], + [12, 1], + [7, 5], + [16, 3], + [21, 2], + [27, 5], + [41, 3], + [37, 8], + [32, 3], + [47, 6], + [54, 3], + [50, 3], + [57, 0] + ], + [ + [2, 6], + [11, 4], + [6, 4], + [15, 4], + [20, 5], + [26, 6], + [40, 4], + [36, 4], + [31, 0], + [46, 5], + [53, 8], + [49, 4], + [3, 5], + [12, 3], + [7, 3], + [16, 3], + [21, 5], + [27, 5], + [41, 3], + [37, 4], + [32, 0], + [47, 5], + [54, 7], + [50, 3], + [57, 0] + ], + [ + [2, 5], + [11, 4], + [6, 0], + [15, 7], + [20, 4], + [26, 5], + [40, 7], + [36, 4], + [31, 4], + [46, 5], + [53, 4], + [49, 4], + [3, 5], + [12, 3], + [7, 0], + [16, 7], + [21, 3], + [27, 5], + [41, 7], + [37, 3], + [32, 3], + [47, 5], + [54, 3], + [50, 3], + [57, 0] + ], + [ + [2, 6], + [11, 4], + [6, 8], + [15, 4], + [20, 5], + [26, 7], + [40, 4], + [36, 4], + [31, 4], + [46, 5], + [53, 4], + [49, 0], + [3, 5], + [12, 4], + [7, 7], + [16, 3], + [21, 3], + [27, 5], + [41, 3], + [37, 3], + [32, 3], + [47, 5], + [54, 4], + [50, 0], + [57, 0] + ], + [ + [2, 5], + [11, 4], + [6, 4], + [15, 4], + [20, 4], + [26, 4], + [40, 4], + [36, 4], + [31, 4], + [46, 4], + [53, 5], + [49, 4], + [3, 4], + [12, 4], + [7, 4], + [16, 4], + [21, 4], + [27, 5], + [41, 4], + [37, 4], + [32, 4], + [47, 5], + [54, 4], + [50, 4], + [57, 0] + ], + [ + [2, 6], + [11, 4], + [6, 4], + [15, 8], + [20, 4], + [26, 6], + [40, 0], + [36, 5], + [31, 4], + [46, 5], + [53, 5], + [49, 4], + [3, 5], + [12, 3], + [7, 3], + [16, 7], + [21, 3], + [27, 5], + [41, 0], + [37, 5], + [32, 3], + [47, 5], + [54, 3], + [50, 3], + [57, 0] + ] + ], + [ + [ + [3, 10], + [12, 7], + [7, 7], + [16, 0], + [21, 9], + [27, 9], + [41, 15], + [37, 7], + [32, 7], + [47, 11], + [54, 7], + [50, 11], + [57, 0] + ], + [ + [3, 10], + [12, 0], + [7, 9], + [16, 7], + [21, 7], + [27, 10], + [41, 7], + [37, 7], + [32, 15], + [47, 11], + [54, 7], + [50, 11], + [57, 0] + ], + [ + [3, 9], + [12, 15], + [7, 7], + [16, 7], + [21, 7], + [27, 10], + [41, 7], + [37, 0], + [32, 9], + [47, 11], + [54, 7], + [50, 11], + [57, 0] + ], + [ + [3, 10], + [12, 7], + [7, 7], + [16, 7], + [21, 15], + [27, 10], + [41, 7], + [37, 7], + [32, 7], + [47, 10], + [54, 0], + [50, 13], + [57, 0] + ], + [ + [3, 10], + [12, 2], + [7, 9], + [16, 7], + [21, 2], + [27, 10], + [41, 7], + [37, 16], + [32, 7], + [47, 11], + [54, 7], + [50, 12], + [57, 0] + ], + [ + [3, 9], + [12, 7], + [7, 7], + [16, 7], + [21, 9], + [27, 10], + [41, 7], + [37, 7], + [32, 0], + [47, 11], + [54, 15], + [50, 11], + [57, 0] + ], + [ + [3, 8], + [12, 8], + [7, 0], + [16, 12], + [21, 8], + [27, 8], + [41, 12], + [37, 8], + [32, 8], + [47, 8], + [54, 8], + [50, 12], + [57, 0] + ], + [ + [3, 10], + [12, 9], + [7, 16], + [16, 7], + [21, 7], + [27, 10], + [41, 7], + [37, 7], + [32, 7], + [47, 11], + [54, 9], + [50, 0], + [57, 0] + ], + [ + [3, 7], + [12, 6], + [7, 6], + [16, 7], + [21, 7], + [27, 7], + [41, 7], + [37, 7], + [32, 7], + [47, 13], + [54, 13], + [50, 13], + [57, 0] + ], + [ + [3, 10], + [12, 8], + [7, 8], + [16, 16], + [21, 7], + [27, 11], + [41, 0], + [37, 9], + [32, 7], + [47, 10], + [54, 7], + [50, 7], + [57, 0] + ] + ] + ] +} \ No newline at end of file diff --git a/system/tables/weapon-shop-random-set-normal.json b/system/tables/weapon-shop-random-set-normal.json new file mode 100644 index 00000000..0cd8bee5 --- /dev/null +++ b/system/tables/weapon-shop-random-set-normal.json @@ -0,0 +1,862 @@ +{ + "BonusRangeTable1": [ + [10, 14], + [10, 14], + [10, 15], + [10, 15], + [10, 16], + [10, 16], + [10, 16], + [10, 17], + [10, 19] + ], + "BonusRangeTable2": [ + [10, 13], + [10, 14], + [10, 14], + [10, 14], + [10, 15], + [10, 15], + [10, 16], + [10, 16], + [10, 19] + ], + "BonusTypeTable1": [ + [ + [0, 60], + [1, 20], + [2, 20], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 50], + [1, 15], + [2, 20], + [3, 15], + [4, 0], + [5, 0] + ], + [ + [0, 30], + [1, 10], + [2, 15], + [3, 25], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 15], + [3, 20], + [4, 30], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 25], + [3, 20], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 30], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 20], + [3, 20], + [4, 25], + [5, 0] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 20], + [4, 25], + [5, 5] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 20], + [4, 20], + [5, 10] + ] + ], + "BonusTypeTable2": [ + [ + [0, 100], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 100], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 65], + [1, 5], + [2, 10], + [3, 10], + [4, 10], + [5, 0] + ], + [ + [0, 45], + [1, 10], + [2, 10], + [3, 15], + [4, 20], + [5, 0] + ], + [ + [0, 30], + [1, 25], + [2, 20], + [3, 10], + [4, 10], + [5, 5] + ], + [ + [0, 15], + [1, 15], + [2, 20], + [3, 25], + [4, 20], + [5, 5] + ], + [ + [0, 15], + [1, 15], + [2, 20], + [3, 25], + [4, 15], + [5, 10] + ], + [ + [0, 10], + [1, 15], + [2, 20], + [3, 20], + [4, 20], + [5, 15] + ], + [ + [0, 10], + [1, 20], + [2, 15], + [3, 15], + [4, 20], + [5, 20] + ] + ], + "DefaultDringRangeTable": [ + [0, 2], + [0, 3], + [0, 4], + [0, 6], + [0, 8], + [0, 10] + ], + "FavoredDringRangeTable": [ + [0, 1], + [0, 3], + [1, 5], + [2, 8], + [3, 11], + [3, 16] + ], + "SpecialModeTable": [ + [ + [0, 85], + [1, 15], + [2, 0] + ], + [ + [0, 70], + [1, 30], + [2, 0] + ], + [ + [0, 50], + [1, 50], + [2, 0] + ], + [ + [0, 20], + [1, 80], + [2, 0] + ], + [ + [0, 20], + [1, 60], + [2, 20] + ], + [ + [0, 20], + [1, 40], + [2, 40] + ], + [ + [0, 10], + [1, 30], + [2, 60] + ], + [ + [0, 10], + [1, 10], + [2, 80] + ] + ], + "WeaponTypeWeightTables": [ + [ + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ], + [ + [0, 20], + [5, 20], + [25, 15], + [30, 25], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 20], + [30, 20], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ], + [ + [0, 20], + [5, 20], + [25, 25], + [30, 10], + [45, 25] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 5], + [45, 30] + ], + [ + [0, 25], + [5, 5], + [25, 25], + [30, 20], + [45, 25] + ], + [ + [0, 20], + [5, 25], + [25, 20], + [30, 10], + [45, 25] + ], + [ + [0, 20], + [5, 20], + [25, 20], + [30, 20], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ] + ], + [ + [ + [5, 8], + [30, 8], + [1, 12], + [10, 8], + [15, 0], + [20, 16], + [26, 12], + [35, 8], + [46, 12], + [49, 8], + [53, 8] + ], + [ + [5, 12], + [30, 18], + [1, 10], + [10, 0], + [15, 8], + [20, 8], + [26, 10], + [35, 8], + [46, 10], + [49, 8], + [53, 8] + ], + [ + [5, 8], + [30, 12], + [1, 10], + [10, 18], + [15, 8], + [20, 8], + [26, 10], + [35, 0], + [46, 10], + [49, 8], + [53, 8] + ], + [ + [5, 8], + [30, 8], + [1, 10], + [10, 8], + [15, 8], + [20, 18], + [26, 10], + [35, 8], + [46, 10], + [49, 12], + [53, 0] + ], + [ + [5, 13], + [30, 8], + [1, 10], + [10, 3], + [15, 8], + [20, 3], + [26, 10], + [35, 19], + [46, 10], + [49, 8], + [53, 8] + ], + [ + [5, 8], + [30, 0], + [1, 10], + [10, 8], + [15, 8], + [20, 12], + [26, 10], + [35, 8], + [46, 10], + [49, 8], + [53, 18] + ], + [ + [5, 0], + [30, 9], + [1, 10], + [10, 9], + [15, 16], + [20, 9], + [26, 10], + [35, 9], + [46, 10], + [49, 9], + [53, 9] + ], + [ + [5, 16], + [30, 8], + [1, 10], + [10, 11], + [15, 8], + [20, 8], + [26, 10], + [35, 8], + [46, 10], + [49, 0], + [53, 11] + ], + [ + [5, 9], + [30, 9], + [1, 10], + [10, 9], + [15, 9], + [20, 9], + [26, 9], + [35, 9], + [46, 9], + [49, 9], + [53, 9] + ], + [ + [5, 7], + [30, 7], + [1, 10], + [10, 7], + [15, 16], + [20, 7], + [26, 10], + [35, 12], + [46, 10], + [49, 7], + [53, 7] + ] + ], + [ + [ + [1, 10], + [10, 7], + [6, 7], + [26, 10], + [35, 7], + [49, 7], + [40, 16], + [15, 0], + [20, 10], + [31, 7], + [46, 12], + [53, 7] + ], + [ + [1, 10], + [10, 0], + [6, 12], + [26, 10], + [35, 7], + [49, 7], + [40, 7], + [15, 7], + [20, 7], + [31, 16], + [46, 10], + [53, 7] + ], + [ + [1, 10], + [10, 16], + [6, 7], + [26, 10], + [35, 0], + [49, 7], + [40, 7], + [15, 7], + [20, 7], + [31, 12], + [46, 10], + [53, 7] + ], + [ + [1, 10], + [10, 7], + [6, 7], + [26, 10], + [35, 7], + [49, 12], + [40, 7], + [15, 7], + [20, 16], + [31, 7], + [46, 10], + [53, 0] + ], + [ + [1, 10], + [10, 3], + [6, 12], + [26, 10], + [35, 17], + [49, 7], + [40, 7], + [15, 7], + [20, 3], + [31, 7], + [46, 10], + [53, 7] + ], + [ + [1, 10], + [10, 7], + [6, 7], + [26, 10], + [35, 7], + [49, 7], + [40, 7], + [15, 7], + [20, 12], + [31, 0], + [46, 10], + [53, 16] + ], + [ + [1, 10], + [10, 7], + [6, 0], + [26, 10], + [35, 7], + [49, 7], + [40, 14], + [15, 14], + [20, 7], + [31, 7], + [46, 10], + [53, 7] + ], + [ + [1, 10], + [10, 10], + [6, 15], + [26, 10], + [35, 7], + [49, 0], + [40, 7], + [15, 7], + [20, 7], + [31, 7], + [46, 10], + [53, 10] + ], + [ + [1, 9], + [10, 8], + [6, 8], + [26, 9], + [35, 8], + [49, 8], + [40, 9], + [15, 8], + [20, 8], + [31, 8], + [46, 9], + [53, 8] + ], + [ + [1, 10], + [10, 7], + [6, 7], + [26, 10], + [35, 12], + [49, 7], + [40, 0], + [15, 16], + [20, 7], + [31, 7], + [46, 10], + [53, 7] + ] + ], + [ + [ + [2, 10], + [11, 7], + [6, 7], + [15, 0], + [20, 12], + [26, 10], + [40, 16], + [36, 7], + [31, 7], + [46, 10], + [53, 7], + [49, 7] + ], + [ + [2, 10], + [11, 0], + [6, 12], + [15, 7], + [20, 7], + [26, 10], + [40, 7], + [36, 7], + [31, 16], + [46, 10], + [53, 7], + [49, 7] + ], + [ + [2, 10], + [11, 16], + [6, 7], + [15, 7], + [20, 7], + [26, 10], + [40, 7], + [36, 0], + [31, 12], + [46, 10], + [53, 7], + [49, 7] + ], + [ + [2, 10], + [11, 7], + [6, 7], + [15, 7], + [20, 16], + [26, 10], + [40, 7], + [36, 7], + [31, 7], + [46, 10], + [53, 0], + [49, 12] + ], + [ + [2, 10], + [11, 3], + [6, 12], + [15, 7], + [20, 3], + [26, 10], + [40, 7], + [36, 17], + [31, 7], + [46, 10], + [53, 7], + [49, 7] + ], + [ + [2, 10], + [11, 7], + [6, 7], + [15, 7], + [20, 12], + [26, 10], + [40, 7], + [36, 7], + [31, 0], + [46, 10], + [53, 16], + [49, 7] + ], + [ + [2, 10], + [11, 7], + [6, 0], + [15, 14], + [20, 7], + [26, 10], + [40, 14], + [36, 7], + [31, 7], + [46, 10], + [53, 7], + [49, 7] + ], + [ + [2, 10], + [11, 10], + [6, 15], + [15, 7], + [20, 7], + [26, 10], + [40, 7], + [36, 7], + [31, 7], + [46, 10], + [53, 10], + [49, 0] + ], + [ + [2, 9], + [11, 8], + [6, 8], + [15, 8], + [20, 8], + [26, 9], + [40, 9], + [36, 8], + [31, 8], + [46, 9], + [53, 8], + [49, 8] + ], + [ + [2, 10], + [11, 7], + [6, 7], + [15, 16], + [20, 7], + [26, 10], + [40, 0], + [36, 12], + [31, 7], + [46, 10], + [53, 7], + [49, 7] + ] + ], + [ + [ + [2, 10], + [11, 7], + [6, 7], + [15, 0], + [20, 12], + [26, 10], + [40, 16], + [36, 7], + [31, 7], + [46, 10], + [53, 7], + [49, 7] + ], + [ + [2, 10], + [11, 0], + [6, 12], + [15, 7], + [20, 7], + [26, 10], + [40, 7], + [36, 7], + [31, 16], + [46, 10], + [53, 7], + [49, 7] + ], + [ + [2, 10], + [11, 16], + [6, 7], + [15, 7], + [20, 7], + [26, 10], + [40, 7], + [36, 0], + [31, 12], + [46, 10], + [53, 7], + [49, 7] + ], + [ + [2, 10], + [11, 7], + [6, 7], + [15, 7], + [20, 16], + [26, 10], + [40, 7], + [36, 7], + [31, 7], + [46, 10], + [53, 0], + [49, 12] + ], + [ + [2, 10], + [11, 3], + [6, 12], + [15, 7], + [20, 3], + [26, 10], + [40, 7], + [36, 17], + [31, 7], + [46, 10], + [53, 7], + [49, 7] + ], + [ + [2, 10], + [11, 7], + [6, 7], + [15, 7], + [20, 12], + [26, 10], + [40, 7], + [36, 7], + [31, 0], + [46, 10], + [53, 16], + [49, 7] + ], + [ + [2, 10], + [11, 7], + [6, 0], + [15, 14], + [20, 7], + [26, 10], + [40, 14], + [36, 7], + [31, 7], + [46, 10], + [53, 7], + [49, 7] + ], + [ + [2, 10], + [11, 10], + [6, 15], + [15, 7], + [20, 7], + [26, 10], + [40, 7], + [36, 7], + [31, 7], + [46, 10], + [53, 10], + [49, 0] + ], + [ + [2, 9], + [11, 8], + [6, 8], + [15, 8], + [20, 8], + [26, 9], + [40, 9], + [36, 8], + [31, 8], + [46, 9], + [53, 8], + [49, 8] + ], + [ + [2, 10], + [11, 7], + [6, 7], + [15, 16], + [20, 7], + [26, 10], + [40, 0], + [36, 12], + [31, 7], + [46, 10], + [53, 7], + [49, 7] + ] + ] + ] +} \ No newline at end of file diff --git a/system/tables/weapon-shop-random-set-ultimate.json b/system/tables/weapon-shop-random-set-ultimate.json new file mode 100644 index 00000000..86e86f89 --- /dev/null +++ b/system/tables/weapon-shop-random-set-ultimate.json @@ -0,0 +1,1296 @@ +{ + "BonusRangeTable1": [ + [10, 14], + [10, 14], + [10, 15], + [10, 15], + [10, 16], + [10, 16], + [10, 16], + [10, 17], + [10, 19] + ], + "BonusRangeTable2": [ + [10, 13], + [10, 14], + [10, 14], + [10, 14], + [10, 15], + [10, 15], + [10, 16], + [10, 16], + [10, 19] + ], + "BonusTypeTable1": [ + [ + [0, 60], + [1, 20], + [2, 20], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 50], + [1, 15], + [2, 20], + [3, 15], + [4, 0], + [5, 0] + ], + [ + [0, 30], + [1, 10], + [2, 15], + [3, 25], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 15], + [3, 20], + [4, 30], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 25], + [3, 20], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 30], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 20], + [3, 20], + [4, 25], + [5, 0] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 20], + [4, 25], + [5, 5] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 20], + [4, 20], + [5, 10] + ] + ], + "BonusTypeTable2": [ + [ + [0, 100], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 100], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 65], + [1, 5], + [2, 10], + [3, 10], + [4, 10], + [5, 0] + ], + [ + [0, 45], + [1, 10], + [2, 10], + [3, 15], + [4, 20], + [5, 0] + ], + [ + [0, 30], + [1, 25], + [2, 20], + [3, 10], + [4, 10], + [5, 5] + ], + [ + [0, 15], + [1, 15], + [2, 20], + [3, 25], + [4, 20], + [5, 5] + ], + [ + [0, 15], + [1, 15], + [2, 20], + [3, 25], + [4, 15], + [5, 10] + ], + [ + [0, 10], + [1, 15], + [2, 20], + [3, 20], + [4, 20], + [5, 15] + ], + [ + [0, 10], + [1, 20], + [2, 15], + [3, 15], + [4, 20], + [5, 20] + ] + ], + "DefaultDringRangeTable": [ + [0, 2], + [0, 3], + [0, 4], + [0, 6], + [0, 8], + [0, 10] + ], + "FavoredDringRangeTable": [ + [0, 1], + [0, 3], + [1, 5], + [2, 8], + [3, 11], + [3, 16] + ], + "SpecialModeTable": [ + [ + [0, 85], + [1, 15], + [2, 0] + ], + [ + [0, 70], + [1, 30], + [2, 0] + ], + [ + [0, 50], + [1, 50], + [2, 0] + ], + [ + [0, 20], + [1, 80], + [2, 0] + ], + [ + [0, 20], + [1, 60], + [2, 20] + ], + [ + [0, 20], + [1, 40], + [2, 40] + ], + [ + [0, 10], + [1, 30], + [2, 60] + ], + [ + [0, 10], + [1, 10], + [2, 80] + ] + ], + "WeaponTypeWeightTables": [ + [ + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ], + [ + [0, 20], + [5, 20], + [25, 15], + [30, 25], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 20], + [30, 20], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ], + [ + [0, 20], + [5, 20], + [25, 25], + [30, 10], + [45, 25] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 5], + [45, 30] + ], + [ + [0, 25], + [5, 5], + [25, 25], + [30, 20], + [45, 25] + ], + [ + [0, 20], + [5, 25], + [25, 20], + [30, 10], + [45, 25] + ], + [ + [0, 20], + [5, 20], + [25, 20], + [30, 20], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ] + ], + [ + [ + [1, 10], + [10, 7], + [15, 0], + [20, 12], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 7], + [6, 7], + [40, 16], + [31, 7] + ], + [ + [1, 10], + [10, 0], + [15, 7], + [20, 7], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 7], + [6, 12], + [40, 7], + [31, 16] + ], + [ + [1, 10], + [10, 16], + [15, 7], + [20, 7], + [26, 10], + [35, 0], + [46, 10], + [49, 7], + [53, 7], + [6, 7], + [40, 7], + [31, 12] + ], + [ + [1, 10], + [10, 7], + [15, 7], + [20, 16], + [26, 10], + [35, 7], + [46, 10], + [49, 12], + [53, 0], + [6, 7], + [40, 7], + [31, 7] + ], + [ + [1, 10], + [10, 3], + [15, 7], + [20, 3], + [26, 10], + [35, 17], + [46, 10], + [49, 7], + [53, 7], + [6, 12], + [40, 7], + [31, 7] + ], + [ + [1, 10], + [10, 7], + [15, 7], + [20, 12], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 16], + [6, 7], + [40, 7], + [31, 0] + ], + [ + [1, 10], + [10, 7], + [15, 14], + [20, 7], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 7], + [6, 0], + [40, 14], + [31, 7] + ], + [ + [1, 10], + [10, 10], + [15, 7], + [20, 7], + [26, 10], + [35, 7], + [46, 10], + [49, 0], + [53, 10], + [6, 15], + [40, 7], + [31, 7] + ], + [ + [1, 9], + [10, 8], + [15, 8], + [20, 8], + [26, 9], + [35, 8], + [46, 9], + [49, 8], + [53, 8], + [6, 8], + [40, 9], + [31, 8] + ], + [ + [1, 10], + [10, 7], + [15, 16], + [20, 7], + [26, 10], + [35, 12], + [46, 10], + [49, 7], + [53, 7], + [6, 7], + [40, 0], + [31, 7] + ] + ], + [ + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 4], + [40, 8], + [15, 0], + [20, 12], + [32, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 3], + [50, 3], + [41, 8] + ], + [ + [1, 5], + [10, 0], + [6, 6], + [26, 5], + [35, 4], + [49, 4], + [40, 4], + [15, 7], + [20, 7], + [32, 16], + [46, 10], + [53, 7], + [2, 5], + [11, 0], + [7, 6], + [27, 5], + [36, 3], + [50, 3], + [41, 3] + ], + [ + [1, 5], + [10, 8], + [6, 4], + [26, 5], + [35, 0], + [49, 4], + [40, 4], + [15, 7], + [20, 7], + [32, 12], + [46, 10], + [53, 7], + [2, 5], + [11, 8], + [7, 3], + [27, 5], + [36, 0], + [50, 3], + [41, 3] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 6], + [40, 4], + [15, 7], + [20, 16], + [32, 7], + [46, 10], + [53, 0], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 3], + [50, 6], + [41, 3] + ], + [ + [1, 5], + [10, 2], + [6, 6], + [26, 5], + [35, 9], + [49, 4], + [40, 4], + [15, 7], + [20, 3], + [32, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 1], + [7, 6], + [27, 5], + [36, 8], + [50, 3], + [41, 3] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 4], + [40, 4], + [15, 7], + [20, 12], + [32, 0], + [46, 10], + [53, 16], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 3], + [50, 3], + [41, 3] + ], + [ + [1, 5], + [10, 4], + [6, 0], + [26, 5], + [35, 4], + [49, 4], + [40, 7], + [15, 14], + [20, 7], + [32, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 3], + [7, 0], + [27, 5], + [36, 3], + [50, 3], + [41, 7] + ], + [ + [1, 5], + [10, 5], + [6, 8], + [26, 5], + [35, 4], + [49, 0], + [40, 4], + [15, 7], + [20, 7], + [32, 7], + [46, 10], + [53, 10], + [2, 5], + [11, 5], + [7, 7], + [27, 5], + [36, 3], + [50, 0], + [41, 3] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 4], + [40, 4], + [15, 9], + [20, 8], + [32, 8], + [46, 9], + [53, 8], + [2, 4], + [11, 4], + [7, 4], + [27, 4], + [36, 4], + [50, 4], + [41, 4] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 6], + [49, 4], + [40, 0], + [15, 16], + [20, 7], + [32, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 6], + [50, 3], + [41, 0] + ] + ], + [ + [ + [2, 9], + [12, 8], + [7, 7], + [16, 0], + [21, 9], + [27, 10], + [41, 18], + [37, 8], + [32, 7], + [47, 10], + [54, 7], + [50, 7], + [57, 0] + ], + [ + [2, 9], + [12, 0], + [7, 9], + [16, 7], + [21, 8], + [27, 10], + [41, 7], + [37, 7], + [32, 18], + [47, 10], + [54, 7], + [50, 8], + [57, 0] + ], + [ + [2, 9], + [12, 18], + [7, 7], + [16, 7], + [21, 7], + [27, 10], + [41, 8], + [37, 0], + [32, 9], + [47, 10], + [54, 8], + [50, 7], + [57, 0] + ], + [ + [2, 9], + [12, 7], + [7, 7], + [16, 8], + [21, 18], + [27, 10], + [41, 8], + [37, 7], + [32, 7], + [47, 10], + [54, 0], + [50, 9], + [57, 0] + ], + [ + [2, 10], + [12, 2], + [7, 9], + [16, 7], + [21, 2], + [27, 11], + [41, 8], + [37, 18], + [32, 7], + [47, 11], + [54, 7], + [50, 8], + [57, 0] + ], + [ + [2, 9], + [12, 7], + [7, 8], + [16, 7], + [21, 9], + [27, 10], + [41, 7], + [37, 7], + [32, 0], + [47, 10], + [54, 18], + [50, 8], + [57, 0] + ], + [ + [2, 8], + [12, 8], + [7, 0], + [16, 12], + [21, 8], + [27, 8], + [41, 12], + [37, 8], + [32, 8], + [47, 8], + [54, 8], + [50, 12], + [57, 0] + ], + [ + [2, 9], + [12, 8], + [7, 18], + [16, 7], + [21, 8], + [27, 10], + [41, 8], + [37, 7], + [32, 7], + [47, 10], + [54, 8], + [50, 0], + [57, 0] + ], + [ + [2, 8], + [12, 8], + [7, 8], + [16, 8], + [21, 8], + [27, 8], + [41, 8], + [37, 8], + [32, 8], + [47, 8], + [54, 5], + [50, 15], + [57, 0] + ], + [ + [2, 9], + [12, 8], + [7, 7], + [16, 18], + [21, 8], + [27, 10], + [41, 0], + [37, 9], + [32, 7], + [47, 10], + [54, 7], + [50, 7], + [57, 0] + ] + ], + [ + [ + [3, 10], + [12, 7], + [8, 7], + [17, 0], + [22, 11], + [28, 10], + [42, 17], + [37, 7], + [32, 7], + [47, 10], + [54, 7], + [50, 7], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 0], + [8, 11], + [17, 7], + [22, 7], + [28, 10], + [42, 7], + [37, 7], + [32, 17], + [47, 10], + [54, 7], + [50, 7], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 17], + [8, 7], + [17, 7], + [22, 7], + [28, 10], + [42, 7], + [37, 0], + [32, 11], + [47, 10], + [54, 7], + [50, 7], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 7], + [8, 7], + [17, 7], + [22, 17], + [28, 11], + [42, 7], + [37, 7], + [32, 7], + [47, 10], + [54, 0], + [50, 10], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 3], + [8, 11], + [17, 7], + [22, 3], + [28, 11], + [42, 7], + [37, 17], + [32, 7], + [47, 10], + [54, 7], + [50, 7], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 7], + [8, 7], + [17, 7], + [22, 11], + [28, 11], + [42, 7], + [37, 7], + [32, 0], + [47, 10], + [54, 17], + [50, 6], + [57, 0], + [58, 0] + ], + [ + [3, 7], + [12, 6], + [8, 0], + [17, 12], + [22, 7], + [28, 7], + [42, 12], + [37, 6], + [32, 7], + [47, 10], + [54, 13], + [50, 13], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 8], + [8, 15], + [17, 6], + [22, 6], + [28, 10], + [42, 6], + [37, 6], + [32, 6], + [47, 10], + [54, 17], + [50, 0], + [57, 0], + [58, 0] + ], + [ + [3, 6], + [12, 6], + [8, 6], + [17, 6], + [22, 6], + [28, 6], + [42, 6], + [37, 6], + [32, 13], + [47, 13], + [54, 13], + [50, 13], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 6], + [8, 6], + [17, 13], + [22, 6], + [28, 9], + [42, 0], + [37, 9], + [32, 14], + [47, 17], + [54, 4], + [50, 6], + [57, 0], + [58, 0] + ] + ], + [ + [ + [4, 10], + [13, 7], + [8, 7], + [18, 0], + [23, 10], + [28, 10], + [43, 18], + [38, 7], + [33, 7], + [48, 10], + [55, 7], + [51, 7], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [13, 0], + [8, 10], + [18, 7], + [23, 7], + [28, 10], + [43, 7], + [38, 7], + [33, 18], + [48, 10], + [55, 7], + [51, 7], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [13, 18], + [8, 7], + [18, 7], + [23, 7], + [28, 10], + [43, 7], + [38, 0], + [33, 10], + [48, 10], + [55, 7], + [51, 7], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [13, 7], + [8, 7], + [18, 7], + [23, 18], + [28, 11], + [43, 7], + [38, 7], + [33, 7], + [48, 10], + [55, 0], + [51, 9], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [13, 3], + [8, 10], + [18, 7], + [23, 3], + [28, 11], + [43, 7], + [38, 18], + [33, 7], + [48, 10], + [55, 7], + [51, 7], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [13, 7], + [8, 7], + [18, 7], + [23, 10], + [28, 11], + [43, 7], + [38, 7], + [33, 0], + [48, 10], + [55, 18], + [51, 6], + [57, 0], + [58, 0] + ], + [ + [4, 7], + [13, 6], + [8, 0], + [18, 13], + [23, 7], + [28, 7], + [43, 11], + [38, 6], + [33, 7], + [48, 10], + [55, 13], + [51, 13], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [13, 8], + [8, 14], + [18, 6], + [23, 6], + [28, 10], + [43, 6], + [38, 6], + [33, 6], + [48, 10], + [55, 18], + [51, 0], + [57, 0], + [58, 0] + ], + [ + [4, 6], + [13, 6], + [8, 6], + [18, 6], + [23, 6], + [28, 6], + [43, 6], + [38, 6], + [33, 13], + [48, 13], + [55, 13], + [51, 13], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [13, 6], + [8, 6], + [18, 13], + [23, 6], + [28, 9], + [43, 0], + [38, 9], + [33, 14], + [48, 17], + [55, 4], + [51, 6], + [57, 0], + [58, 0] + ] + ], + [ + [ + [4, 10], + [14, 7], + [9, 7], + [19, 0], + [24, 10], + [29, 10], + [44, 18], + [39, 7], + [34, 7], + [48, 10], + [56, 7], + [52, 7], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [14, 0], + [9, 10], + [19, 7], + [24, 7], + [29, 10], + [44, 7], + [39, 7], + [34, 18], + [48, 10], + [56, 7], + [52, 7], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [14, 18], + [9, 7], + [19, 7], + [24, 7], + [29, 10], + [44, 7], + [39, 0], + [34, 10], + [48, 10], + [56, 7], + [52, 7], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [14, 7], + [9, 7], + [19, 7], + [24, 18], + [29, 11], + [44, 7], + [39, 7], + [34, 7], + [48, 10], + [56, 0], + [52, 9], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [14, 3], + [9, 10], + [19, 7], + [24, 3], + [29, 11], + [44, 7], + [39, 18], + [34, 7], + [48, 10], + [56, 7], + [52, 7], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [14, 7], + [9, 7], + [19, 7], + [24, 10], + [29, 11], + [44, 7], + [39, 7], + [34, 0], + [48, 10], + [56, 18], + [52, 6], + [57, 0], + [58, 0] + ], + [ + [4, 7], + [14, 6], + [9, 0], + [19, 13], + [24, 7], + [29, 7], + [44, 13], + [39, 6], + [34, 7], + [48, 10], + [56, 12], + [52, 12], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [14, 8], + [9, 14], + [19, 6], + [24, 6], + [29, 10], + [44, 6], + [39, 6], + [34, 6], + [48, 10], + [56, 18], + [52, 0], + [57, 0], + [58, 0] + ], + [ + [4, 6], + [14, 6], + [9, 6], + [19, 6], + [24, 6], + [29, 6], + [44, 6], + [39, 6], + [34, 13], + [48, 13], + [56, 13], + [52, 13], + [57, 0], + [58, 0] + ], + [ + [4, 10], + [14, 6], + [9, 6], + [19, 13], + [24, 6], + [29, 9], + [44, 0], + [39, 9], + [34, 14], + [48, 17], + [56, 4], + [52, 6], + [57, 0], + [58, 0] + ] + ] + ] +} \ No newline at end of file diff --git a/system/tables/weapon-shop-random-set-very-hard.json b/system/tables/weapon-shop-random-set-very-hard.json new file mode 100644 index 00000000..285207d1 --- /dev/null +++ b/system/tables/weapon-shop-random-set-very-hard.json @@ -0,0 +1,972 @@ +{ + "BonusRangeTable1": [ + [10, 14], + [10, 14], + [10, 15], + [10, 15], + [10, 16], + [10, 16], + [10, 16], + [10, 17], + [10, 19] + ], + "BonusRangeTable2": [ + [10, 13], + [10, 14], + [10, 14], + [10, 14], + [10, 15], + [10, 15], + [10, 16], + [10, 16], + [10, 19] + ], + "BonusTypeTable1": [ + [ + [0, 60], + [1, 20], + [2, 20], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 50], + [1, 15], + [2, 20], + [3, 15], + [4, 0], + [5, 0] + ], + [ + [0, 30], + [1, 10], + [2, 15], + [3, 25], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 15], + [3, 20], + [4, 30], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 25], + [3, 20], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 30], + [4, 20], + [5, 0] + ], + [ + [0, 10], + [1, 25], + [2, 20], + [3, 20], + [4, 25], + [5, 0] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 20], + [4, 25], + [5, 5] + ], + [ + [0, 10], + [1, 20], + [2, 20], + [3, 20], + [4, 20], + [5, 10] + ] + ], + "BonusTypeTable2": [ + [ + [0, 100], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 100], + [1, 0], + [2, 0], + [3, 0], + [4, 0], + [5, 0] + ], + [ + [0, 65], + [1, 5], + [2, 10], + [3, 10], + [4, 10], + [5, 0] + ], + [ + [0, 45], + [1, 10], + [2, 10], + [3, 15], + [4, 20], + [5, 0] + ], + [ + [0, 30], + [1, 25], + [2, 20], + [3, 10], + [4, 10], + [5, 5] + ], + [ + [0, 15], + [1, 15], + [2, 20], + [3, 25], + [4, 20], + [5, 5] + ], + [ + [0, 15], + [1, 15], + [2, 20], + [3, 25], + [4, 15], + [5, 10] + ], + [ + [0, 10], + [1, 15], + [2, 20], + [3, 20], + [4, 20], + [5, 15] + ], + [ + [0, 10], + [1, 20], + [2, 15], + [3, 15], + [4, 20], + [5, 20] + ] + ], + "DefaultDringRangeTable": [ + [0, 2], + [0, 3], + [0, 4], + [0, 6], + [0, 8], + [0, 10] + ], + "FavoredDringRangeTable": [ + [0, 1], + [0, 3], + [1, 5], + [2, 8], + [3, 11], + [3, 16] + ], + "SpecialModeTable": [ + [ + [0, 85], + [1, 15], + [2, 0] + ], + [ + [0, 70], + [1, 30], + [2, 0] + ], + [ + [0, 50], + [1, 50], + [2, 0] + ], + [ + [0, 20], + [1, 80], + [2, 0] + ], + [ + [0, 20], + [1, 60], + [2, 20] + ], + [ + [0, 20], + [1, 40], + [2, 40] + ], + [ + [0, 10], + [1, 30], + [2, 60] + ], + [ + [0, 10], + [1, 10], + [2, 80] + ] + ], + "WeaponTypeWeightTables": [ + [ + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ], + [ + [0, 20], + [5, 20], + [25, 15], + [30, 25], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 20], + [30, 20], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ], + [ + [0, 20], + [5, 20], + [25, 25], + [30, 10], + [45, 25] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 5], + [45, 30] + ], + [ + [0, 25], + [5, 5], + [25, 25], + [30, 20], + [45, 25] + ], + [ + [0, 20], + [5, 25], + [25, 20], + [30, 10], + [45, 25] + ], + [ + [0, 20], + [5, 20], + [25, 20], + [30, 20], + [45, 20] + ], + [ + [0, 25], + [5, 15], + [25, 25], + [30, 15], + [45, 20] + ] + ], + [ + [ + [1, 10], + [10, 7], + [15, 0], + [20, 12], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 7], + [6, 7], + [40, 16], + [31, 7] + ], + [ + [1, 10], + [10, 0], + [15, 7], + [20, 7], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 7], + [6, 12], + [40, 7], + [31, 16] + ], + [ + [1, 10], + [10, 16], + [15, 7], + [20, 7], + [26, 10], + [35, 0], + [46, 10], + [49, 7], + [53, 7], + [6, 7], + [40, 7], + [31, 12] + ], + [ + [1, 10], + [10, 7], + [15, 7], + [20, 16], + [26, 10], + [35, 7], + [46, 10], + [49, 12], + [53, 0], + [6, 7], + [40, 7], + [31, 7] + ], + [ + [1, 10], + [10, 3], + [15, 7], + [20, 3], + [26, 10], + [35, 17], + [46, 10], + [49, 7], + [53, 7], + [6, 12], + [40, 7], + [31, 7] + ], + [ + [1, 10], + [10, 7], + [15, 7], + [20, 12], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 16], + [6, 7], + [40, 7], + [31, 0] + ], + [ + [1, 10], + [10, 7], + [15, 14], + [20, 7], + [26, 10], + [35, 7], + [46, 10], + [49, 7], + [53, 7], + [6, 0], + [40, 14], + [31, 7] + ], + [ + [1, 10], + [10, 10], + [15, 7], + [20, 7], + [26, 10], + [35, 7], + [46, 10], + [49, 0], + [53, 10], + [6, 15], + [40, 7], + [31, 7] + ], + [ + [1, 9], + [10, 8], + [15, 8], + [20, 8], + [26, 9], + [35, 8], + [46, 9], + [49, 8], + [53, 8], + [6, 8], + [40, 9], + [31, 8] + ], + [ + [1, 10], + [10, 7], + [15, 16], + [20, 7], + [26, 10], + [35, 12], + [46, 10], + [49, 7], + [53, 7], + [6, 7], + [40, 0], + [31, 7] + ] + ], + [ + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 4], + [40, 8], + [15, 0], + [20, 12], + [32, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 3], + [50, 3], + [41, 8] + ], + [ + [1, 5], + [10, 0], + [6, 6], + [26, 5], + [35, 4], + [49, 4], + [40, 4], + [15, 7], + [20, 7], + [32, 16], + [46, 10], + [53, 7], + [2, 5], + [11, 0], + [7, 6], + [27, 5], + [36, 3], + [50, 3], + [41, 3] + ], + [ + [1, 5], + [10, 8], + [6, 4], + [26, 5], + [35, 0], + [49, 4], + [40, 4], + [15, 7], + [20, 7], + [32, 12], + [46, 10], + [53, 7], + [2, 5], + [11, 8], + [7, 3], + [27, 5], + [36, 0], + [50, 3], + [41, 3] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 6], + [40, 4], + [15, 7], + [20, 16], + [32, 7], + [46, 10], + [53, 0], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 3], + [50, 6], + [41, 3] + ], + [ + [1, 5], + [10, 2], + [6, 6], + [26, 5], + [35, 9], + [49, 4], + [40, 4], + [15, 7], + [20, 3], + [32, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 1], + [7, 6], + [27, 5], + [36, 8], + [50, 3], + [41, 3] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 4], + [40, 4], + [15, 7], + [20, 12], + [32, 0], + [46, 10], + [53, 16], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 3], + [50, 3], + [41, 3] + ], + [ + [1, 5], + [10, 4], + [6, 0], + [26, 5], + [35, 4], + [49, 4], + [40, 7], + [15, 14], + [20, 7], + [32, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 3], + [7, 0], + [27, 5], + [36, 3], + [50, 3], + [41, 7] + ], + [ + [1, 5], + [10, 5], + [6, 8], + [26, 5], + [35, 4], + [49, 0], + [40, 4], + [15, 7], + [20, 7], + [32, 7], + [46, 10], + [53, 10], + [2, 5], + [11, 5], + [7, 7], + [27, 5], + [36, 3], + [50, 0], + [41, 3] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 4], + [49, 4], + [40, 4], + [15, 9], + [20, 8], + [32, 8], + [46, 9], + [53, 8], + [2, 4], + [11, 4], + [7, 4], + [27, 4], + [36, 4], + [50, 4], + [41, 4] + ], + [ + [1, 5], + [10, 4], + [6, 4], + [26, 5], + [35, 6], + [49, 4], + [40, 0], + [15, 16], + [20, 7], + [32, 7], + [46, 10], + [53, 7], + [2, 5], + [11, 3], + [7, 3], + [27, 5], + [36, 6], + [50, 3], + [41, 0] + ] + ], + [ + [ + [2, 9], + [12, 8], + [7, 7], + [16, 0], + [21, 9], + [27, 10], + [41, 18], + [37, 8], + [32, 7], + [47, 10], + [54, 7], + [50, 7], + [57, 0] + ], + [ + [2, 9], + [12, 0], + [7, 9], + [16, 7], + [21, 8], + [27, 10], + [41, 7], + [37, 7], + [32, 18], + [47, 10], + [54, 7], + [50, 8], + [57, 0] + ], + [ + [2, 9], + [12, 18], + [7, 7], + [16, 7], + [21, 7], + [27, 10], + [41, 8], + [37, 0], + [32, 9], + [47, 10], + [54, 8], + [50, 7], + [57, 0] + ], + [ + [2, 9], + [12, 7], + [7, 7], + [16, 8], + [21, 18], + [27, 10], + [41, 8], + [37, 7], + [32, 7], + [47, 10], + [54, 0], + [50, 9], + [57, 0] + ], + [ + [2, 10], + [12, 2], + [7, 9], + [16, 7], + [21, 2], + [27, 11], + [41, 8], + [37, 18], + [32, 7], + [47, 11], + [54, 7], + [50, 8], + [57, 0] + ], + [ + [2, 9], + [12, 7], + [7, 8], + [16, 7], + [21, 9], + [27, 10], + [41, 7], + [37, 7], + [32, 0], + [47, 10], + [54, 18], + [50, 8], + [57, 0] + ], + [ + [2, 8], + [12, 8], + [7, 0], + [16, 12], + [21, 8], + [27, 8], + [41, 12], + [37, 8], + [32, 8], + [47, 8], + [54, 8], + [50, 12], + [57, 0] + ], + [ + [2, 9], + [12, 8], + [7, 18], + [16, 7], + [21, 8], + [27, 10], + [41, 8], + [37, 7], + [32, 7], + [47, 10], + [54, 8], + [50, 0], + [57, 0] + ], + [ + [2, 8], + [12, 8], + [7, 8], + [16, 8], + [21, 8], + [27, 8], + [41, 8], + [37, 8], + [32, 8], + [47, 8], + [54, 5], + [50, 15], + [57, 0] + ], + [ + [2, 9], + [12, 8], + [7, 7], + [16, 18], + [21, 8], + [27, 10], + [41, 0], + [37, 9], + [32, 7], + [47, 10], + [54, 7], + [50, 7], + [57, 0] + ] + ], + [ + [ + [3, 10], + [12, 7], + [8, 7], + [17, 0], + [22, 11], + [28, 10], + [42, 17], + [37, 7], + [32, 7], + [47, 10], + [54, 7], + [50, 7], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 0], + [8, 11], + [17, 7], + [22, 7], + [28, 10], + [42, 7], + [37, 7], + [32, 17], + [47, 10], + [54, 7], + [50, 7], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 17], + [8, 7], + [17, 7], + [22, 7], + [28, 10], + [42, 7], + [37, 0], + [32, 11], + [47, 10], + [54, 7], + [50, 7], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 7], + [8, 7], + [17, 7], + [22, 17], + [28, 11], + [42, 7], + [37, 7], + [32, 7], + [47, 10], + [54, 0], + [50, 10], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 3], + [8, 11], + [17, 7], + [22, 3], + [28, 11], + [42, 7], + [37, 17], + [32, 7], + [47, 10], + [54, 7], + [50, 7], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 7], + [8, 7], + [17, 7], + [22, 11], + [28, 11], + [42, 7], + [37, 7], + [32, 0], + [47, 10], + [54, 17], + [50, 6], + [57, 0], + [58, 0] + ], + [ + [3, 7], + [12, 6], + [8, 0], + [17, 12], + [22, 7], + [28, 7], + [42, 12], + [37, 6], + [32, 7], + [47, 10], + [54, 13], + [50, 13], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 8], + [8, 15], + [17, 6], + [22, 6], + [28, 10], + [42, 6], + [37, 6], + [32, 6], + [47, 10], + [54, 17], + [50, 0], + [57, 0], + [58, 0] + ], + [ + [3, 6], + [12, 6], + [8, 6], + [17, 6], + [22, 6], + [28, 6], + [42, 6], + [37, 6], + [32, 13], + [47, 13], + [54, 13], + [50, 13], + [57, 0], + [58, 0] + ], + [ + [3, 10], + [12, 6], + [8, 6], + [17, 13], + [22, 6], + [28, 9], + [42, 0], + [37, 9], + [32, 14], + [47, 17], + [54, 4], + [50, 6], + [57, 0], + [58, 0] + ] + ] + ] +} \ No newline at end of file diff --git a/tests/game-tables.test.sh b/tests/game-tables.test.sh index 9861e345..3c0c22fb 100755 --- a/tests/game-tables.test.sh +++ b/tests/game-tables.test.sh @@ -11,6 +11,41 @@ DIR=tests/game-tables PMT_PREFIX=$DIR/item-parameter-table MMT_PREFIX=$DIR/mag-metadata-table +echo "... (armor-random-shop-set)" +$EXECUTABLE decode-armor-shop-random-set --big-endian $DIR/armor-shop-random-set.expected.bin $DIR/armor-shop-random-set.json +$EXECUTABLE encode-armor-shop-random-set --big-endian $DIR/armor-shop-random-set.json $DIR/armor-shop-random-set.encoded.bin +bindiff $DIR/armor-shop-random-set.expected.bin $DIR/armor-shop-random-set.encoded.bin + +echo "... (tool-random-shop-set)" +$EXECUTABLE decode-tool-shop-random-set --big-endian $DIR/tool-shop-random-set.expected.bin $DIR/tool-shop-random-set.json +$EXECUTABLE encode-tool-shop-random-set --big-endian $DIR/tool-shop-random-set.json $DIR/tool-shop-random-set.encoded.bin +bindiff $DIR/tool-shop-random-set.expected.bin $DIR/tool-shop-random-set.encoded.bin + +echo "... (weapon-random-shop-set-normal)" +$EXECUTABLE decode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-normal.expected.bin $DIR/weapon-shop-random-set-normal.json +$EXECUTABLE encode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-normal.json $DIR/weapon-shop-random-set-normal.encoded.bin +bindiff $DIR/weapon-shop-random-set-normal.expected.bin $DIR/weapon-shop-random-set-normal.encoded.bin + +echo "... (weapon-random-shop-set-hard)" +$EXECUTABLE decode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-hard.expected.bin $DIR/weapon-shop-random-set-hard.json +$EXECUTABLE encode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-hard.json $DIR/weapon-shop-random-set-hard.encoded.bin +bindiff $DIR/weapon-shop-random-set-hard.expected.bin $DIR/weapon-shop-random-set-hard.encoded.bin + +echo "... (weapon-random-shop-set-very-hard)" +$EXECUTABLE decode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-very-hard.expected.bin $DIR/weapon-shop-random-set-very-hard.json +$EXECUTABLE encode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-very-hard.json $DIR/weapon-shop-random-set-very-hard.encoded.bin +bindiff $DIR/weapon-shop-random-set-very-hard.expected.bin $DIR/weapon-shop-random-set-very-hard.encoded.bin + +echo "... (weapon-random-shop-set-ultimate)" +$EXECUTABLE decode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-ultimate.expected.bin $DIR/weapon-shop-random-set-ultimate.json +$EXECUTABLE encode-weapon-shop-random-set --big-endian $DIR/weapon-shop-random-set-ultimate.json $DIR/weapon-shop-random-set-ultimate.encoded.bin +bindiff $DIR/weapon-shop-random-set-ultimate.expected.bin $DIR/weapon-shop-random-set-ultimate.encoded.bin + +echo "... (tekker-adjustment-set)" +$EXECUTABLE decode-tekker-adjustment-set --big-endian $DIR/tekker-adjustment-set.expected.bin $DIR/tekker-adjustment-set.json +$EXECUTABLE encode-tekker-adjustment-set --big-endian $DIR/tekker-adjustment-set.json $DIR/tekker-adjustment-set.encoded.bin +bindiff $DIR/tekker-adjustment-set.expected.bin $DIR/tekker-adjustment-set.encoded.bin + echo "... (battle-params)" $EXECUTABLE decode-battle-params tests/game-tables/battle-params-ep1-on.dat tests/game-tables/battle-params-ep2-on.dat tests/game-tables/battle-params-ep4-on.dat tests/game-tables/battle-params-ep1-off.dat tests/game-tables/battle-params-ep2-off.dat tests/game-tables/battle-params-ep4-off.dat tests/game-tables/battle-params.json $EXECUTABLE encode-battle-params tests/game-tables/battle-params.json tests/game-tables/battle-params-encoded @@ -21,11 +56,6 @@ bindiff tests/game-tables/battle-params-ep1-off.dat tests/game-tables/battle-par bindiff tests/game-tables/battle-params-ep2-off.dat tests/game-tables/battle-params-encoded_lab.dat bindiff tests/game-tables/battle-params-ep4-off.dat tests/game-tables/battle-params-encoded_ep4.dat -echo "... (tekker-adjustment-set)" -$EXECUTABLE decode-tekker-adjustment-set --big-endian $DIR/tekker-adjustment-set.expected.bin $DIR/tekker-adjustment-set.json -$EXECUTABLE encode-tekker-adjustment-set --big-endian $DIR/tekker-adjustment-set.json $DIR/tekker-adjustment-set.encoded.bin -bindiff $DIR/tekker-adjustment-set.expected.bin $DIR/tekker-adjustment-set.encoded.bin - echo "... (level-table) BB" $EXECUTABLE decode-level-table --bb-v4 $DIR/level-table-bb-v4.expected.bin --decompressed $DIR/level-table-bb-v4.json --hex $EXECUTABLE encode-level-table-v4 $DIR/level-table-bb-v4.json $DIR/level-table-bb-v4.encoded.bin --decompressed diff --git a/system/tables/ArmorRandom-gc-v3.rel b/tests/game-tables/armor-shop-random-set.expected.bin similarity index 100% rename from system/tables/ArmorRandom-gc-v3.rel rename to tests/game-tables/armor-shop-random-set.expected.bin diff --git a/system/tables/ToolRandom-gc-v3.rel b/tests/game-tables/tool-shop-random-set.expected.bin similarity index 100% rename from system/tables/ToolRandom-gc-v3.rel rename to tests/game-tables/tool-shop-random-set.expected.bin diff --git a/system/tables/WeaponRandomHard-gc-v3.rel b/tests/game-tables/weapon-shop-random-set-hard.expected.bin similarity index 100% rename from system/tables/WeaponRandomHard-gc-v3.rel rename to tests/game-tables/weapon-shop-random-set-hard.expected.bin diff --git a/system/tables/WeaponRandomNormal-gc-v3.rel b/tests/game-tables/weapon-shop-random-set-normal.expected.bin similarity index 100% rename from system/tables/WeaponRandomNormal-gc-v3.rel rename to tests/game-tables/weapon-shop-random-set-normal.expected.bin diff --git a/system/tables/WeaponRandomUltimate-gc-v3.rel b/tests/game-tables/weapon-shop-random-set-ultimate.expected.bin similarity index 100% rename from system/tables/WeaponRandomUltimate-gc-v3.rel rename to tests/game-tables/weapon-shop-random-set-ultimate.expected.bin diff --git a/system/tables/WeaponRandomVeryHard-gc-v3.rel b/tests/game-tables/weapon-shop-random-set-very-hard.expected.bin similarity index 100% rename from system/tables/WeaponRandomVeryHard-gc-v3.rel rename to tests/game-tables/weapon-shop-random-set-very-hard.expected.bin