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");
|
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),
|
: data(data),
|
||||||
|
num_mags(num_mags),
|
||||||
r(*data) {
|
r(*data) {
|
||||||
size_t offset_table_offset = this->r.pget_u32l(this->data->size() - 0x10);
|
size_t offset_table_offset = this->r.pget_u32l(this->data->size() - 0x10);
|
||||||
this->offsets = &r.pget<TableOffsets>(offset_table_offset);
|
this->offsets = &r.pget<TableOffsets>(offset_table_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t MagEvolutionTable::get_evolution_number(uint8_t data1_1) const {
|
uint8_t MagEvolutionTable::get_evolution_number(uint8_t data1_1) const {
|
||||||
const auto& table = this->r.pget<EvolutionNumberTable>(this->offsets->evolution_number);
|
if (data1_1 >= this->num_mags) {
|
||||||
return table.values[data1_1];
|
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 {
|
class MagEvolutionTable {
|
||||||
public:
|
public:
|
||||||
|
// TODO: V1 format is different! Offsets are 0438 0440 0498 0520 054C
|
||||||
struct TableOffsets {
|
struct TableOffsets {
|
||||||
// num_mags = 0x53 in BB, 0x43 in V3
|
// num_mags = 0x3A in v2 and GC NTE, 0x43 in V3, 0x53 in BB
|
||||||
/* 00 / 0400 */ le_uint32_t unknown_a1; // -> [offset -> (0xC-byte struct)[num_mags], offset -> (same as first offset)]
|
// TODO: GC NTE uses the v2 format but is big-endian
|
||||||
/* 04 / 0408 */ le_uint32_t unknown_a2; // -> (2-byte struct, or single word)[num_mags]
|
/* -- / V2 / V3 / BB */
|
||||||
/* 08 / 04AE */ le_uint32_t unknown_a3; // -> (0xA8 bytes; possibly (8-byte struct)[0x15])
|
/* 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))]
|
||||||
/* 0C / 0556 */ le_uint32_t unknown_a4; // -> (uint8_t)[num_mags]
|
/* 04 / 0594 / 0348 / 0408 */ le_uint32_t unknown_a2; // -> (uint8_t[2])[num_mags]
|
||||||
/* 10 / 05AC */ le_uint32_t unknown_a5; // -> (float)[0x48]
|
/* 08 / 0608 / 03CE / 04AE */ le_uint32_t unknown_a3; // -> (8-byte struct)[0x15]
|
||||||
/* 14 / 06CC */ le_uint32_t evolution_number; // -> (uint8_t)[num_mags]
|
/* 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);
|
} __packed_ws__(TableOffsets, 0x18);
|
||||||
|
|
||||||
struct EvolutionNumberTable {
|
MagEvolutionTable(std::shared_ptr<const std::string> data, size_t num_mags);
|
||||||
parray<uint8_t, 0x53> values;
|
|
||||||
} __packed_ws__(EvolutionNumberTable, 0x53);
|
|
||||||
|
|
||||||
MagEvolutionTable(std::shared_ptr<const std::string> data);
|
|
||||||
~MagEvolutionTable() = default;
|
~MagEvolutionTable() = default;
|
||||||
|
|
||||||
uint8_t get_evolution_number(uint8_t data1_1) const;
|
uint8_t get_evolution_number(uint8_t data1_1) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::shared_ptr<const std::string> data;
|
std::shared_ptr<const std::string> data;
|
||||||
|
size_t num_mags;
|
||||||
phosg::StringReader r;
|
phosg::StringReader r;
|
||||||
const TableOffsets* offsets;
|
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
|
item.flags &= (~8); // Unequip it
|
||||||
should_delete_item = false;
|
should_delete_item = false;
|
||||||
|
|
||||||
} else if (primary_identifier == 0x030C0000) {
|
} else if ((primary_identifier & 0xFFFF0000) == 0x030C0000) { // Non-combo mag cells
|
||||||
// Cell of MAG 502
|
|
||||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
||||||
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x1D : 0x21;
|
if (s->mag_evolution_table(c->version())->get_evolution_number(mag.data.data1[1]) < 4) {
|
||||||
|
switch (item.data.data1[2]) {
|
||||||
} else if (primary_identifier == 0x030C0100) {
|
case 0x00: // Cell of MAG 502
|
||||||
// Cell of MAG 213
|
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x1D : 0x21;
|
||||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
break;
|
||||||
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x27 : 0x22;
|
case 0x01: // Cell of MAG 213
|
||||||
|
mag.data.data1[1] = (player->disp.visual.section_id & 1) ? 0x27 : 0x22;
|
||||||
} else if (primary_identifier == 0x030C0200) {
|
break;
|
||||||
// Parts of RoboChao
|
case 0x02: // Parts of RoboChao
|
||||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
mag.data.data1[1] = 0x28;
|
||||||
mag.data.data1[1] = 0x28;
|
break;
|
||||||
|
case 0x03: // Heart of Opa Opa
|
||||||
} else if (primary_identifier == 0x030C0300) {
|
mag.data.data1[1] = 0x29;
|
||||||
// Heart of Opa Opa
|
break;
|
||||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
case 0x04: // Heart of Pian
|
||||||
mag.data.data1[1] = 0x29;
|
mag.data.data1[1] = 0x2A;
|
||||||
|
break;
|
||||||
} else if (primary_identifier == 0x030C0400) {
|
case 0x05: // Heart of Chao
|
||||||
// Heart of Pian
|
mag.data.data1[1] = 0x2B;
|
||||||
auto& mag = player->inventory.items[player->inventory.find_equipped_item(EquipSlot::MAG)];
|
break;
|
||||||
mag.data.data1[1] = 0x2A;
|
default:
|
||||||
|
throw runtime_error("invalid mag cell used");
|
||||||
} 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;
|
|
||||||
|
|
||||||
} else if ((primary_identifier & 0xFFFF0000) == 0x03150000) {
|
} else if ((primary_identifier & 0xFFFF0000) == 0x03150000) {
|
||||||
// Christmas Present, etc. - use unwrap_table + probabilities therein
|
// 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) {
|
if (sum == 0) {
|
||||||
throw runtime_error("no unwrap results available for event");
|
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;
|
size_t det = random_from_optional_crypt(opt_rand_crypt) % sum;
|
||||||
for (size_t z = 0; z < table.second; z++) {
|
for (size_t z = 0; z < table.second; z++) {
|
||||||
const auto& entry = table.first[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);
|
item.data.data1.clear_after(3);
|
||||||
should_delete_item = false;
|
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();
|
auto l = c->require_lobby();
|
||||||
for (const auto& lc : l->clients) {
|
for (const auto& lc : l->clients) {
|
||||||
if (lc && (lc->version() == Version::BB_V4)) {
|
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[mag_item_index].data,
|
||||||
player->inventory.items[fed_item_index].data,
|
player->inventory.items[fed_item_index].data,
|
||||||
s->item_parameter_table(c->version()),
|
s->item_parameter_table(c->version()),
|
||||||
s->mag_evolution_table,
|
s->mag_evolution_table(c->version()),
|
||||||
player->disp.visual.char_class,
|
player->disp.visual.char_class,
|
||||||
player->disp.visual.section_id,
|
player->disp.visual.section_id,
|
||||||
!is_v1_or_v2(c->version()));
|
!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);
|
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 {
|
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));
|
auto ret = this->item_stack_limits_tables.at(static_cast<size_t>(version));
|
||||||
if (ret == nullptr) {
|
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.
|
// TODO: We should probably load the tables for other versions too.
|
||||||
config_log.info("Loading mag evolution table");
|
config_log.info("Loading v1/v2 mag evolution table");
|
||||||
auto mag_data = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-bb-v4.prs")));
|
auto mag_data_v1_v2 = make_shared<string>(prs_decompress(phosg::load_file("system/item-tables/ItemMagEdit-dc-v2.prs")));
|
||||||
auto new_mag_evolution_table = make_shared<MagEvolutionTable>(mag_data);
|
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(),
|
auto set = [s = this->shared_from_this(),
|
||||||
new_item_parameter_tables = std::move(new_item_parameter_tables),
|
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->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));
|
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::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 ItemParameterTable>, NUM_VERSIONS> item_parameter_tables;
|
||||||
std::array<std::shared_ptr<const ItemData::StackLimits>, NUM_VERSIONS> item_stack_limits_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::shared_ptr<const TextIndex> text_index;
|
||||||
std::array<std::shared_ptr<const ItemNameIndex>, NUM_VERSIONS> item_name_indexes;
|
std::array<std::shared_ptr<const ItemNameIndex>, NUM_VERSIONS> item_name_indexes;
|
||||||
std::shared_ptr<const WordSelectTable> word_select_table;
|
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 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(Version version) const;
|
||||||
std::shared_ptr<const ItemParameterTable> item_parameter_table_for_encode(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 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_opt(Version version) const; // Returns null if missing
|
||||||
std::shared_ptr<const ItemNameIndex> item_name_index(Version version) const; // Throws if missing
|
std::shared_ptr<const ItemNameIndex> item_name_index(Version version) const; // Throws if missing
|
||||||
|
|||||||
Reference in New Issue
Block a user