diff --git a/src/ItemCreator.cc b/src/ItemCreator.cc index 66d1480e..fe8faf9f 100644 --- a/src/ItemCreator.cc +++ b/src/ItemCreator.cc @@ -134,46 +134,49 @@ uint8_t ItemCreator::normalize_area_number(uint8_t area) const { } } -ItemData ItemCreator::on_box_item_drop(uint16_t entity_id, uint8_t area) { +ItemCreator::DropResult ItemCreator::on_box_item_drop(uint16_t entity_id, uint8_t area) { return this->destroyed_boxes.count(entity_id) - ? ItemData() + ? DropResult() : this->on_box_item_drop_with_area_norm(this->normalize_area_number(area)); } -ItemData ItemCreator::on_monster_item_drop(uint16_t entity_id, uint32_t enemy_type, uint8_t area) { +ItemCreator::DropResult ItemCreator::on_monster_item_drop(uint16_t entity_id, uint32_t enemy_type, uint8_t area) { return this->destroyed_monsters.count(entity_id) - ? ItemData() + ? DropResult() : this->on_monster_item_drop_with_area_norm(enemy_type, this->normalize_area_number(area)); } -ItemData ItemCreator::on_box_item_drop_with_area_norm(uint8_t area_norm) { +ItemCreator::DropResult ItemCreator::on_box_item_drop_with_area_norm(uint8_t area_norm) { this->log.info("Box drop checks for area_norm %02hhX; random state: %08" PRIX32 " %08" PRIX32, area_norm, this->random_crypt.seed(), this->random_crypt.absolute_offset()); - ItemData item = this->check_rare_specs_and_create_rare_box_item(area_norm); - if (item.empty()) { + DropResult res; + res.item = this->check_rare_specs_and_create_rare_box_item(area_norm); + if (!res.item.empty()) { + res.is_from_rare_table = true; + } else { uint8_t item_class = this->get_rand_from_weighted_tables_2d_vertical(this->pt->box_item_class_prob_table, area_norm); this->log.info("Item class is %02hhX", item_class); switch (item_class) { case 0: // Weapon - item.data1[0] = 0; + res.item.data1[0] = 0; break; case 1: // Armor - item.data1[0] = 1; - item.data1[1] = 1; + res.item.data1[0] = 1; + res.item.data1[1] = 1; break; case 2: // Shield - item.data1[0] = 1; - item.data1[1] = 2; + res.item.data1[0] = 1; + res.item.data1[1] = 2; break; case 3: // Unit - item.data1[0] = 1; - item.data1[1] = 3; + res.item.data1[0] = 1; + res.item.data1[1] = 3; break; case 4: // Tool - item.data1[0] = 3; + res.item.data1[0] = 3; break; case 5: // Meseta - item.data1[0] = 4; + res.item.data1[0] = 4; break; case 6: // Nothing break; @@ -181,16 +184,16 @@ ItemData ItemCreator::on_box_item_drop_with_area_norm(uint8_t area_norm) { throw logic_error("this should be impossible"); } if (item_class < 6) { - this->generate_common_item_variances(area_norm, item); + this->generate_common_item_variances(area_norm, res.item); } } - return item; + return res; } -ItemData ItemCreator::on_monster_item_drop_with_area_norm(uint32_t enemy_type, uint8_t area_norm) { +ItemCreator::DropResult ItemCreator::on_monster_item_drop_with_area_norm(uint32_t enemy_type, uint8_t area_norm) { if (enemy_type > 0x58) { this->log.warning("Invalid enemy type: %" PRIX32, enemy_type); - return ItemData(); + return DropResult(); } this->log.info("Enemy type: %" PRIX32 "; random state: %08" PRIX32 " %08" PRIX32, enemy_type, this->random_crypt.seed(), this->random_crypt.absolute_offset()); @@ -198,13 +201,16 @@ ItemData ItemCreator::on_monster_item_drop_with_area_norm(uint32_t enemy_type, u uint8_t drop_sample = this->rand_int(100); if (drop_sample >= type_drop_prob) { this->log.info("Drop not chosen (%hhu >= %hhu)", drop_sample, type_drop_prob); - return ItemData(); + return DropResult(); } else { this->log.info("Drop chosen (%hhu < %hhu)", drop_sample, type_drop_prob); } - ItemData item = this->check_rare_spec_and_create_rare_enemy_item(enemy_type, area_norm); - if (item.empty()) { + DropResult res; + res.item = this->check_rare_spec_and_create_rare_enemy_item(enemy_type, area_norm); + if (!res.item.empty()) { + res.is_from_rare_table = true; + } else { uint32_t item_class_determinant = this->should_allow_meseta_drops() ? this->rand_int(3) @@ -229,34 +235,34 @@ ItemData ItemCreator::on_monster_item_drop_with_area_norm(uint32_t enemy_type, u switch (item_class) { case 0: // Weapon - item.data1[0] = 0x00; + res.item.data1[0] = 0x00; break; case 1: // Armor - item.data1w[0] = 0x0101; + res.item.data1w[0] = 0x0101; break; case 2: // Shield - item.data1w[0] = 0x0201; + res.item.data1w[0] = 0x0201; break; case 3: // Unit - item.data1w[0] = 0x0301; + res.item.data1w[0] = 0x0301; break; case 4: // Tool - item.data1[0] = 0x03; + res.item.data1[0] = 0x03; break; case 5: // Meseta - item.data1[0] = 0x04; - item.data2d = this->choose_meseta_amount(this->pt->enemy_meseta_ranges, enemy_type) & 0xFFFF; + res.item.data1[0] = 0x04; + res.item.data2d = this->choose_meseta_amount(this->pt->enemy_meseta_ranges, enemy_type) & 0xFFFF; break; default: - return item; + return res; } - if (item.data1[0] != 0x04) { - this->generate_common_item_variances(area_norm, item); + if (res.item.data1[0] != 0x04) { + this->generate_common_item_variances(area_norm, res.item); } } - return item; + return res; } ItemData ItemCreator::check_rare_specs_and_create_rare_box_item(uint8_t area_norm) { @@ -1663,20 +1669,21 @@ void ItemCreator::generate_weapon_shop_item_bonus2(ItemData& item, size_t player } } -ItemData ItemCreator::on_specialized_box_item_drop( +ItemCreator::DropResult ItemCreator::on_specialized_box_item_drop( uint16_t entity_id, uint8_t area, float def_z, uint32_t def0, uint32_t def1, uint32_t def2) { if (this->destroyed_boxes.count(entity_id)) { - return ItemData(); + return DropResult(); } - ItemData item = this->base_item_for_specialized_box(def0, def1, def2); + DropResult res; + res.item = this->base_item_for_specialized_box(def0, def1, def2); if (def_z == 0.0f) { - uint16_t type = item.data1w[0]; - item.clear(); - item.data1w[0] = type; - this->generate_common_item_variances(this->normalize_area_number(area), item); + uint16_t type = res.item.data1w[0]; + res.item.clear(); + res.item.data1w[0] = type; + this->generate_common_item_variances(this->normalize_area_number(area), res.item); } - return item; + return res; } ItemData ItemCreator::base_item_for_specialized_box(uint32_t def0, uint32_t def1, uint32_t def2) { diff --git a/src/ItemCreator.hh b/src/ItemCreator.hh index 3d672b4d..c8bcf6bb 100644 --- a/src/ItemCreator.hh +++ b/src/ItemCreator.hh @@ -31,9 +31,14 @@ public: void set_random_state(uint32_t seed, uint32_t absolute_offset); void clear_destroyed_entities(); - ItemData on_monster_item_drop(uint16_t entity_id, uint32_t enemy_type, uint8_t area); - ItemData on_box_item_drop(uint16_t entity_id, uint8_t area); - ItemData on_specialized_box_item_drop(uint16_t entity_id, uint8_t area, float def_z, uint32_t def0, uint32_t def1, uint32_t def2); + struct DropResult { + ItemData item; + bool is_from_rare_table = false; + }; + + DropResult on_monster_item_drop(uint16_t entity_id, uint32_t enemy_type, uint8_t area); + DropResult on_box_item_drop(uint16_t entity_id, uint8_t area); + DropResult on_specialized_box_item_drop(uint16_t entity_id, uint8_t area, float def_z, uint32_t def0, uint32_t def1, uint32_t def2); void set_monster_destroyed(uint16_t entity_id); void set_box_destroyed(uint16_t entity_id); @@ -87,8 +92,8 @@ private: bool are_rare_drops_allowed() const; uint8_t normalize_area_number(uint8_t area) const; - ItemData on_monster_item_drop_with_area_norm(uint32_t enemy_type, uint8_t area_norm); - ItemData on_box_item_drop_with_area_norm(uint8_t area_norm); + DropResult on_monster_item_drop_with_area_norm(uint32_t enemy_type, uint8_t area_norm); + DropResult on_box_item_drop_with_area_norm(uint8_t area_norm); uint32_t rand_int(uint64_t max); float rand_float_0_1_from_crypt(); diff --git a/src/ReceiveSubcommands.cc b/src/ReceiveSubcommands.cc index 1bc4bc82..401b88a2 100644 --- a/src/ReceiveSubcommands.cc +++ b/src/ReceiveSubcommands.cc @@ -2113,7 +2113,7 @@ static void on_entity_drop_item_request(shared_ptr c, uint8_t command, u cmd.effective_area = in_cmd.floor; } - auto generate_item = [&]() -> ItemData { + auto generate_item = [&]() -> ItemCreator::DropResult { if (cmd.rt_index == 0x30) { if (l->map) { auto& object = l->map->objects.at(cmd.entity_id); @@ -2177,33 +2177,37 @@ static void on_entity_drop_item_request(shared_ptr c, uint8_t command, u case Lobby::DropMode::SERVER_DUPLICATE: { // TODO: In SERVER_DUPLICATE mode, should we reduce the rates for rare // items? Maybe by a factor of l->count_clients()? - auto item = generate_item(); - if (item.empty()) { + auto res = generate_item(); + if (res.item.empty()) { l->log.info("No item was created"); } else { - string name = s->describe_item(l->base_version, item, false); + string name = s->describe_item(l->base_version, res.item, false); l->log.info("Entity %04hX (area %02hX) created item %s", cmd.entity_id.load(), cmd.effective_area, name.c_str()); if (l->drop_mode == Lobby::DropMode::SERVER_DUPLICATE) { for (const auto& lc : l->clients) { if (lc && ((cmd.rt_index == 0x30) || (lc->floor == cmd.floor))) { - item.id = l->generate_item_id(0xFF); + res.item.id = l->generate_item_id(0xFF); l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for %s", - item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load(), lc->channel.name.c_str()); - l->add_item(cmd.floor, item, cmd.x, cmd.z, (1 << lc->lobby_client_id)); - send_drop_item_to_channel(s, lc->channel, item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id); - send_rare_notification_if_needed(lc, item); + res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load(), lc->channel.name.c_str()); + l->add_item(cmd.floor, res.item, cmd.x, cmd.z, (1 << lc->lobby_client_id)); + send_drop_item_to_channel(s, lc->channel, res.item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id); + if (res.is_from_rare_table) { + send_rare_notification_if_needed(lc, res.item); + } } } } else { - item.id = l->generate_item_id(0xFF); + res.item.id = l->generate_item_id(0xFF); l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for all clients", - item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load()); - l->add_item(cmd.floor, item, cmd.x, cmd.z, 0x00F); - send_drop_item_to_lobby(l, item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id); - for (auto lc : l->clients) { - if (lc) { - send_rare_notification_if_needed(lc, item); + res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load()); + l->add_item(cmd.floor, res.item, cmd.x, cmd.z, 0x00F); + send_drop_item_to_lobby(l, res.item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id); + if (res.is_from_rare_table) { + for (auto lc : l->clients) { + if (lc) { + send_rare_notification_if_needed(lc, res.item); + } } } } @@ -2213,18 +2217,20 @@ static void on_entity_drop_item_request(shared_ptr c, uint8_t command, u case Lobby::DropMode::SERVER_PRIVATE: { for (const auto& lc : l->clients) { if (lc && ((cmd.rt_index == 0x30) || (lc->floor == cmd.floor))) { - auto item = generate_item(); - if (item.empty()) { + auto res = generate_item(); + if (res.item.empty()) { l->log.info("No item was created for %s", lc->channel.name.c_str()); } else { - string name = s->describe_item(l->base_version, item, false); + string name = s->describe_item(l->base_version, res.item, false); l->log.info("Entity %04hX (area %02hX) created item %s", cmd.entity_id.load(), cmd.effective_area, name.c_str()); - item.id = l->generate_item_id(0xFF); + res.item.id = l->generate_item_id(0xFF); l->log.info("Creating item %08" PRIX32 " at %02hhX:%g,%g for %s", - item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load(), lc->channel.name.c_str()); - l->add_item(cmd.floor, item, cmd.x, cmd.z, (1 << lc->lobby_client_id)); - send_drop_item_to_channel(s, lc->channel, item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id); - send_rare_notification_if_needed(lc, item); + res.item.id.load(), cmd.floor, cmd.x.load(), cmd.z.load(), lc->channel.name.c_str()); + l->add_item(cmd.floor, res.item, cmd.x, cmd.z, (1 << lc->lobby_client_id)); + send_drop_item_to_channel(s, lc->channel, res.item, cmd.rt_index != 0x30, cmd.floor, cmd.x, cmd.z, cmd.entity_id); + if (res.is_from_rare_table) { + send_rare_notification_if_needed(lc, res.item); + } } } }