diff --git a/src/ItemParameterTable.cc b/src/ItemParameterTable.cc index 46139e33..7117d43c 100644 --- a/src/ItemParameterTable.cc +++ b/src/ItemParameterTable.cc @@ -327,7 +327,7 @@ size_t ItemParameterTable::price_for_item(const ItemData& item) const { size_t special_stars = this->get_special_stars(item.data1[4]); double special_stars_factor = 1000.0 * special_stars * special_stars; - return special_stars_factor + (atp_factor * (bonus_factor / 100.0)); + return special_stars_factor + ((atp_factor * bonus_factor) / 100.0); } case 1: { diff --git a/src/Player.cc b/src/Player.cc index 19786b43..b66d3fb4 100644 --- a/src/Player.cc +++ b/src/Player.cc @@ -573,10 +573,7 @@ void SavedPlayerDataBB::add_item(const PlayerInventoryItem& item) { // Annoyingly, meseta is in the disp data, not in the inventory struct. If the // item is meseta, we have to modify disp instead. if (pid == MESETA_IDENTIFIER) { - this->disp.stats.meseta += item.data.data2d; - if (this->disp.stats.meseta > 999999) { - this->disp.stats.meseta = 999999; - } + this->add_meseta(item.data.data2d); return; } @@ -657,13 +654,7 @@ PlayerInventoryItem SavedPlayerDataBB::remove_item( // If we're removing meseta (signaled by an invalid item ID), then create a // meseta item. if (item_id == 0xFFFFFFFF) { - if (amount <= this->disp.stats.meseta) { - this->disp.stats.meseta -= amount; - } else if (allow_meseta_overdraft) { - this->disp.stats.meseta = 0; - } else { - throw out_of_range("player does not have enough meseta"); - } + this->remove_meseta(amount, allow_meseta_overdraft); ret.data.data1[0] = 0x04; ret.data.data2d = amount; return ret; @@ -697,6 +688,20 @@ PlayerInventoryItem SavedPlayerDataBB::remove_item( return ret; } +void SavedPlayerDataBB::add_meseta(uint32_t amount) { + this->disp.stats.meseta = min(static_cast(this->disp.stats.meseta) + amount, 999999); +} + +void SavedPlayerDataBB::remove_meseta(uint32_t amount, bool allow_overdraft) { + if (amount <= this->disp.stats.meseta) { + this->disp.stats.meseta -= amount; + } else if (allow_overdraft) { + this->disp.stats.meseta = 0; + } else { + throw out_of_range("player does not have enough meseta"); + } +} + PlayerBankItem PlayerBank::remove_item(uint32_t item_id, uint32_t amount) { PlayerBankItem ret; diff --git a/src/Player.hh b/src/Player.hh index b214da80..fc3171f7 100644 --- a/src/Player.hh +++ b/src/Player.hh @@ -452,6 +452,8 @@ struct SavedPlayerDataBB { // .nsc file format void add_item(const PlayerInventoryItem& item); PlayerInventoryItem remove_item( uint32_t item_id, uint32_t amount, bool allow_meseta_overdraft); + void add_meseta(uint32_t amount); + void remove_meseta(uint32_t amount, bool allow_overdraft); void print_inventory(FILE* stream) const; } __attribute__((packed)); diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index b0c7edc5..cb22d221 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -833,7 +833,7 @@ static void on_drop_partial_stack_bb(shared_ptr, } } -static void on_buy_shop_item(shared_ptr, +static void on_buy_shop_item(shared_ptr s, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size) { const auto& cmd = check_size_t(data, size); @@ -853,17 +853,21 @@ static void on_buy_shop_item(shared_ptr, if (c->version() == GameVersion::GC) { item.data.bswap_data2_if_mag(); } - c->game_data.player()->add_item(item); + auto p = c->game_data.player(); + p->add_item(item); + + size_t price = s->item_parameter_table->price_for_item(item.data); auto name = item.data.name(false); - l->log.info("Player %hu bought item %08" PRIX32 " (%s) from shop", - cmd.header.client_id.load(), item.data.id.load(), name.c_str()); + l->log.info("Player %hu bought item %08" PRIX32 " (%s) from shop (%zu Meseta)", + cmd.header.client_id.load(), item.data.id.load(), name.c_str(), price); if (c->options.debug) { string name = item.data.name(true); send_text_message_printf(c, "$C5BUY %08" PRIX32 "\n%s", item.data.id.load(), name.c_str()); } - c->game_data.player()->print_inventory(stderr); + p->remove_meseta(price, c->version() != GameVersion::BB); + p->print_inventory(stderr); } forward_subcommand_with_mag_bswap_t(l, c, command, flag, cmd); @@ -1701,15 +1705,14 @@ static void on_sell_item_at_shop_bb(shared_ptr s, throw logic_error("item tracking not enabled in BB game"); } - auto item = c->game_data.player()->remove_item( - cmd.item_id, cmd.amount, c->version() != GameVersion::BB); + auto p = c->game_data.player(); + auto item = p->remove_item(cmd.item_id, cmd.amount, c->version() != GameVersion::BB); size_t price = (s->item_parameter_table->price_for_item(item.data) >> 3) * cmd.amount; - c->game_data.player()->disp.stats.meseta = min( - c->game_data.player()->disp.stats.meseta + price, 999999); + p->add_meseta(price); auto name = item.data.name(false); - l->log.info("Inventory item %hu:%08" PRIX32 " destroyed via sale (%s)", - c->lobby_client_id, cmd.item_id.load(), name.c_str()); + l->log.info("Inventory item %hu:%08" PRIX32 " (%s) destroyed via sale (%zu Meseta)", + c->lobby_client_id, cmd.item_id.load(), name.c_str(), price); c->game_data.player()->print_inventory(stderr); if (c->options.debug) { string name = item.data.name(true); @@ -1739,19 +1742,17 @@ static void on_buy_shop_item_bb(shared_ptr, size_t price = item.data.data2d * cmd.amount; item.data.data2d = 0; - if (c->game_data.player()->disp.stats.meseta < price) { - throw runtime_error("player does not have enough money"); - } - c->game_data.player()->disp.stats.meseta -= price; + auto p = c->game_data.player(); + p->remove_meseta(price, false); item.data.id = cmd.inventory_item_id; - c->game_data.player()->add_item(item); + p->add_item(item); send_create_inventory_item(l, c, item.data); auto name = item.data.name(false); l->log.info("Inventory item %hu:%08" PRIX32 " created via purchase (%s) for %zu meseta", c->lobby_client_id, cmd.inventory_item_id.load(), name.c_str(), price); - c->game_data.player()->print_inventory(stderr); + p->print_inventory(stderr); if (c->options.debug) { string name = item.data.name(true); send_text_message_printf(c, "$C5CREATE/BUY %08" PRIX32 "\n-%zu Meseta\n%s", @@ -1762,20 +1763,13 @@ static void on_buy_shop_item_bb(shared_ptr, static void on_medical_center_bb(shared_ptr, shared_ptr l, shared_ptr c, uint8_t, uint8_t, const void*, size_t) { - if (l->version == GameVersion::BB) { - if (c->game_data.player()->disp.stats.meseta < 10) { - throw runtime_error("insufficient funds"); - } - c->game_data.player()->disp.stats.meseta -= 10; + c->game_data.player()->remove_meseta(10, false); } } //////////////////////////////////////////////////////////////////////////////// -// Subcommands are described by four fields: the minimum size and maximum size (in DWORDs), -// the handler function, and flags that tell when to allow the command. See command-input-subs.h -// for more information on flags. The maximum size is not enforced if it's zero. typedef void (*subcommand_handler_t)(shared_ptr s, shared_ptr l, shared_ptr c, uint8_t command, uint8_t flag, const void* data, size_t size);