diff --git a/common/eq_constants.h b/common/eq_constants.h index ce65ab59b..fe4f516de 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -79,6 +79,8 @@ #define ANIM_DEATH 0x73 #define ANIM_LOOT 0x69 +constexpr int16 RECAST_TYPE_UNLINKED_ITEM = -1; + typedef enum { eaStanding = 0, eaSitting, //1 diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 979c8b86e..982cbafd7 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4545,7 +4545,7 @@ struct ItemVerifyReply_Struct { struct ItemRecastDelay_Struct { /*000*/ uint32 recast_delay; // in seconds /*004*/ uint32 recast_type; -/*008*/ uint32 unknown008; +/*008*/ bool ignore_casting_requirement; //Ignores recast times allows items to be reset? /*012*/ }; diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 0f949f861..57a707ecf 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -721,10 +721,14 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) inst->SetCharges(charges); if (item->RecastDelay) { - if (timestamps.count(item->RecastType)) + if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->RecastType)) { inst->SetRecastTimestamp(timestamps.at(item->RecastType)); - else + } else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) { + inst->SetRecastTimestamp(timestamps.at(item->ID)); + } + else { inst->SetRecastTimestamp(0); + } } if (item->IsClassCommon()) { diff --git a/common/version.h b/common/version.h index 370fd400a..41545c3c0 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9217 +#define CURRENT_BINARY_DATABASE_VERSION 9218 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9037 #endif diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 1302574d3..8ed8d0a12 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -471,6 +471,7 @@ 9215|2023_01_08_zone_max_level.sql|SHOW COLUMNS FROM `zone` LIKE 'max_level'|empty| 9216|2023_01_15_merc_data.sql|SHOW TABLES LIKE 'mercs'|empty| 9217|2023_01_15_chatchannel_reserved_names.sql|SHOW TABLES LIKE 'chatchannel_reserved_names'|empty| +9218|2023_01_24_item_recast.sql|show columns from character_item_recast like '%recast_type%'|contains|smallint # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2023_01_24_item_recast.sql b/utils/sql/git/required/2023_01_24_item_recast.sql new file mode 100644 index 000000000..f2722af1c --- /dev/null +++ b/utils/sql/git/required/2023_01_24_item_recast.sql @@ -0,0 +1,2 @@ +ALTER TABLE `character_item_recast` + CHANGE COLUMN `recast_type` `recast_type` INT(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `id`; \ No newline at end of file diff --git a/zone/client.cpp b/zone/client.cpp index ada87526e..c549378d3 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -10446,8 +10446,8 @@ int Client::CountItem(uint32 item_id) { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, }; - const size_t size = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < size; ++slot_index) { + 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) { item = GetInv().GetItem(slot_id); if (item && item->GetID() == item_id) { @@ -10459,6 +10459,45 @@ int Client::CountItem(uint32 item_id) return quantity; } +void Client::ResetItemCooldown(uint32 item_id) +{ + EQ::ItemInstance *item = nullptr; + const EQ::ItemData* item_d = database.GetItem(item_id); + if (!item_d) { + return; + } + int recast_type = item_d->RecastType; + bool found_item = false; + + 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}, + { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, + { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, + { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, + { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, + }; + 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) { + item = GetInv().GetItem(slot_id); + if (item) { + item_d = item->GetItem(); + if (item_d && item->GetID() == item_id || (item_d->RecastType != RECAST_TYPE_UNLINKED_ITEM && item_d->RecastType == recast_type)) { + item->SetRecastTimestamp(0); + DeleteItemRecastTimer(item_d->ID); + SendItemPacket(slot_id, item, ItemPacketCharmUpdate); + found_item = true; + } + } + } + } + if (!found_item) { + DeleteItemRecastTimer(item_id); //We didn't find the item but we still want to remove the timer + } +} + void Client::RemoveItem(uint32 item_id, uint32 quantity) { EQ::ItemInstance *item = nullptr; @@ -10472,8 +10511,8 @@ void Client::RemoveItem(uint32 item_id, uint32 quantity) { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, }; int16 removed_count = 0; - const size_t size = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < size; ++slot_index) { + 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) { if (removed_count == quantity) { break; diff --git a/zone/client.h b/zone/client.h index 2bb016fe6..627624726 100644 --- a/zone/client.h +++ b/zone/client.h @@ -966,6 +966,7 @@ public: void SendCursorBuffer(); void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true); int CountItem(uint32 item_id); + void ResetItemCooldown(uint32 item_id); void RemoveItem(uint32 item_id, uint32 quantity = 1); bool SwapItem(MoveItem_Struct* move_in); void SwapItemResync(MoveItem_Struct* move_slots); @@ -1516,8 +1517,9 @@ public: void SendReloadCommandMessages(); - void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0); + void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0, bool in_ignore_casting_requirement = false); void SetItemRecastTimer(int32 spell_id, uint32 inventory_slot); + void DeleteItemRecastTimer(uint32 item_id); bool HasItemRecastTimer(int32 spell_id, uint32 inventory_slot); inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQ::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4b4b8f758..7aa3d435b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -8927,9 +8927,12 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) { if (item->RecastDelay > 0) { - if (!GetPTimers().Expired(&database, (pTimerItemStart + item->RecastType), false)) { + if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && !GetPTimers().Expired(&database, (pTimerItemStart + item->RecastType), false)) { SendItemRecastTimer(item->RecastType); //Problem: When you loot corpse, recast display is not present. This causes it to display again. Could not get to display when sending from looting. - MessageString(Chat::Red, SPELL_RECAST); + SendSpellBarEnable(item->Click.Effect); + LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); + return; + } else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && !GetPTimers().Expired(&database, (pTimerNegativeItemReuse * item->ID), false)) { SendSpellBarEnable(item->Click.Effect); LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); return; @@ -8972,11 +8975,16 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) { if (augitem->RecastDelay > 0) { - if (!GetPTimers().Expired(&database, (pTimerItemStart + augitem->RecastType), false)) { + if (augitem->RecastType != RECAST_TYPE_UNLINKED_ITEM && !GetPTimers().Expired(&database, (pTimerItemStart + augitem->RecastType), false)) { LogSpells("Casting of [{}] canceled: item spell reuse timer from augment not expired", spell_id); MessageString(Chat::Red, SPELL_RECAST); SendSpellBarEnable(augitem->Click.Effect); return; + } else if (augitem->RecastType == RECAST_TYPE_UNLINKED_ITEM && !GetPTimers().Expired(&database, (pTimerNegativeItemReuse * augitem->ID), false)) { + MessageString(Chat::Red, SPELL_RECAST); + SendSpellBarEnable(augitem->Click.Effect); + LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); + return; } } diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 4e7715dc9..66cb21d93 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1209,8 +1209,13 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a auto pkinst = database.CreateItem(pkitem, pkitem->MaxCharges); if (pkinst) { - if (pkitem->RecastDelay) - pkinst->SetRecastTimestamp(timestamps.count(pkitem->RecastType) ? timestamps.at(pkitem->RecastType) : 0); + if (pkitem->RecastDelay) { + if (pkitem->RecastType != RECAST_TYPE_UNLINKED_ITEM) { + pkinst->SetRecastTimestamp(timestamps.count(pkitem->RecastType) ? timestamps.at(pkitem->RecastType) : 0); + } else { + pkinst->SetRecastTimestamp(timestamps.count(pkitem->ID) ? timestamps.at(pkitem->ID) : 0); + } + } LogInventory("MakeLootRequestPackets() Slot [{}], Item [{}]", EQ::invslot::CORPSE_BEGIN, pkitem->Name); @@ -1264,8 +1269,13 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a if (!inst) continue; - if (item->RecastDelay) - inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); + if (item->RecastDelay) { + if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM) { + inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); + } else { + inst->SetRecastTimestamp(timestamps.count(item->ID) ? timestamps.at(item->ID) : 0); + } + } LogInventory("MakeLootRequestPackets() Slot [{}], Item [{}]", loot_slot, item->Name); @@ -1477,6 +1487,16 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app) // get count for task update before it's mutated by AutoPutLootInInventory int count = inst->IsStackable() ? inst->GetCharges() : 1; + //Set recast on item when looting it! + auto timestamps = database.GetItemRecastTimestamps(client->CharacterID()); + const auto* d = inst->GetItem(); + if (d->RecastDelay) { + if (d->RecastType != RECAST_TYPE_UNLINKED_ITEM) { + inst->SetRecastTimestamp(timestamps.count(d->RecastType) ? timestamps.at(d->RecastType) : 0); + } else { + inst->SetRecastTimestamp(timestamps.count(d->ID) ? timestamps.at(d->ID) : 0); + } + } /* First add it to the looter - this will do the bag contents too */ if (lootitem->auto_loot > 0) { diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 397bc3863..fbf2d87a5 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -714,6 +714,15 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // in any other situation just use charges as passed EQ::ItemInstance* inst = database.CreateItem(item, charges); + auto timestamps = database.GetItemRecastTimestamps(CharacterID()); + const auto* d = inst->GetItem(); + if (d->RecastDelay) { + if (d->RecastType != RECAST_TYPE_UNLINKED_ITEM) { + inst->SetRecastTimestamp(timestamps.count(d->RecastType) ? timestamps.at(d->RecastType) : 0); + } else { + inst->SetRecastTimestamp(timestamps.count(d->ID) ? timestamps.at(d->ID) : 0); + } + } if(inst == nullptr) { Message(Chat::Red, "An unknown server error has occurred and your item was not created."); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index bc5884427..242fc6e71 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -3014,6 +3014,12 @@ void Lua_Client::CampAllBots(uint8 class_id) self->CampAllBots(class_id); } +void Lua_Client::ResetItemCooldown(uint32 item_id) +{ + Lua_Safe_Call_Void(); + self->ResetItemCooldown(item_id); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -3373,6 +3379,7 @@ luabind::scope lua_register_client() { .def("ResetCastbarCooldownBySlot", (void(Lua_Client::*)(int))&Lua_Client::ResetCastbarCooldownBySlot) .def("ResetCastbarCooldownBySpellID", (void(Lua_Client::*)(uint32))&Lua_Client::ResetCastbarCooldownBySpellID) .def("ResetDisciplineTimer", (void(Lua_Client::*)(uint32))&Lua_Client::ResetDisciplineTimer) + .def("ResetItemCooldown", (void(Lua_Client::*)(uint32))&Lua_Client::ResetItemCooldown) .def("ResetTrade", (void(Lua_Client::*)(void))&Lua_Client::ResetTrade) .def("RewardFaction", (void(Lua_Client::*)(int,int))&Lua_Client::RewardFaction) .def("Save", (void(Lua_Client::*)(int))&Lua_Client::Save) diff --git a/zone/lua_client.h b/zone/lua_client.h index 187b841a4..efba961c7 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -462,6 +462,7 @@ public: bool CanEnterZone(std::string zone_short_name); bool CanEnterZone(std::string zone_short_name, int16 instance_version); void SendPath(Lua_Mob target); + void ResetItemCooldown(uint32 item_id); void ApplySpell(int spell_id); void ApplySpell(int spell_id, int duration); diff --git a/zone/object.cpp b/zone/object.cpp index be6974934..1add7adff 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -507,9 +507,15 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) cursordelete = true; // otherwise, we delete the new one } - if (item->RecastDelay) - m_inst->SetRecastTimestamp( - database.GetItemRecastTimestamp(sender->CharacterID(), item->RecastType)); + if (item->RecastDelay) { + if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM) { + m_inst->SetRecastTimestamp( + database.GetItemRecastTimestamp(sender->CharacterID(), item->RecastType)); + } else { + m_inst->SetRecastTimestamp( + database.GetItemRecastTimestamp(sender->CharacterID(), item->ID)); + } + } std::string export_string = fmt::format("{}", item->ID); std::vector args; diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 117437e90..856f000a1 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -2872,6 +2872,11 @@ void Perl_Client_CampAllBots(Client* self, uint8 class_id) self->CampAllBots(class_id); } +void Perl_Client_ResetItemCooldown(Client* self, uint32 item_id) +{ + self->ResetItemCooldown(item_id); +} + void perl_register_client() { perl::interpreter perl(PERL_GET_THX); @@ -3231,6 +3236,7 @@ void perl_register_client() package.add("ResetCastbarCooldownBySlot", &Perl_Client_ResetCastbarCooldownBySlot); package.add("ResetCastbarCooldownBySpellID", &Perl_Client_ResetCastbarCooldownBySpellID); package.add("ResetDisciplineTimer", &Perl_Client_ResetDisciplineTimer); + package.add("ResetItemCooldown", &Perl_Client_ResetItemCooldown); package.add("ResetTrade", &Perl_Client_ResetTrade); package.add("Save", &Perl_Client_Save); package.add("SaveBackup", &Perl_Client_SaveBackup); diff --git a/zone/spells.cpp b/zone/spells.cpp index a7876e312..b9fd84abc 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -6342,9 +6342,9 @@ void Client::SendSpellAnim(uint16 target_id, uint16 spell_id) entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles)); } -void Client::SendItemRecastTimer(int32 recast_type, uint32 recast_delay) +void Client::SendItemRecastTimer(int32 recast_type, uint32 recast_delay, bool in_ignore_casting_requirement) { - if (recast_type == -1) { + if (recast_type == RECAST_TYPE_UNLINKED_ITEM) { return; } @@ -6357,6 +6357,7 @@ void Client::SendItemRecastTimer(int32 recast_type, uint32 recast_delay) ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; ird->recast_delay = recast_delay; ird->recast_type = static_cast(recast_type); + ird->ignore_casting_requirement = in_ignore_casting_requirement; //True allows reset of item cast timers QueuePacket(outapp); safe_delete(outapp); } @@ -6369,10 +6370,12 @@ void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot) int recast_delay = 0; int recast_type = 0; bool from_augment = false; + int item_casting = 0; if (!item) { return; } + item_casting = item->GetItem()->ID; //Check primary item. if (item->GetItem()->RecastDelay > 0) { @@ -6396,6 +6399,7 @@ void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot) recast_delay = aug_i->GetItem()->RecastDelay; recast_type = aug_i->GetItem()->RecastType; from_augment = true; + item_casting = aug_i->GetItem()->ID; break; } } @@ -6409,14 +6413,21 @@ void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot) recast_delay = std::max(recast_delay, 0); if (recast_delay > 0) { - - GetPTimers().Start((pTimerItemStart + recast_type), static_cast(recast_delay)); - if (recast_type != -1) { - database.UpdateItemRecastTimestamps( + + if (recast_type != RECAST_TYPE_UNLINKED_ITEM) { + GetPTimers().Start((pTimerItemStart + recast_type), static_cast(recast_delay)); + database.UpdateItemRecast( CharacterID(), recast_type, GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp() ); + } else if (recast_type == RECAST_TYPE_UNLINKED_ITEM) { + GetPTimers().Start((pTimerNegativeItemReuse * item_casting), static_cast(recast_delay)); + database.UpdateItemRecast( + CharacterID(), + item_casting, + GetPTimers().Get(pTimerNegativeItemReuse * item_casting)->GetReadyTimestamp() + ); } if (!from_augment) { @@ -6425,12 +6436,32 @@ void Client::SetItemRecastTimer(int32 spell_id, uint32 inventory_slot) } } +void Client::DeleteItemRecastTimer(uint32 item_id) +{ + const auto* d = database.GetItem(item_id); + + if (!d) { + return; + } + + const auto recast_type = d->RecastType != RECAST_TYPE_UNLINKED_ITEM ? d->RecastType : item_id; + const int timer_id = d->RecastType != RECAST_TYPE_UNLINKED_ITEM ? (pTimerItemStart + recast_type) : (pTimerNegativeItemReuse * item_id); + + database.DeleteItemRecast(CharacterID(), recast_type); + GetPTimers().Clear(&database, timer_id); + + if (recast_type != RECAST_TYPE_UNLINKED_ITEM) { + SendItemRecastTimer(recast_type, 1, true); + } +} + bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot) { EQ::ItemInstance *item = CastToClient()->GetInv().GetItem(inventory_slot); int recast_delay = 0; int recast_type = 0; + int item_id = 0; bool from_augment = false; if (!item) { @@ -6445,6 +6476,7 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot) if (item->GetItem()->RecastDelay > 0) { recast_type = item->GetItem()->RecastType; recast_delay = item->GetItem()->RecastDelay; + item_id = item->GetItem()->ID; } //Check augmenent else { @@ -6463,6 +6495,7 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot) if (aug_i->GetItem() && aug_i->GetItem()->RecastDelay > 0) { recast_delay = aug_i->GetItem()->RecastDelay; recast_type = aug_i->GetItem()->RecastType; + item_id = aug_i->GetItem()->ID; } break; } @@ -6473,7 +6506,9 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot) return false; } //if time is not expired, then it exists and therefore we have a recast on this item. - if (!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recast_type), false)) { + if (recast_type != RECAST_TYPE_UNLINKED_ITEM && !CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recast_type), false)) { + return true; + } else if (recast_type == RECAST_TYPE_UNLINKED_ITEM && !CastToClient()->GetPTimers().Expired(&database, (pTimerNegativeItemReuse * item_id), false)) { return true; } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 14d664fa7..d1e22ce1f 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3333,11 +3333,24 @@ void ZoneDatabase::RemoveTempFactions(Client *client) { QueryDatabase(query); } -void ZoneDatabase::UpdateItemRecastTimestamps(uint32 char_id, uint32 recast_type, uint32 timestamp) +void ZoneDatabase::UpdateItemRecast(uint32 character_id, uint32 recast_type, uint32 timestamp) { - std::string query = - StringFormat("REPLACE INTO character_item_recast (id, recast_type, timestamp) VALUES (%u, %u, %u)", char_id, - recast_type, timestamp); + const auto query = fmt::format( + "REPLACE INTO character_item_recast (id, recast_type, timestamp) VALUES ({}, {}, {})", + character_id, + recast_type, + timestamp + ); + QueryDatabase(query); +} + +void ZoneDatabase::DeleteItemRecast(uint32 character_id, uint32 recast_type) +{ + const auto query = fmt::format( + "DELETE FROM character_item_recast WHERE id = {} AND recast_type = {}", + character_id, + recast_type + ); QueryDatabase(query); } diff --git a/zone/zonedb.h b/zone/zonedb.h index 7d612fabf..56d67849c 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -369,7 +369,8 @@ public: void LoadPetInfo(Client *c); void SavePetInfo(Client *c); void RemoveTempFactions(Client *c); - void UpdateItemRecastTimestamps(uint32 char_id, uint32 recast_type, uint32 timestamp); + void UpdateItemRecast(uint32 char_id, uint32 recast_type, uint32 timestamp); + void DeleteItemRecast(uint32 char_id, uint32 recast_type); bool DeleteCharacterAAs(uint32 character_id); bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id);