diff --git a/common/ruletypes.h b/common/ruletypes.h index b8ca75c33..e2df23f4b 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -229,6 +229,7 @@ RULE_BOOL(Character, GroupInvitesRequireTarget, false, "Enable to require player RULE_BOOL(Character, PlayerTradingLoreFeedback, true, "If enabled, during a player to player trade, if lore items exist, it will output which items.") RULE_INT(Character, MendAlwaysSucceedValue, 199, "Value at which mend will always succeed its skill check. Default: 199") RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false") +RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/zone/client.cpp b/zone/client.cpp index d272c5a26..14bef0930 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -186,7 +186,8 @@ Client::Client(EQStreamInterface *ieqs) : Mob( consent_throttle_timer(2000), tmSitting(0), parcel_timer(RuleI(Parcel, ParcelDeliveryDelay)), - lazy_load_bank_check_timer(1000) + lazy_load_bank_check_timer(1000), + bandolier_throttle_timer(0) { for (auto client_filter = FilterNone; client_filter < _FilterCount; client_filter = eqFilterType(client_filter + 1)) { SetFilter(client_filter, FilterShow); @@ -12706,3 +12707,26 @@ bool Client::TakeMoneyFromPPWithOverFlow(uint64 copper, bool update_client) RecalcWeight(); return true; } + +void Client::SendTopLevelInventory() +{ + EQ::ItemInstance* inst = nullptr; + + static const int16 slots[][2] = { + { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, + { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, + { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END } + }; + + const auto& inv = GetInv(); + + const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]); + for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) { + for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { + inst = inv.GetItem(slot_id); + if (inst) { + SendItemPacket(slot_id, inst, ItemPacketType::ItemPacketTrade); + } + } + } +} diff --git a/zone/client.h b/zone/client.h index 0336798ba..b79d8b5cd 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2012,6 +2012,8 @@ private: void ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm); void ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); + void SendTopLevelInventory(); + glm::vec4 m_ZoneSummonLocation; uint16 zonesummon_id; uint8 zonesummon_ignorerestrictions; @@ -2057,6 +2059,7 @@ private: Timer pick_lock_timer; Timer parcel_timer; //Used to limit the number of parcels to one every 30 seconds (default). Changable via rule. Timer lazy_load_bank_check_timer; + Timer bandolier_throttle_timer; bool m_lazy_load_bank = false; int m_lazy_load_sent_bank_slots = 0; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2eb688f5e..151048410 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -3642,29 +3642,39 @@ void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) { // Although there are three different structs for OP_Bandolier, they are all the same size. - // if (app->size != sizeof(BandolierCreate_Struct)) { LogDebug("Size mismatch in OP_Bandolier expected [{}] got [{}]", sizeof(BandolierCreate_Struct), app->size); DumpPacket(app); return; } - BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; + auto bs = (BandolierCreate_Struct*) app->pBuffer; - switch (bs->Action) - { - case bandolierCreate: - CreateBandolier(app); - break; - case bandolierRemove: - RemoveBandolier(app); - break; - case bandolierSet: - SetBandolier(app); - break; - default: - LogDebug("Unknown Bandolier action [{}]", bs->Action); - break; + switch (bs->Action) { + case bandolierCreate: + CreateBandolier(app); + break; + case bandolierRemove: + RemoveBandolier(app); + break; + case bandolierSet: + if (bandolier_throttle_timer.GetDuration() && !bandolier_throttle_timer.Check()) { + Message( + Chat::White, + fmt::format( + "You may only modify your bandolier once every {}.", + Strings::ToLower(Strings::MillisecondsToTime(RuleI(Character, BandolierSwapDelay))) + ).c_str() + ); + SendTopLevelInventory(); + break; + } + + SetBandolier(app); + break; + default: + LogDebug("Unknown Bandolier action [{}]", bs->Action); + break; } } diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 030770a78..30f4c31da 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -3431,6 +3431,11 @@ void Client::SetBandolier(const EQApplicationPacket *app) } } } + + if (RuleI(Character, BandolierSwapDelay) > 0) { + bandolier_throttle_timer.Start(RuleI(Character, BandolierSwapDelay)); + } + // finally, recalculate any stat bonuses from the item change CalcBonuses(); } @@ -4913,4 +4918,4 @@ bool Client::FindNumberOfFreeInventorySlotsWithSizeCheck(std::vector