use mag evolution table for fixed-type cell evolution; fixes #608
This commit is contained in:
@@ -1286,14 +1286,17 @@ size_t ItemParameterTable::price_for_item(const ItemData& item) const {
|
||||
throw logic_error("this should be impossible");
|
||||
}
|
||||
|
||||
MagEvolutionTable::MagEvolutionTable(shared_ptr<const string> data)
|
||||
MagEvolutionTable::MagEvolutionTable(shared_ptr<const string> data, size_t num_mags)
|
||||
: data(data),
|
||||
num_mags(num_mags),
|
||||
r(*data) {
|
||||
size_t offset_table_offset = this->r.pget_u32l(this->data->size() - 0x10);
|
||||
this->offsets = &r.pget<TableOffsets>(offset_table_offset);
|
||||
}
|
||||
|
||||
uint8_t MagEvolutionTable::get_evolution_number(uint8_t data1_1) const {
|
||||
const auto& table = this->r.pget<EvolutionNumberTable>(this->offsets->evolution_number);
|
||||
return table.values[data1_1];
|
||||
if (data1_1 >= this->num_mags) {
|
||||
throw runtime_error("invalid mag number");
|
||||
}
|
||||
return this->r.pget_u8(this->offsets->evolution_number + data1_1);
|
||||
}
|
||||
|
||||
+12
-12
@@ -628,27 +628,27 @@ protected:
|
||||
|
||||
class MagEvolutionTable {
|
||||
public:
|
||||
// TODO: V1 format is different! Offsets are 0438 0440 0498 0520 054C
|
||||
struct TableOffsets {
|
||||
// num_mags = 0x53 in BB, 0x43 in V3
|
||||
/* 00 / 0400 */ le_uint32_t unknown_a1; // -> [offset -> (0xC-byte struct)[num_mags], offset -> (same as first offset)]
|
||||
/* 04 / 0408 */ le_uint32_t unknown_a2; // -> (2-byte struct, or single word)[num_mags]
|
||||
/* 08 / 04AE */ le_uint32_t unknown_a3; // -> (0xA8 bytes; possibly (8-byte struct)[0x15])
|
||||
/* 0C / 0556 */ le_uint32_t unknown_a4; // -> (uint8_t)[num_mags]
|
||||
/* 10 / 05AC */ le_uint32_t unknown_a5; // -> (float)[0x48]
|
||||
/* 14 / 06CC */ le_uint32_t evolution_number; // -> (uint8_t)[num_mags]
|
||||
// num_mags = 0x3A in v2 and GC NTE, 0x43 in V3, 0x53 in BB
|
||||
// TODO: GC NTE uses the v2 format but is big-endian
|
||||
/* -- / V2 / V3 / BB */
|
||||
/* 00 / 05BC / 0340 / 0400 */ le_uint32_t unknown_a1; // -> [offset -> (uint8_t[0xC])[num_mags], offset -> (same as first offset on v3; different on v2 (TODO))]
|
||||
/* 04 / 0594 / 0348 / 0408 */ le_uint32_t unknown_a2; // -> (uint8_t[2])[num_mags]
|
||||
/* 08 / 0608 / 03CE / 04AE */ le_uint32_t unknown_a3; // -> (8-byte struct)[0x15]
|
||||
/* 0C / 06B0 / 0476 / 0556 */ le_uint32_t unknown_a4; // -> (uint8_t)[num_mags]
|
||||
/* 10 / 06EC / 04BC / 05AC */ le_uint32_t unknown_a5; // -> (float)[0x48] on v3+, (float)[0x24] on v2
|
||||
/* 14 / 077C / 05DC / 06CC */ le_uint32_t evolution_number; // -> (uint8_t)[num_mags]
|
||||
} __packed_ws__(TableOffsets, 0x18);
|
||||
|
||||
struct EvolutionNumberTable {
|
||||
parray<uint8_t, 0x53> values;
|
||||
} __packed_ws__(EvolutionNumberTable, 0x53);
|
||||
|
||||
MagEvolutionTable(std::shared_ptr<const std::string> data);
|
||||
MagEvolutionTable(std::shared_ptr<const std::string> data, size_t num_mags);
|
||||
~MagEvolutionTable() = default;
|
||||
|
||||
uint8_t get_evolution_number(uint8_t data1_1) const;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<const std::string> data;
|
||||
size_t num_mags;
|
||||
phosg::StringReader r;
|
||||
const TableOffsets* offsets;
|
||||
};
|
||||
|
||||
+30
-32
@@ -135,35 +135,32 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<PSOLFGE
|
||||
item.flags &= (~8); // Unequip it
|
||||
should_delete_item = false;
|
||||
|
||||
} else if (primary_identifier == 0x030C0000) {
|
||||
// Cell of MAG 502
|
||||
} else if ((primary_identifier & 0xFFFF0000) == 0x030C0000) { // Non-combo mag cells
|
||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
||||
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x1D : 0x21;
|
||||
|
||||
} else if (primary_identifier == 0x030C0100) {
|
||||
// Cell of MAG 213
|
||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
||||
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x27 : 0x22;
|
||||
|
||||
} else if (primary_identifier == 0x030C0200) {
|
||||
// Parts of RoboChao
|
||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
||||
mag.data.data1[1] = 0x28;
|
||||
|
||||
} else if (primary_identifier == 0x030C0300) {
|
||||
// Heart of Opa Opa
|
||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
||||
mag.data.data1[1] = 0x29;
|
||||
|
||||
} else if (primary_identifier == 0x030C0400) {
|
||||
// Heart of Pian
|
||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
||||
mag.data.data1[1] = 0x2A;
|
||||
|
||||
} else if (primary_identifier == 0x030C0500) {
|
||||
// Heart of Chao
|
||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
||||
mag.data.data1[1] = 0x2B;
|
||||
if (s->mag_evolution_table(c->version())->get_evolution_number(mag.data.data1[1]) < 4) {
|
||||
switch (item.data.data1[2]) {
|
||||
case 0x00: // Cell of MAG 502
|
||||
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x1D : 0x21;
|
||||
break;
|
||||
case 0x01: // Cell of MAG 213
|
||||
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x27 : 0x22;
|
||||
break;
|
||||
case 0x02: // Parts of RoboChao
|
||||
mag.data.data1[1] = 0x28;
|
||||
break;
|
||||
case 0x03: // Heart of Opa Opa
|
||||
mag.data.data1[1] = 0x29;
|
||||
break;
|
||||
case 0x04: // Heart of Pian
|
||||
mag.data.data1[1] = 0x2A;
|
||||
break;
|
||||
case 0x05: // Heart of Chao
|
||||
mag.data.data1[1] = 0x2B;
|
||||
break;
|
||||
default:
|
||||
throw runtime_error("invalid mag cell used");
|
||||
}
|
||||
}
|
||||
|
||||
} else if ((primary_identifier & 0xFFFF0000) == 0x03150000) {
|
||||
// Christmas Present, etc. - use unwrap_table + probabilities therein
|
||||
@@ -176,6 +173,10 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<PSOLFGE
|
||||
if (sum == 0) {
|
||||
throw runtime_error("no unwrap results available for event");
|
||||
}
|
||||
// TODO: It seems that on non-BB, clients don't synchronize this at all, so
|
||||
// they could end up thinking the unwrapped item is something completely
|
||||
// different. (They don't even use a fixed random seed, like for rares;
|
||||
// they just call rand().) How does this actually work on console PSO?
|
||||
size_t det = random_from_optional_crypt(opt_rand_crypt) % sum;
|
||||
for (size_t z = 0; z < table.second; z++) {
|
||||
const auto& entry = table.first[z];
|
||||
@@ -189,9 +190,6 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<PSOLFGE
|
||||
item.data.data1.clear_after(3);
|
||||
should_delete_item = false;
|
||||
|
||||
// TODO: It seems that on non-BB, clients don't synchronize this at all
|
||||
// so they could end up thinking the unwrapped item is something
|
||||
// completely different. How does this actually work on console PSO?
|
||||
auto l = c->require_lobby();
|
||||
for (const auto& lc : l->clients) {
|
||||
if (lc && (lc->version() == Version::BB_V4)) {
|
||||
@@ -506,7 +504,7 @@ void player_feed_mag(std::shared_ptr<Client> c, size_t mag_item_index, size_t fe
|
||||
player->inventory.items[mag_item_index].data,
|
||||
player->inventory.items[fed_item_index].data,
|
||||
s->item_parameter_table(c->version()),
|
||||
s->mag_evolution_table,
|
||||
s->mag_evolution_table(c->version()),
|
||||
player->disp.visual.char_class,
|
||||
player->disp.visual.section_id,
|
||||
!is_v1_or_v2(c->version()));
|
||||
|
||||
+25
-5
@@ -451,6 +451,16 @@ shared_ptr<const ItemParameterTable> ServerState::item_parameter_table_for_encod
|
||||
return this->item_parameter_table(is_v1(version) ? Version::PC_V2 : version);
|
||||
}
|
||||
|
||||
shared_ptr<const MagEvolutionTable> ServerState::mag_evolution_table(Version version) const {
|
||||
if (is_v1_or_v2(version)) {
|
||||
return this->mag_evolution_table_v1_v2;
|
||||
} else if (!is_v4(version)) {
|
||||
return this->mag_evolution_table_v3;
|
||||
} else {
|
||||
return this->mag_evolution_table_v4;
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<const ItemData::StackLimits> ServerState::item_stack_limits(Version version) const {
|
||||
auto ret = this->item_stack_limits_tables.at(static_cast<size_t>(version));
|
||||
if (ret == nullptr) {
|
||||
@@ -2060,15 +2070,25 @@ void ServerState::load_item_definitions(bool from_non_event_thread) {
|
||||
}
|
||||
|
||||
// TODO: We should probably load the tables for other versions too.
|
||||
config_log.info("Loading mag evolution table");
|
||||
auto mag_data = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-bb-v4.prs")));
|
||||
auto new_mag_evolution_table = make_shared<MagEvolutionTable>(mag_data);
|
||||
config_log.info("Loading v1/v2 mag evolution table");
|
||||
auto mag_data_v1_v2 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-dc-v2.prs")));
|
||||
auto new_table_v1_v2 = make_shared<MagEvolutionTable>(mag_data_v1_v2, 0x3A);
|
||||
config_log.info("Loading v3 mag evolution table");
|
||||
auto mag_data_v3 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-xb-v3.prs")));
|
||||
auto new_table_v3 = make_shared<MagEvolutionTable>(mag_data_v3, 0x43);
|
||||
config_log.info("Loading v4 mag evolution table");
|
||||
auto mag_data_v4 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-bb-v4.prs")));
|
||||
auto new_table_v4 = make_shared<MagEvolutionTable>(mag_data_v4, 0x53);
|
||||
|
||||
auto set = [s = this->shared_from_this(),
|
||||
new_item_parameter_tables = std::move(new_item_parameter_tables),
|
||||
new_mag_evolution_table = std::move(new_mag_evolution_table)]() {
|
||||
new_table_v1_v2 = std::move(new_table_v1_v2),
|
||||
new_table_v3 = std::move(new_table_v3),
|
||||
new_table_v4 = std::move(new_table_v4)]() {
|
||||
s->item_parameter_tables = std::move(new_item_parameter_tables);
|
||||
s->mag_evolution_table = std::move(new_mag_evolution_table);
|
||||
s->mag_evolution_table_v1_v2 = std::move(new_table_v1_v2);
|
||||
s->mag_evolution_table_v3 = std::move(new_table_v3);
|
||||
s->mag_evolution_table_v4 = std::move(new_table_v4);
|
||||
};
|
||||
this->forward_or_call(from_non_event_thread, std::move(set));
|
||||
}
|
||||
|
||||
+4
-1
@@ -201,7 +201,9 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
std::shared_ptr<const TekkerAdjustmentSet> tekker_adjustment_set;
|
||||
std::array<std::shared_ptr<const ItemParameterTable>, NUM_VERSIONS> item_parameter_tables;
|
||||
std::array<std::shared_ptr<const ItemData::StackLimits>, NUM_VERSIONS> item_stack_limits_tables;
|
||||
std::shared_ptr<const MagEvolutionTable> mag_evolution_table;
|
||||
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v1_v2;
|
||||
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v3;
|
||||
std::shared_ptr<const MagEvolutionTable> mag_evolution_table_v4;
|
||||
std::shared_ptr<const TextIndex> text_index;
|
||||
std::array<std::shared_ptr<const ItemNameIndex>, NUM_VERSIONS> item_name_indexes;
|
||||
std::shared_ptr<const WordSelectTable> word_select_table;
|
||||
@@ -344,6 +346,7 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
||||
std::shared_ptr<const LevelTable> level_table(Version version) const;
|
||||
std::shared_ptr<const ItemParameterTable> item_parameter_table(Version version) const;
|
||||
std::shared_ptr<const ItemParameterTable> item_parameter_table_for_encode(Version version) const;
|
||||
std::shared_ptr<const MagEvolutionTable> mag_evolution_table(Version version) const;
|
||||
std::shared_ptr<const ItemData::StackLimits> item_stack_limits(Version version) const;
|
||||
std::shared_ptr<const ItemNameIndex> item_name_index_opt(Version version) const; // Returns null if missing
|
||||
std::shared_ptr<const ItemNameIndex> item_name_index(Version version) const; // Throws if missing
|
||||
|
||||
Reference in New Issue
Block a user