fix tool item stackability on 11/2000

This commit is contained in:
Martin Michelsen
2024-01-01 11:22:03 -08:00
parent 2fda85c750
commit 962ee6874e
16 changed files with 142 additions and 125 deletions
+3 -3
View File
@@ -480,7 +480,7 @@ void ItemCreator::set_item_unidentified_flag_if_not_challenge(ItemData& item) co
void ItemCreator::set_tool_item_amount_to_1(ItemData& item) const { void ItemCreator::set_tool_item_amount_to_1(ItemData& item) const {
if (item.data1[0] == 0x03) { if (item.data1[0] == 0x03) {
item.set_tool_item_amount(1); item.set_tool_item_amount(this->version, 1);
} }
} }
@@ -1686,7 +1686,7 @@ ItemCreator::DropResult ItemCreator::on_specialized_box_item_drop(
return res; return res;
} }
ItemData ItemCreator::base_item_for_specialized_box(uint32_t def0, uint32_t def1, uint32_t def2) { ItemData ItemCreator::base_item_for_specialized_box(uint32_t def0, uint32_t def1, uint32_t def2) const {
ItemData item; ItemData item;
item.data1[0] = (def0 >> 0x18) & 0x0F; item.data1[0] = (def0 >> 0x18) & 0x0F;
item.data1[1] = (def0 >> 0x10) + ((item.data1[0] == 0x00) || (item.data1[0] == 0x01)); item.data1[1] = (def0 >> 0x10) + ((item.data1[0] == 0x00) || (item.data1[0] == 0x01));
@@ -1715,7 +1715,7 @@ ItemData ItemCreator::base_item_for_specialized_box(uint32_t def0, uint32_t def1
if (item.data1[1] == 0x02) { if (item.data1[1] == 0x02) {
item.data1[4] = def0 & 0xFF; item.data1[4] = def0 & 0xFF;
} }
item.set_tool_item_amount(1); item.set_tool_item_amount(this->version, 1);
break; break;
case 0x04: case 0x04:
item.data2d = ((def1 >> 0x10) & 0xFFFF) * 10; item.data2d = ((def1 >> 0x10) & 0xFFFF) * 10;
+1 -1
View File
@@ -43,7 +43,7 @@ public:
void set_monster_destroyed(uint16_t entity_id); void set_monster_destroyed(uint16_t entity_id);
void set_box_destroyed(uint16_t entity_id); void set_box_destroyed(uint16_t entity_id);
static ItemData base_item_for_specialized_box(uint32_t def0, uint32_t def1, uint32_t def2); ItemData base_item_for_specialized_box(uint32_t def0, uint32_t def1, uint32_t def2) const;
std::vector<ItemData> generate_armor_shop_contents(size_t player_level); std::vector<ItemData> generate_armor_shop_contents(size_t player_level);
std::vector<ItemData> generate_tool_shop_contents(size_t player_level); std::vector<ItemData> generate_tool_shop_contents(size_t player_level);
+18 -18
View File
@@ -84,7 +84,7 @@ uint32_t ItemData::primary_identifier() const {
} }
} }
bool ItemData::is_wrapped() const { bool ItemData::is_wrapped(Version version) const {
switch (this->data1[0]) { switch (this->data1[0]) {
case 0: case 0:
case 1: case 1:
@@ -92,7 +92,7 @@ bool ItemData::is_wrapped() const {
case 2: case 2:
return this->data2[2] & 0x40; return this->data2[2] & 0x40;
case 3: case 3:
return !this->is_stackable() && (this->data1[3] & 0x40); return !this->is_stackable(version) && (this->data1[3] & 0x40);
case 4: case 4:
return false; return false;
default: default:
@@ -100,7 +100,7 @@ bool ItemData::is_wrapped() const {
} }
} }
void ItemData::wrap() { void ItemData::wrap(Version version) {
switch (this->data1[0]) { switch (this->data1[0]) {
case 0: case 0:
case 1: case 1:
@@ -110,7 +110,7 @@ void ItemData::wrap() {
this->data2[2] |= 0x40; this->data2[2] |= 0x40;
break; break;
case 3: case 3:
if (!this->is_stackable()) { if (!this->is_stackable(version)) {
this->data1[3] |= 0x40; this->data1[3] |= 0x40;
} }
break; break;
@@ -121,7 +121,7 @@ void ItemData::wrap() {
} }
} }
void ItemData::unwrap() { void ItemData::unwrap(Version version) {
switch (this->data1[0]) { switch (this->data1[0]) {
case 0: case 0:
case 1: case 1:
@@ -131,7 +131,7 @@ void ItemData::unwrap() {
this->data2[2] &= 0xBF; this->data2[2] &= 0xBF;
break; break;
case 3: case 3:
if (!this->is_stackable()) { if (!this->is_stackable(version)) {
this->data1[3] &= 0xBF; this->data1[3] &= 0xBF;
} }
break; break;
@@ -142,23 +142,23 @@ void ItemData::unwrap() {
} }
} }
bool ItemData::is_stackable() const { bool ItemData::is_stackable(Version version) const {
return this->max_stack_size() > 1; return this->max_stack_size(version) > 1;
} }
size_t ItemData::stack_size() const { size_t ItemData::stack_size(Version version) const {
if (max_stack_size_for_item(this->data1[0], this->data1[1]) > 1) { if (max_stack_size_for_item(version, this->data1[0], this->data1[1]) > 1) {
return this->data1[5]; return this->data1[5];
} }
return 1; return 1;
} }
size_t ItemData::max_stack_size() const { size_t ItemData::max_stack_size(Version version) const {
return max_stack_size_for_item(this->data1[0], this->data1[1]); return max_stack_size_for_item(version, this->data1[0], this->data1[1]);
} }
void ItemData::enforce_min_stack_size() { void ItemData::enforce_min_stack_size(Version version) {
if (this->stack_size() == 0) { if (this->stack_size(version) == 0) {
this->data1[5] = 1; this->data1[5] = 1;
} }
} }
@@ -502,12 +502,12 @@ void ItemData::set_sealed_item_kill_count(uint16_t v) {
} }
} }
uint8_t ItemData::get_tool_item_amount() const { uint8_t ItemData::get_tool_item_amount(Version version) const {
return this->is_stackable() ? this->data1[5] : 1; return this->is_stackable(version) ? this->data1[5] : 1;
} }
void ItemData::set_tool_item_amount(uint8_t amount) { void ItemData::set_tool_item_amount(Version version, uint8_t amount) {
if (this->is_stackable()) { if (this->is_stackable(version)) {
this->data1[5] = amount; this->data1[5] = amount;
} else if (this->data1[0] == 0x03) { } else if (this->data1[0] == 0x03) {
this->data1[5] = 0x00; this->data1[5] = 0x00;
+9 -9
View File
@@ -140,14 +140,14 @@ struct ItemData { // 0x14 bytes
std::string hex() const; std::string hex() const;
uint32_t primary_identifier() const; uint32_t primary_identifier() const;
bool is_wrapped() const; bool is_wrapped(Version version) const;
void wrap(); void wrap(Version version);
void unwrap(); void unwrap(Version version);
bool is_stackable() const; bool is_stackable(Version version) const;
size_t stack_size() const; size_t stack_size(Version version) const;
size_t max_stack_size() const; size_t max_stack_size(Version version) const;
void enforce_min_stack_size(); void enforce_min_stack_size(Version version);
static bool is_common_consumable(uint32_t primary_identifier); static bool is_common_consumable(uint32_t primary_identifier);
bool is_common_consumable() const; bool is_common_consumable() const;
@@ -166,8 +166,8 @@ struct ItemData { // 0x14 bytes
uint16_t get_sealed_item_kill_count() const; uint16_t get_sealed_item_kill_count() const;
void set_sealed_item_kill_count(uint16_t v); void set_sealed_item_kill_count(uint16_t v);
uint8_t get_tool_item_amount() const; uint8_t get_tool_item_amount(Version version) const;
void set_tool_item_amount(uint8_t amount); void set_tool_item_amount(Version version, uint8_t amount);
int16_t get_armor_or_shield_defense_bonus() const; int16_t get_armor_or_shield_defense_bonus() const;
void set_armor_or_shield_defense_bonus(int16_t bonus); void set_armor_or_shield_defense_bonus(int16_t bonus);
int16_t get_common_armor_evasion_bonus() const; int16_t get_common_armor_evasion_bonus() const;
+8 -6
View File
@@ -21,9 +21,11 @@ using namespace std;
// }; // };
ItemNameIndex::ItemNameIndex( ItemNameIndex::ItemNameIndex(
Version version,
std::shared_ptr<const ItemParameterTable> item_parameter_table, std::shared_ptr<const ItemParameterTable> item_parameter_table,
const std::vector<std::string>& name_coll) const std::vector<std::string>& name_coll)
: item_parameter_table(item_parameter_table) { : version(version),
item_parameter_table(item_parameter_table) {
auto find_items_1d = [&](uint64_t data1, size_t position) -> size_t { auto find_items_1d = [&](uint64_t data1, size_t position) -> size_t {
ItemData item(data1, 0); ItemData item(data1, 0);
@@ -176,7 +178,7 @@ std::string ItemNameIndex::describe_item(
// flags in a different location. // flags in a different location.
if (((item.data1[1] == 0x01) && (item.data1[4] & 0x40)) || if (((item.data1[1] == 0x01) && (item.data1[4] & 0x40)) ||
((item.data1[0] == 0x02) && (item.data2[2] & 0x40)) || ((item.data1[0] == 0x02) && (item.data2[2] & 0x40)) ||
((item.data1[0] == 0x03) && !item.is_stackable() && (item.data1[3] & 0x40))) { ((item.data1[0] == 0x03) && !item.is_stackable(this->version) && (item.data1[3] & 0x40))) {
ret_tokens.emplace_back("Wrapped"); ret_tokens.emplace_back("Wrapped");
} }
@@ -359,7 +361,7 @@ std::string ItemNameIndex::describe_item(
// For tools, add the amount (if applicable) // For tools, add the amount (if applicable)
} else if (item.data1[0] == 0x03) { } else if (item.data1[0] == 0x03) {
if (item.max_stack_size() > 1) { if (item.max_stack_size(this->version) > 1) {
ret_tokens.emplace_back(string_printf("x%hhu", item.data1[5])); ret_tokens.emplace_back(string_printf("x%hhu", item.data1[5]));
} }
} }
@@ -401,7 +403,7 @@ ItemData ItemNameIndex::parse_item_description(const std::string& desc) const {
} }
} }
} }
ret.enforce_min_stack_size(); ret.enforce_min_stack_size(this->version);
return ret; return ret;
} }
@@ -635,7 +637,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
ret.data2[2] |= 0x40; ret.data2[2] |= 0x40;
} }
} else if (ret.data1[0] == 0x03) { } else if (ret.data1[0] == 0x03) {
if (ret.max_stack_size() > 1) { if (ret.max_stack_size(this->version) > 1) {
if (starts_with(desc, "x")) { if (starts_with(desc, "x")) {
ret.data1[5] = stoul(desc.substr(1), nullptr, 10); ret.data1[5] = stoul(desc.substr(1), nullptr, 10);
} else { } else {
@@ -646,7 +648,7 @@ ItemData ItemNameIndex::parse_item_description_phase(const std::string& descript
} }
if (is_wrapped) { if (is_wrapped) {
if (ret.is_stackable()) { if (ret.is_stackable(this->version)) {
throw runtime_error("stackable items cannot be wrapped"); throw runtime_error("stackable items cannot be wrapped");
} else { } else {
ret.data1[3] |= 0x40; ret.data1[3] |= 0x40;
+5 -1
View File
@@ -19,7 +19,10 @@ public:
std::string name; std::string name;
}; };
ItemNameIndex(std::shared_ptr<const ItemParameterTable> pmt, const std::vector<std::string>& name_coll); ItemNameIndex(
Version version,
std::shared_ptr<const ItemParameterTable> pmt,
const std::vector<std::string>& name_coll);
inline size_t entry_count() const { inline size_t entry_count() const {
return this->primary_identifier_index.size(); return this->primary_identifier_index.size();
@@ -38,6 +41,7 @@ public:
private: private:
ItemData parse_item_description_phase(const std::string& description, bool skip_special) const; ItemData parse_item_description_phase(const std::string& description, bool skip_special) const;
Version version;
std::shared_ptr<const ItemParameterTable> item_parameter_table; std::shared_ptr<const ItemParameterTable> item_parameter_table;
std::unordered_map<uint32_t, std::shared_ptr<const ItemMetadata>> primary_identifier_index; std::unordered_map<uint32_t, std::shared_ptr<const ItemMetadata>> primary_identifier_index;
+3 -3
View File
@@ -111,9 +111,9 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<PSOLFGE
} }
armor.data.data1[5]++; armor.data.data1[5]++;
} else if (item.data.is_wrapped()) { } else if (item.data.is_wrapped(c->version())) {
// Unwrap present // Unwrap present
item.data.unwrap(); item.data.unwrap(c->version());
should_delete_item = false; should_delete_item = false;
} else if (item_identifier == 0x003300) { } else if (item_identifier == 0x003300) {
@@ -250,7 +250,7 @@ void player_use_item(shared_ptr<Client> c, size_t item_index, shared_ptr<PSOLFGE
if (should_delete_item) { if (should_delete_item) {
// Allow overdrafting meseta if the client is not BB, since the server isn't // Allow overdrafting meseta if the client is not BB, since the server isn't
// informed when meseta is added or removed from the bank. // informed when meseta is added or removed from the bank.
player->remove_item(item.data.id, 1, !is_v4(c->version())); player->remove_item(item.data.id, 1, c->version());
} }
} }
+5 -5
View File
@@ -450,7 +450,7 @@ PlayerRecordsBB_Challenge::operator PlayerRecordsV3_Challenge<false>() const {
return ret; return ret;
} }
void PlayerBank::add_item(const ItemData& item) { void PlayerBank::add_item(const ItemData& item, Version version) {
uint32_t pid = item.primary_identifier(); uint32_t pid = item.primary_identifier();
if (pid == MESETA_IDENTIFIER) { if (pid == MESETA_IDENTIFIER) {
@@ -461,7 +461,7 @@ void PlayerBank::add_item(const ItemData& item) {
return; return;
} }
size_t combine_max = item.max_stack_size(); size_t combine_max = item.max_stack_size(version);
if (combine_max > 1) { if (combine_max > 1) {
size_t y; size_t y;
for (y = 0; y < this->num_items; y++) { for (y = 0; y < this->num_items; y++) {
@@ -486,17 +486,17 @@ void PlayerBank::add_item(const ItemData& item) {
} }
auto& last_item = this->items[this->num_items]; auto& last_item = this->items[this->num_items];
last_item.data = item; last_item.data = item;
last_item.amount = (item.max_stack_size() > 1) ? item.data1[5] : 1; last_item.amount = (item.max_stack_size(version) > 1) ? item.data1[5] : 1;
last_item.present = 1; last_item.present = 1;
this->num_items++; this->num_items++;
} }
ItemData PlayerBank::remove_item(uint32_t item_id, uint32_t amount) { ItemData PlayerBank::remove_item(uint32_t item_id, uint32_t amount, Version version) {
size_t index = this->find_item(item_id); size_t index = this->find_item(item_id);
auto& bank_item = this->items[index]; auto& bank_item = this->items[index];
ItemData ret; ItemData ret;
if (amount && (bank_item.data.stack_size() > 1) && (amount < bank_item.data.data1[5])) { if (amount && (bank_item.data.stack_size(version) > 1) && (amount < bank_item.data.data1[5])) {
ret = bank_item.data; ret = bank_item.data;
ret.data1[5] = amount; ret.data1[5] = amount;
bank_item.data.data1[5] -= amount; bank_item.data.data1[5] -= amount;
+2 -2
View File
@@ -100,8 +100,8 @@ struct PlayerBank {
/* 0008 */ parray<PlayerBankItem, 200> items; /* 0008 */ parray<PlayerBankItem, 200> items;
/* 12C8 */ /* 12C8 */
void add_item(const ItemData& item); void add_item(const ItemData& item, Version version);
ItemData remove_item(uint32_t item_id, uint32_t amount); ItemData remove_item(uint32_t item_id, uint32_t amount, Version version);
size_t find_item(uint32_t item_id); size_t find_item(uint32_t item_id);
void sort(); void sort();
+5 -5
View File
@@ -3671,7 +3671,7 @@ static void on_DF_BB(shared_ptr<Client> c, uint16_t command, uint32_t, string& d
? p->challenge_records.ep2_online_award_state ? p->challenge_records.ep2_online_award_state
: p->challenge_records.ep1_online_award_state; : p->challenge_records.ep1_online_award_state;
award_state.rank_award_flags |= cmd.rank_bitmask; award_state.rank_award_flags |= cmd.rank_bitmask;
p->add_item(cmd.item); p->add_item(cmd.item, c->version());
l->on_item_id_generated_externally(cmd.item.id); l->on_item_id_generated_externally(cmd.item.id);
string desc = s->describe_item(Version::BB_V4, cmd.item, false); string desc = s->describe_item(Version::BB_V4, cmd.item, false);
l->log.info("(Challenge mode) Item awarded to player %hhu: %s", c->lobby_client_id, desc.c_str()); l->log.info("(Challenge mode) Item awarded to player %hhu: %s", c->lobby_client_id, desc.c_str());
@@ -4543,9 +4543,9 @@ static void on_D2_V3_BB(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
auto to_p = to_c->character(); auto to_p = to_c->character();
auto from_p = from_c->character(); auto from_p = from_c->character();
for (const auto& trade_item : from_c->pending_item_trade->items) { for (const auto& trade_item : from_c->pending_item_trade->items) {
size_t amount = trade_item.stack_size(); size_t amount = trade_item.stack_size(from_c->version());
auto item = from_p->remove_item(trade_item.id, amount, false); auto item = from_p->remove_item(trade_item.id, amount, from_c->version());
// This is a special case: when the trade is executed, the client // This is a special case: when the trade is executed, the client
// deletes the traded items from its own inventory automatically, so we // deletes the traded items from its own inventory automatically, so we
// should NOT send the 6x29 to that client; we should only send it to // should NOT send the 6x29 to that client; we should only send it to
@@ -4557,7 +4557,7 @@ static void on_D2_V3_BB(shared_ptr<Client> c, uint16_t, uint32_t, string& data)
} }
} }
to_p->add_item(trade_item); to_p->add_item(trade_item, to_c->version());
send_create_inventory_item_to_lobby(to_c, to_c->lobby_client_id, item); send_create_inventory_item_to_lobby(to_c, to_c->lobby_client_id, item);
} }
send_command(to_c, 0xD3, 0x00); send_command(to_c, 0xD3, 0x00);
@@ -4991,7 +4991,7 @@ static void on_EA_BB(shared_ptr<Client> c, uint16_t command, uint32_t flag, stri
} }
} }
if (!reward.reward_item.empty()) { if (!reward.reward_item.empty()) {
c->current_bank().add_item(reward.reward_item); c->current_bank().add_item(reward.reward_item, c->version());
} }
} }
break; break;
+51 -51
View File
@@ -1376,7 +1376,7 @@ static void on_player_drop_item(shared_ptr<Client> c, uint8_t command, uint8_t f
auto l = c->require_lobby(); auto l = c->require_lobby();
auto p = c->character(); auto p = c->character();
auto item = p->remove_item(cmd.item_id, 0, c->version() != Version::BB_V4); auto item = p->remove_item(cmd.item_id, 0, c->version());
l->add_item(cmd.floor, item, cmd.x, cmd.z, 0x00F); l->add_item(cmd.floor, item, cmd.x, cmd.z, 0x00F);
if (l->log.should_log(LogLevel::INFO)) { if (l->log.should_log(LogLevel::INFO)) {
@@ -1438,7 +1438,7 @@ static void on_create_inventory_item_t(shared_ptr<Client> c, uint8_t command, ui
ItemData item = cmd.item_data; ItemData item = cmd.item_data;
item.decode_for_version(c->version()); item.decode_for_version(c->version());
l->on_item_id_generated_externally(item.id); l->on_item_id_generated_externally(item.id);
p->add_item(item); p->add_item(item, c->version());
if (l->log.should_log(LogLevel::INFO)) { if (l->log.should_log(LogLevel::INFO)) {
auto s = c->require_server_state(); auto s = c->require_server_state();
@@ -1511,7 +1511,7 @@ static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint
} }
auto p = c->character(); auto p = c->character();
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != Version::BB_V4); auto item = p->remove_item(cmd.item_id, cmd.amount, c->version());
// If a stack was split, the original item still exists, so the dropped item // If a stack was split, the original item still exists, so the dropped item
// needs a new ID. remove_item signals this by returning an item with an ID // needs a new ID. remove_item signals this by returning an item with an ID
@@ -1523,7 +1523,7 @@ static void on_drop_partial_stack_bb(shared_ptr<Client> c, uint8_t command, uint
// PSOBB sends a 6x29 command after it receives the 6x5D, so we need to add // PSOBB sends a 6x29 command after it receives the 6x5D, so we need to add
// the item back to the player's inventory to correct for this (it will get // the item back to the player's inventory to correct for this (it will get
// removed again by the 6x29 handler) // removed again by the 6x29 handler)
p->add_item(item); p->add_item(item, c->version());
l->add_item(cmd.floor, item, cmd.x, cmd.z, 0x00F); l->add_item(cmd.floor, item, cmd.x, cmd.z, 0x00F);
send_drop_stacked_item_to_lobby(l, item, cmd.floor, cmd.x, cmd.z); send_drop_stacked_item_to_lobby(l, item, cmd.floor, cmd.x, cmd.z);
@@ -1558,7 +1558,7 @@ static void on_buy_shop_item(shared_ptr<Client> c, uint8_t command, uint8_t flag
item.data2d = 0; // Clear the price field item.data2d = 0; // Clear the price field
item.decode_for_version(c->version()); item.decode_for_version(c->version());
l->on_item_id_generated_externally(item.id); l->on_item_id_generated_externally(item.id);
p->add_item(item); p->add_item(item, c->version());
size_t price = s->item_parameter_table(c->version())->price_for_item(item); size_t price = s->item_parameter_table(c->version())->price_for_item(item);
p->remove_meseta(price, c->version() != Version::BB_V4); p->remove_meseta(price, c->version() != Version::BB_V4);
@@ -1674,7 +1674,7 @@ static void on_pick_up_item_generic(
} }
try { try {
p->add_item(fi->data); p->add_item(fi->data, c->version());
} catch (const out_of_range&) { } catch (const out_of_range&) {
// Inventory is full; put the item back where it was // Inventory is full; put the item back where it was
l->log.warning("Player %hu requests to pick up %08" PRIX32 ", but their inventory is full; dropping command", l->log.warning("Player %hu requests to pick up %08" PRIX32 ", but their inventory is full; dropping command",
@@ -1811,8 +1811,8 @@ static void on_feed_mag(
// a 6x29 immediately after to destroy the fed item. So on BB, we should // a 6x29 immediately after to destroy the fed item. So on BB, we should
// remove the fed item here, but on other versions, we allow the following // remove the fed item here, but on other versions, we allow the following
// 6x29 command to do that. // 6x29 command to do that.
if (l->base_version == Version::BB_V4) { if (c->version() == Version::BB_V4) {
p->remove_item(cmd.fed_item_id, 1, false); p->remove_item(cmd.fed_item_id, 1, c->version());
} }
if (l->log.should_log(LogLevel::INFO)) { if (l->log.should_log(LogLevel::INFO)) {
@@ -1899,7 +1899,7 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint
} }
} else { // Deposit item } else { // Deposit item
auto item = p->remove_item(cmd.item_id, cmd.item_amount, c->version() != Version::BB_V4); auto item = p->remove_item(cmd.item_id, cmd.item_amount, c->version());
// If a stack was split, the bank item retains the same item ID as the // If a stack was split, the bank item retains the same item ID as the
// inventory item. This is annoying but doesn't cause any problems // inventory item. This is annoying but doesn't cause any problems
// because we always generate a new item ID when withdrawing from the // because we always generate a new item ID when withdrawing from the
@@ -1907,7 +1907,7 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint
if (item.id == 0xFFFFFFFF) { if (item.id == 0xFFFFFFFF) {
item.id = cmd.item_id; item.id = cmd.item_id;
} }
bank.add_item(item); bank.add_item(item, c->version());
send_destroy_item_to_lobby(c, cmd.item_id, cmd.item_amount, true); send_destroy_item_to_lobby(c, cmd.item_id, cmd.item_amount, true);
if (l->log.should_log(LogLevel::INFO)) { if (l->log.should_log(LogLevel::INFO)) {
@@ -1934,9 +1934,9 @@ static void on_ep3_private_word_select_bb_bank_action(shared_ptr<Client> c, uint
} }
} else { // Take item } else { // Take item
auto item = bank.remove_item(cmd.item_id, cmd.item_amount); auto item = bank.remove_item(cmd.item_id, cmd.item_amount, c->version());
item.id = l->generate_item_id(c->lobby_client_id); item.id = l->generate_item_id(c->lobby_client_id);
p->add_item(item); p->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
if (l->log.should_log(LogLevel::INFO)) { if (l->log.should_log(LogLevel::INFO)) {
@@ -2659,7 +2659,7 @@ void on_adjust_player_meseta_bb(shared_ptr<Client> c, uint8_t, uint8_t, void* da
item.data1[0] = 0x04; item.data1[0] = 0x04;
item.data2d = cmd.amount.load(); item.data2d = cmd.amount.load();
item.id = l->generate_item_id(c->lobby_client_id); item.id = l->generate_item_id(c->lobby_client_id);
p->add_item(item); p->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
} }
} }
@@ -2670,9 +2670,9 @@ void on_item_reward_request_bb(shared_ptr<Client> c, uint8_t, uint8_t, void* dat
ItemData item; ItemData item;
item = cmd.item_data; item = cmd.item_data;
item.enforce_min_stack_size(); item.enforce_min_stack_size(c->version());
item.id = l->generate_item_id(c->lobby_client_id); item.id = l->generate_item_id(c->lobby_client_id);
c->character()->add_item(item); c->character()->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
} }
@@ -2699,7 +2699,7 @@ void on_transfer_item_via_mail_message_bb(shared_ptr<Client> c, uint8_t command,
auto s = c->require_server_state(); auto s = c->require_server_state();
auto p = c->character(); auto p = c->character();
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != Version::BB_V4); auto item = p->remove_item(cmd.item_id, cmd.amount, c->version());
if (l->log.should_log(LogLevel::INFO)) { if (l->log.should_log(LogLevel::INFO)) {
auto name = s->describe_item(c->version(), item, false); auto name = s->describe_item(c->version(), item, false);
@@ -2718,7 +2718,7 @@ void on_transfer_item_via_mail_message_bb(shared_ptr<Client> c, uint8_t command,
(target_c->character(false) != nullptr) && (target_c->character(false) != nullptr) &&
!target_c->config.check_flag(Client::Flag::AT_BANK_COUNTER)) { !target_c->config.check_flag(Client::Flag::AT_BANK_COUNTER)) {
try { try {
target_c->current_bank().add_item(item); target_c->current_bank().add_item(item, target_c->version());
item_sent = true; item_sent = true;
} catch (const runtime_error&) { } catch (const runtime_error&) {
} }
@@ -2730,7 +2730,7 @@ void on_transfer_item_via_mail_message_bb(shared_ptr<Client> c, uint8_t command,
send_command(c, 0x16EA, 0x00000000); send_command(c, 0x16EA, 0x00000000);
// If the item failed to send, add it back to the sender's inventory // If the item failed to send, add it back to the sender's inventory
item.id = l->generate_item_id(0xFF); item.id = l->generate_item_id(0xFF);
p->add_item(item); p->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
} }
} }
@@ -2756,7 +2756,7 @@ void on_exchange_item_for_team_points_bb(shared_ptr<Client> c, uint8_t command,
auto s = c->require_server_state(); auto s = c->require_server_state();
auto p = c->character(); auto p = c->character();
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != Version::BB_V4); auto item = p->remove_item(cmd.item_id, cmd.amount, c->version());
size_t points = s->item_parameter_table_v4->get_item_team_points(item); size_t points = s->item_parameter_table_v4->get_item_team_points(item);
s->team_index->add_member_points(c->license->serial_number, points); s->team_index->add_member_points(c->license->serial_number, points);
@@ -2784,7 +2784,7 @@ static void on_destroy_inventory_item(shared_ptr<Client> c, uint8_t command, uin
auto s = c->require_server_state(); auto s = c->require_server_state();
auto p = c->character(); auto p = c->character();
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != Version::BB_V4); auto item = p->remove_item(cmd.item_id, cmd.amount, c->version());
if (l->log.should_log(LogLevel::INFO)) { if (l->log.should_log(LogLevel::INFO)) {
auto name = s->describe_item(c->version(), item, false); auto name = s->describe_item(c->version(), item, false);
@@ -2876,7 +2876,7 @@ static void on_accept_identify_item_bb(shared_ptr<Client> c, uint8_t command, ui
if (c->bb_identify_result.id != cmd.item_id) { if (c->bb_identify_result.id != cmd.item_id) {
throw runtime_error("accepted item ID does not match previous identify request"); throw runtime_error("accepted item ID does not match previous identify request");
} }
c->character()->add_item(c->bb_identify_result); c->character()->add_item(c->bb_identify_result, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, c->bb_identify_result); send_create_inventory_item_to_lobby(c, c->lobby_client_id, c->bb_identify_result);
c->bb_identify_result.clear(); c->bb_identify_result.clear();
@@ -2893,7 +2893,7 @@ static void on_sell_item_at_shop_bb(shared_ptr<Client> c, uint8_t command, uint8
auto s = c->require_server_state(); auto s = c->require_server_state();
auto p = c->character(); auto p = c->character();
auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != Version::BB_V4); auto item = p->remove_item(cmd.item_id, cmd.amount, c->version());
size_t price = (s->item_parameter_table(c->version())->price_for_item(item) >> 3) * cmd.amount; size_t price = (s->item_parameter_table(c->version())->price_for_item(item) >> 3) * cmd.amount;
p->add_meseta(price); p->add_meseta(price);
@@ -2915,7 +2915,7 @@ static void on_buy_shop_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, void* da
ItemData item; ItemData item;
item = c->bb_shop_contents.at(cmd.shop_type).at(cmd.item_index); item = c->bb_shop_contents.at(cmd.shop_type).at(cmd.item_index);
if (item.is_stackable()) { if (item.is_stackable(c->version())) {
item.data1[5] = cmd.amount; item.data1[5] = cmd.amount;
} else if (cmd.amount != 1) { } else if (cmd.amount != 1) {
throw runtime_error("item is not stackable"); throw runtime_error("item is not stackable");
@@ -2928,7 +2928,7 @@ static void on_buy_shop_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, void* da
item.id = cmd.shop_item_id; item.id = cmd.shop_item_id;
l->on_item_id_generated_externally(item.id); l->on_item_id_generated_externally(item.id);
p->add_item(item); p->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item, true); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item, true);
if (l->log.should_log(LogLevel::INFO)) { if (l->log.should_log(LogLevel::INFO)) {
@@ -3031,15 +3031,15 @@ static void on_quest_exchange_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, vo
auto p = c->character(); auto p = c->character();
size_t found_index = p->inventory.find_item_by_primary_identifier(cmd.find_item.primary_identifier()); size_t found_index = p->inventory.find_item_by_primary_identifier(cmd.find_item.primary_identifier());
auto found_item = p->remove_item(p->inventory.items[found_index].data.id, 1, false); auto found_item = p->remove_item(p->inventory.items[found_index].data.id, 1, c->version());
send_destroy_item_to_lobby(c, found_item.id, 1); send_destroy_item_to_lobby(c, found_item.id, 1);
// TODO: We probably should use an allow-list here to prevent the client // TODO: We probably should use an allow-list here to prevent the client
// from creating arbitrary items if cheat mode is disabled. // from creating arbitrary items if cheat mode is disabled.
ItemData new_item = cmd.replace_item; ItemData new_item = cmd.replace_item;
new_item.enforce_min_stack_size(); new_item.enforce_min_stack_size(c->version());
new_item.id = l->generate_item_id(c->lobby_client_id); new_item.id = l->generate_item_id(c->lobby_client_id);
p->add_item(new_item); p->add_item(new_item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item);
send_quest_function_call(c, cmd.success_function_id); send_quest_function_call(c, cmd.success_function_id);
@@ -3057,10 +3057,10 @@ static void on_wrap_item_bb(shared_ptr<Client> c, uint8_t, uint8_t, void* data,
const auto& cmd = check_size_t<G_WrapItem_BB_6xD6>(data, size); const auto& cmd = check_size_t<G_WrapItem_BB_6xD6>(data, size);
auto p = c->character(); auto p = c->character();
auto item = p->remove_item(cmd.item.id, 1, false); auto item = p->remove_item(cmd.item.id, 1, c->version());
send_destroy_item_to_lobby(c, item.id, 1); send_destroy_item_to_lobby(c, item.id, 1);
item.wrap(); item.wrap(c->version());
p->add_item(item); p->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
} }
} }
@@ -3074,15 +3074,15 @@ static void on_photon_drop_exchange_for_item_bb(shared_ptr<Client> c, uint8_t, u
auto p = c->character(); auto p = c->character();
size_t found_index = p->inventory.find_item_by_primary_identifier(0x031000); size_t found_index = p->inventory.find_item_by_primary_identifier(0x031000);
auto found_item = p->remove_item(p->inventory.items[found_index].data.id, 0, false); auto found_item = p->remove_item(p->inventory.items[found_index].data.id, 0, c->version());
send_destroy_item_to_lobby(c, found_item.id, found_item.stack_size()); send_destroy_item_to_lobby(c, found_item.id, found_item.stack_size(c->version()));
// TODO: We probably should use an allow-list here to prevent the client // TODO: We probably should use an allow-list here to prevent the client
// from creating arbitrary items if cheat mode is disabled. // from creating arbitrary items if cheat mode is disabled.
ItemData new_item = cmd.new_item; ItemData new_item = cmd.new_item;
new_item.enforce_min_stack_size(); new_item.enforce_min_stack_size(c->version());
new_item.id = l->generate_item_id(c->lobby_client_id); new_item.id = l->generate_item_id(c->lobby_client_id);
p->add_item(new_item); p->add_item(new_item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item);
send_quest_function_call(c, cmd.success_function_id); send_quest_function_call(c, cmd.success_function_id);
@@ -3110,13 +3110,13 @@ static void on_photon_drop_exchange_for_s_rank_special_bb(shared_ptr<Client> c,
// consistent in case of error // consistent in case of error
p->inventory.find_item(cmd.item_id); p->inventory.find_item(cmd.item_id);
auto payment_item = p->remove_item(p->inventory.items[payment_item_index].data.id, cost, false); auto payment_item = p->remove_item(p->inventory.items[payment_item_index].data.id, cost, c->version());
send_destroy_item_to_lobby(c, payment_item.id, cost); send_destroy_item_to_lobby(c, payment_item.id, cost);
auto item = p->remove_item(cmd.item_id, 1, false); auto item = p->remove_item(cmd.item_id, 1, c->version());
send_destroy_item_to_lobby(c, item.id, cost); send_destroy_item_to_lobby(c, item.id, cost);
item.data1[2] = cmd.special_type; item.data1[2] = cmd.special_type;
p->add_item(item); p->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
send_quest_function_call(c, cmd.success_function_id); send_quest_function_call(c, cmd.success_function_id);
@@ -3157,14 +3157,14 @@ static void on_secret_lottery_ticket_exchange_bb(shared_ptr<Client> c, uint8_t,
exchange_cmd.amount = 1; exchange_cmd.amount = 1;
send_command_t(c, 0x60, 0x00, exchange_cmd); send_command_t(c, 0x60, 0x00, exchange_cmd);
send_destroy_item_to_lobby(c, slt_item_id, 1); p->remove_item(slt_item_id, 1, c->version());
ItemData item = (s->secret_lottery_results.size() == 1) ItemData item = (s->secret_lottery_results.size() == 1)
? s->secret_lottery_results[0] ? s->secret_lottery_results[0]
: s->secret_lottery_results[l->random_crypt->next() % s->secret_lottery_results.size()]; : s->secret_lottery_results[l->random_crypt->next() % s->secret_lottery_results.size()];
item.enforce_min_stack_size(); item.enforce_min_stack_size(c->version());
item.id = l->generate_item_id(c->lobby_client_id); item.id = l->generate_item_id(c->lobby_client_id);
p->add_item(item); p->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
} }
@@ -3190,7 +3190,7 @@ static void on_photon_crystal_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t
check_size_t<G_ExchangePhotonCrystals_BB_6xDF>(data, size); check_size_t<G_ExchangePhotonCrystals_BB_6xDF>(data, size);
auto p = c->character(); auto p = c->character();
size_t index = p->inventory.find_item_by_primary_identifier(0x031002); size_t index = p->inventory.find_item_by_primary_identifier(0x031002);
auto item = p->remove_item(p->inventory.items[index].data.id, 1, false); auto item = p->remove_item(p->inventory.items[index].data.id, 1, c->version());
send_destroy_item_to_lobby(c, item.id, 1); send_destroy_item_to_lobby(c, item.id, 1);
} }
} }
@@ -3216,7 +3216,7 @@ static void on_quest_F95E_result_bb(shared_ptr<Client> c, uint8_t, uint8_t, void
} else if (item.data1[0] == 0x00) { } else if (item.data1[0] == 0x00) {
item.data1[4] |= 0x80; // Unidentified item.data1[4] |= 0x80; // Unidentified
} else { } else {
item.enforce_min_stack_size(); item.enforce_min_stack_size(c->version());
} }
item.id = l->generate_item_id(0xFF); item.id = l->generate_item_id(0xFF);
@@ -3240,7 +3240,7 @@ static void on_quest_F95F_result_bb(shared_ptr<Client> c, uint8_t, uint8_t, void
} }
size_t index = p->inventory.find_item_by_primary_identifier(0x031004); // Photon Ticket size_t index = p->inventory.find_item_by_primary_identifier(0x031004); // Photon Ticket
auto ticket_item = p->remove_item(p->inventory.items[index].data.id, result.first, false); auto ticket_item = p->remove_item(p->inventory.items[index].data.id, result.first, c->version());
// TODO: Shouldn't we send a 6x29 here? Check if this causes desync in an // TODO: Shouldn't we send a 6x29 here? Check if this causes desync in an
// actual game // actual game
@@ -3252,9 +3252,9 @@ static void on_quest_F95F_result_bb(shared_ptr<Client> c, uint8_t, uint8_t, void
send_command_t(c, 0x60, 0x00, cmd_6xDB); send_command_t(c, 0x60, 0x00, cmd_6xDB);
ItemData new_item = result.second; ItemData new_item = result.second;
new_item.enforce_min_stack_size(); new_item.enforce_min_stack_size(c->version());
new_item.id = l->generate_item_id(c->lobby_client_id); new_item.id = l->generate_item_id(c->lobby_client_id);
p->add_item(new_item); p->add_item(new_item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item);
S_GallonPlanResult_BB_25 out_cmd; S_GallonPlanResult_BB_25 out_cmd;
@@ -3318,7 +3318,7 @@ static void on_quest_F960_result_bb(shared_ptr<Client> c, uint8_t, uint8_t, void
send_command_t(c, 0x60, 0x00, cmd_6xE3); send_command_t(c, 0x60, 0x00, cmd_6xE3);
try { try {
p->add_item(item); p->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
if (c->log.should_log(LogLevel::INFO)) { if (c->log.should_log(LogLevel::INFO)) {
string name = s->describe_item(c->version(), item, false); string name = s->describe_item(c->version(), item, false);
@@ -3340,7 +3340,7 @@ static void on_momoka_item_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t, v
auto p = c->character(); auto p = c->character();
try { try {
size_t found_index = p->inventory.find_item_by_primary_identifier(cmd.find_item.primary_identifier()); size_t found_index = p->inventory.find_item_by_primary_identifier(cmd.find_item.primary_identifier());
auto found_item = p->remove_item(p->inventory.items[found_index].data.id, 1, false); auto found_item = p->remove_item(p->inventory.items[found_index].data.id, 1, c->version());
G_ExchangeItemInQuest_BB_6xDB cmd_6xDB = {{0xDB, 0x04, c->lobby_client_id}, 1, found_item.id, 1}; G_ExchangeItemInQuest_BB_6xDB cmd_6xDB = {{0xDB, 0x04, c->lobby_client_id}, 1, found_item.id, 1};
send_command_t(c, 0x60, 0x00, cmd_6xDB); send_command_t(c, 0x60, 0x00, cmd_6xDB);
@@ -3350,9 +3350,9 @@ static void on_momoka_item_exchange_bb(shared_ptr<Client> c, uint8_t, uint8_t, v
// TODO: We probably should use an allow-list here to prevent the client // TODO: We probably should use an allow-list here to prevent the client
// from creating arbitrary items if cheat mode is disabled. // from creating arbitrary items if cheat mode is disabled.
ItemData new_item = cmd.replace_item; ItemData new_item = cmd.replace_item;
new_item.enforce_min_stack_size(); new_item.enforce_min_stack_size(c->version());
new_item.id = l->generate_item_id(c->lobby_client_id); new_item.id = l->generate_item_id(c->lobby_client_id);
p->add_item(new_item); p->add_item(new_item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item); send_create_inventory_item_to_lobby(c, c->lobby_client_id, new_item);
send_command(c, 0x23, 0x00); send_command(c, 0x23, 0x00);
@@ -3375,10 +3375,10 @@ static void on_upgrade_weapon_attribute_bb(shared_ptr<Client> c, uint8_t, uint8_
uint32_t payment_primary_identifier = cmd.payment_type ? 0x031001 : 0x031000; uint32_t payment_primary_identifier = cmd.payment_type ? 0x031001 : 0x031000;
size_t payment_index = p->inventory.find_item_by_primary_identifier(payment_primary_identifier); size_t payment_index = p->inventory.find_item_by_primary_identifier(payment_primary_identifier);
auto& payment_item = p->inventory.items[payment_index].data; auto& payment_item = p->inventory.items[payment_index].data;
if (payment_item.stack_size() < cmd.payment_count) { if (payment_item.stack_size(c->version()) < cmd.payment_count) {
throw runtime_error("not enough payment items present"); throw runtime_error("not enough payment items present");
} }
p->remove_item(payment_item.id, cmd.payment_count, false); p->remove_item(payment_item.id, cmd.payment_count, c->version());
send_destroy_item_to_lobby(c, payment_item.id, cmd.payment_count); send_destroy_item_to_lobby(c, payment_item.id, cmd.payment_count);
uint8_t attribute_amount = 0; uint8_t attribute_amount = 0;
+5 -5
View File
@@ -396,7 +396,7 @@ PSOBBCharacterFile::SymbolChatEntry PSOBBCharacterFile::DefaultSymbolChatEntry::
// TODO: Eliminate duplication between this function and the parallel function // TODO: Eliminate duplication between this function and the parallel function
// in PlayerBank // in PlayerBank
void PSOBBCharacterFile::add_item(const ItemData& item) { void PSOBBCharacterFile::add_item(const ItemData& item, Version version) {
uint32_t pid = item.primary_identifier(); uint32_t pid = item.primary_identifier();
// Annoyingly, meseta is in the disp data, not in the inventory struct. If the // Annoyingly, meseta is in the disp data, not in the inventory struct. If the
@@ -407,7 +407,7 @@ void PSOBBCharacterFile::add_item(const ItemData& item) {
} }
// Handle combinable items // Handle combinable items
size_t combine_max = item.max_stack_size(); size_t combine_max = item.max_stack_size(version);
if (combine_max > 1) { if (combine_max > 1) {
// Get the item index if there's already a stack of the same item in the // Get the item index if there's already a stack of the same item in the
// player's inventory // player's inventory
@@ -444,13 +444,13 @@ void PSOBBCharacterFile::add_item(const ItemData& item) {
// TODO: Eliminate code duplication between this function and the parallel // TODO: Eliminate code duplication between this function and the parallel
// function in PlayerBank // function in PlayerBank
ItemData PSOBBCharacterFile::remove_item(uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft) { ItemData PSOBBCharacterFile::remove_item(uint32_t item_id, uint32_t amount, Version version) {
ItemData ret; ItemData ret;
// If we're removing meseta (signaled by an invalid item ID), then create a // If we're removing meseta (signaled by an invalid item ID), then create a
// meseta item. // meseta item.
if (item_id == 0xFFFFFFFF) { if (item_id == 0xFFFFFFFF) {
this->remove_meseta(amount, allow_meseta_overdraft); this->remove_meseta(amount, !is_v4(version));
ret.data1[0] = 0x04; ret.data1[0] = 0x04;
ret.data2d = amount; ret.data2d = amount;
return ret; return ret;
@@ -464,7 +464,7 @@ ItemData PSOBBCharacterFile::remove_item(uint32_t item_id, uint32_t amount, bool
// then create a new item and reduce the amount of the existing stack. Note // then create a new item and reduce the amount of the existing stack. Note
// that passing amount == 0 means to remove the entire stack, so this only // that passing amount == 0 means to remove the entire stack, so this only
// applies if amount is nonzero. // applies if amount is nonzero.
if (amount && (inventory_item.data.stack_size() > 1) && if (amount && (inventory_item.data.stack_size(version) > 1) &&
(amount < inventory_item.data.data1[5])) { (amount < inventory_item.data.data1[5])) {
if (is_equipped) { if (is_equipped) {
throw runtime_error("character has a combine item equipped"); throw runtime_error("character has a combine item equipped");
+2 -2
View File
@@ -231,8 +231,8 @@ struct PSOBBCharacterFile {
const PlayerDispDataBBPreview& preview, const PlayerDispDataBBPreview& preview,
std::shared_ptr<const LevelTable> level_table); std::shared_ptr<const LevelTable> level_table);
void add_item(const ItemData& item); void add_item(const ItemData& item, Version version);
ItemData remove_item(uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft); ItemData remove_item(uint32_t item_id, uint32_t amount, Version version);
void add_meseta(uint32_t amount); void add_meseta(uint32_t amount);
void remove_meseta(uint32_t amount, bool allow_overdraft); void remove_meseta(uint32_t amount, bool allow_overdraft);
+18 -11
View File
@@ -1146,25 +1146,25 @@ shared_ptr<ItemNameIndex> ServerState::create_item_name_index_for_version(
Version version, shared_ptr<const ItemParameterTable> pmt, shared_ptr<const TextIndex> text_index) { Version version, shared_ptr<const ItemParameterTable> pmt, shared_ptr<const TextIndex> text_index) {
switch (version) { switch (version) {
case Version::DC_NTE: case Version::DC_NTE:
return make_shared<ItemNameIndex>(pmt, text_index->get(Version::DC_NTE, 0, 2)); return make_shared<ItemNameIndex>(version, pmt, text_index->get(Version::DC_NTE, 0, 2));
case Version::DC_V1_11_2000_PROTOTYPE: case Version::DC_V1_11_2000_PROTOTYPE:
return make_shared<ItemNameIndex>(pmt, text_index->get(Version::DC_V1_11_2000_PROTOTYPE, 1, 2)); return make_shared<ItemNameIndex>(version, pmt, text_index->get(Version::DC_V1_11_2000_PROTOTYPE, 1, 2));
case Version::DC_V1: case Version::DC_V1:
return make_shared<ItemNameIndex>(pmt, text_index->get(Version::DC_V1, 1, 2)); return make_shared<ItemNameIndex>(version, pmt, text_index->get(Version::DC_V1, 1, 2));
case Version::DC_V2: case Version::DC_V2:
return make_shared<ItemNameIndex>(pmt, text_index->get(Version::DC_V2, 1, 3)); return make_shared<ItemNameIndex>(version, pmt, text_index->get(Version::DC_V2, 1, 3));
case Version::PC_NTE: case Version::PC_NTE:
return make_shared<ItemNameIndex>(pmt, text_index->get(Version::PC_NTE, 1, 3)); return make_shared<ItemNameIndex>(version, pmt, text_index->get(Version::PC_NTE, 1, 3));
case Version::PC_V2: case Version::PC_V2:
return make_shared<ItemNameIndex>(pmt, text_index->get(Version::PC_V2, 1, 3)); return make_shared<ItemNameIndex>(version, pmt, text_index->get(Version::PC_V2, 1, 3));
case Version::GC_NTE: case Version::GC_NTE:
return make_shared<ItemNameIndex>(pmt, text_index->get(Version::GC_NTE, 1, 0)); return make_shared<ItemNameIndex>(version, pmt, text_index->get(Version::GC_NTE, 1, 0));
case Version::GC_V3: case Version::GC_V3:
return make_shared<ItemNameIndex>(pmt, text_index->get(Version::GC_V3, 1, 0)); return make_shared<ItemNameIndex>(version, pmt, text_index->get(Version::GC_V3, 1, 0));
case Version::XB_V3: case Version::XB_V3:
return make_shared<ItemNameIndex>(pmt, text_index->get(Version::XB_V3, 1, 0)); return make_shared<ItemNameIndex>(version, pmt, text_index->get(Version::XB_V3, 1, 0));
case Version::BB_V4: case Version::BB_V4:
return make_shared<ItemNameIndex>(pmt, text_index->get(Version::BB_V4, 1, 1)); return make_shared<ItemNameIndex>(version, pmt, text_index->get(Version::BB_V4, 1, 1));
default: default:
return nullptr; return nullptr;
} }
@@ -1180,12 +1180,19 @@ void ServerState::load_item_name_indexes() {
auto pc_v2_index = create_item_name_index_for_version( auto pc_v2_index = create_item_name_index_for_version(
Version::PC_V2, this->item_parameter_table(Version::PC_V2), this->text_index); Version::PC_V2, this->item_parameter_table(Version::PC_V2), this->text_index);
this->set_item_name_index(Version::DC_NTE, pc_v2_index); this->set_item_name_index(Version::DC_NTE, pc_v2_index);
this->set_item_name_index(Version::DC_V1_11_2000_PROTOTYPE, pc_v2_index);
this->set_item_name_index(Version::DC_V1, pc_v2_index); this->set_item_name_index(Version::DC_V1, pc_v2_index);
this->set_item_name_index(Version::DC_V2, pc_v2_index); this->set_item_name_index(Version::DC_V2, pc_v2_index);
this->set_item_name_index(Version::PC_NTE, pc_v2_index); this->set_item_name_index(Version::PC_NTE, pc_v2_index);
this->set_item_name_index(Version::PC_V2, pc_v2_index); this->set_item_name_index(Version::PC_V2, pc_v2_index);
// All tools are stackable on 11/2000, so make a separate index (still using
// V2 data) with the correct version
auto dc_112000_index = make_shared<ItemNameIndex>(
Version::DC_V1_11_2000_PROTOTYPE,
this->item_parameter_table(Version::PC_V2),
this->text_index->get(Version::PC_V2, 1, 3));
this->set_item_name_index(Version::DC_V1_11_2000_PROTOTYPE, dc_112000_index);
auto gc_v3_index = create_item_name_index_for_version( auto gc_v3_index = create_item_name_index_for_version(
Version::GC_V3, this->item_parameter_table(Version::GC_V3), this->text_index); Version::GC_V3, this->item_parameter_table(Version::GC_V3), this->text_index);
this->set_item_name_index(Version::GC_NTE, gc_v3_index); this->set_item_name_index(Version::GC_NTE, gc_v3_index);
+5 -2
View File
@@ -493,12 +493,15 @@ uint8_t language_code_for_char(char language_char) {
} }
} }
size_t max_stack_size_for_item(uint8_t data0, uint8_t data1) { size_t max_stack_size_for_item(Version version, uint8_t data0, uint8_t data1) {
if (data0 == 4) { if (data0 == 4) {
return 999999; return 999999;
} }
if (data0 == 3) { if (data0 == 3) {
if ((data1 < 9) && (data1 != 2)) { if (version == Version::DC_V1_11_2000_PROTOTYPE) {
// All tool items are stackable up to x10 on this version
return 10;
} else if ((data1 < 9) && (data1 != 2)) {
return 10; return 10;
} else if (data1 == 0x10) { } else if (data1 == 0x10) {
return 99; return 99;
+2 -1
View File
@@ -7,6 +7,7 @@
#include <vector> #include <vector>
#include "FileContentsCache.hh" #include "FileContentsCache.hh"
#include "Version.hh"
enum class Episode { enum class Episode {
NONE = 0, NONE = 0,
@@ -32,7 +33,7 @@ enum class GameMode {
const char* name_for_mode(GameMode mode); const char* name_for_mode(GameMode mode);
const char* abbreviation_for_mode(GameMode mode); const char* abbreviation_for_mode(GameMode mode);
size_t max_stack_size_for_item(uint8_t data0, uint8_t data1); size_t max_stack_size_for_item(Version version, uint8_t data0, uint8_t data1);
extern const std::vector<std::string> tech_id_to_name; extern const std::vector<std::string> tech_id_to_name;
extern const std::unordered_map<std::string, uint8_t> name_to_tech_id; extern const std::unordered_map<std::string, uint8_t> name_to_tech_id;