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
+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 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);
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;
item.decode_for_version(c->version());
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)) {
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 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
// 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
// the item back to the player's inventory to correct for this (it will get
// 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);
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.decode_for_version(c->version());
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);
p->remove_meseta(price, c->version() != Version::BB_V4);
@@ -1674,7 +1674,7 @@ static void on_pick_up_item_generic(
}
try {
p->add_item(fi->data);
p->add_item(fi->data, c->version());
} catch (const out_of_range&) {
// 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",
@@ -1811,8 +1811,8 @@ static void on_feed_mag(
// 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
// 6x29 command to do that.
if (l->base_version == Version::BB_V4) {
p->remove_item(cmd.fed_item_id, 1, false);
if (c->version() == Version::BB_V4) {
p->remove_item(cmd.fed_item_id, 1, c->version());
}
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
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
// inventory item. This is annoying but doesn't cause any problems
// 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) {
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);
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
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);
p->add_item(item);
p->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
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.data2d = cmd.amount.load();
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);
}
}
@@ -2670,9 +2670,9 @@ void on_item_reward_request_bb(shared_ptr<Client> c, uint8_t, uint8_t, void* dat
ItemData item;
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);
c->character()->add_item(item);
c->character()->add_item(item, c->version());
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 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)) {
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->config.check_flag(Client::Flag::AT_BANK_COUNTER)) {
try {
target_c->current_bank().add_item(item);
target_c->current_bank().add_item(item, target_c->version());
item_sent = true;
} 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);
// If the item failed to send, add it back to the sender's inventory
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);
}
}
@@ -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 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);
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 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)) {
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) {
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);
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 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;
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;
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;
} else if (cmd.amount != 1) {
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;
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);
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();
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);
// TODO: We probably should use an allow-list here to prevent the client
// from creating arbitrary items if cheat mode is disabled.
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);
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_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);
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);
item.wrap();
p->add_item(item);
item.wrap(c->version());
p->add_item(item, c->version());
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();
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);
send_destroy_item_to_lobby(c, found_item.id, found_item.stack_size());
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(c->version()));
// TODO: We probably should use an allow-list here to prevent the client
// from creating arbitrary items if cheat mode is disabled.
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);
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_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
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);
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);
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_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;
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)
? s->secret_lottery_results[0]
: 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);
p->add_item(item);
p->add_item(item, c->version());
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);
auto p = c->character();
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);
}
}
@@ -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) {
item.data1[4] |= 0x80; // Unidentified
} else {
item.enforce_min_stack_size();
item.enforce_min_stack_size(c->version());
}
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
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
// 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);
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);
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);
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);
try {
p->add_item(item);
p->add_item(item, c->version());
send_create_inventory_item_to_lobby(c, c->lobby_client_id, item);
if (c->log.should_log(LogLevel::INFO)) {
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();
try {
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};
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
// from creating arbitrary items if cheat mode is disabled.
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);
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_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;
size_t payment_index = p->inventory.find_item_by_primary_identifier(payment_primary_identifier);
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");
}
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);
uint8_t attribute_amount = 0;