diff --git a/common/item.cpp b/common/item.cpp index 35de0e773..a43b5d12b 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -1441,6 +1441,7 @@ ItemInst::ItemInst(const Item_Struct* item, int16 charges) { m_ornamenticon = 0; m_ornamentidfile = 0; m_ornament_hero_model = 0; + m_recast_timestamp = 0; } ItemInst::ItemInst(SharedDatabase *db, uint32 item_id, int16 charges) { @@ -1466,6 +1467,7 @@ ItemInst::ItemInst(SharedDatabase *db, uint32 item_id, int16 charges) { m_ornamenticon = 0; m_ornamentidfile = 0; m_ornament_hero_model = 0; + m_recast_timestamp = 0; } ItemInst::ItemInst(ItemInstTypes use_type) { @@ -1486,6 +1488,7 @@ ItemInst::ItemInst(ItemInstTypes use_type) { m_ornamenticon = 0; m_ornamentidfile = 0; m_ornament_hero_model = 0; + m_recast_timestamp = 0; } // Make a copy of an ItemInst object @@ -1539,6 +1542,7 @@ ItemInst::ItemInst(const ItemInst& copy) m_ornamenticon = copy.m_ornamenticon; m_ornamentidfile = copy.m_ornamentidfile; m_ornament_hero_model = copy.m_ornament_hero_model; + m_recast_timestamp = copy.m_recast_timestamp; } // Clean up container contents diff --git a/common/item.h b/common/item.h index 0868e8c5f..fd30b5ea0 100644 --- a/common/item.h +++ b/common/item.h @@ -403,6 +403,8 @@ public: void SetOrnamentationIDFile(uint32 ornament_idfile) { m_ornamentidfile = ornament_idfile; } uint32 GetOrnamentHeroModel(int32 material_slot = -1) const; void SetOrnamentHeroModel(uint32 ornament_hero_model) { m_ornament_hero_model = ornament_hero_model; } + uint32 GetRecastTimestamp() const { return m_recast_timestamp; } + void SetRecastTimestamp(uint32 in) { m_recast_timestamp = in; } void Initialize(SharedDatabase *db = nullptr); void ScaleItem(); @@ -450,6 +452,7 @@ protected: uint32 m_ornamenticon; uint32 m_ornamentidfile; uint32 m_ornament_hero_model; + uint32 m_recast_timestamp; // // Items inside of this item (augs or contents); diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 9494c5048..772e60517 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -5000,7 +5000,7 @@ namespace RoF hdr.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 0; hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index d4c619942..6947ded15 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -5069,7 +5069,7 @@ namespace RoF2 hdr.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 0; hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 2e96719be..98f7faccd 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -3548,7 +3548,7 @@ namespace SoD hdr.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 0; hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 44a87d3cc..b8a84d993 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -2872,7 +2872,7 @@ namespace SoF hdr.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 0; hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index aa5d3d7fb..3be2d7d70 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -2004,7 +2004,7 @@ namespace Titanium inst->IsScaling() ? inst->GetExp() / 100 : 0, //merchant_slot, //instance ID, bullshit for now (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot, - 0, // item recast timer timestamp field (aka..last_cast_time field in SoF+ clients) + inst->GetRecastTimestamp(), (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? 1 : 0) : charges), inst->IsAttuned() ? 1 : 0, 0 diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 3f297534d..f772509d0 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -3793,7 +3793,7 @@ namespace UF hdr.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 0; hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; diff --git a/common/ptimer.h b/common/ptimer.h index 829a015b2..d12b6779d 100644 --- a/common/ptimer.h +++ b/common/ptimer.h @@ -79,6 +79,7 @@ public: inline const uint32 GetTimerTime() const { return timer_time; } inline const uint32 GetStartTime() const { return start_time; } inline const pTimerType GetType() const { return _type; } + inline const uint32 GetReadyTimestamp() const { return start_time + timer_time; } inline bool Enabled() { return enabled; } diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 159ce13fc..08bb0103f 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -498,6 +498,8 @@ bool SharedDatabase::GetInventory(uint32 char_id, Inventory *inv) return false; } + auto timestamps = GetItemRecastTimestamps(char_id); + for (auto row = results.begin(); row != results.end(); ++row) { int16 slot_id = atoi(row[0]); uint32 item_id = atoi(row[1]); @@ -582,6 +584,13 @@ bool SharedDatabase::GetInventory(uint32 char_id, Inventory *inv) else inst->SetCharges(charges); + if (item->RecastDelay) { + if (timestamps.count(item->RecastType)) + inst->SetRecastTimestamp(timestamps.at(item->RecastType)); + else + inst->SetRecastTimestamp(0); + } + if (item->ItemClass == ItemClassCommon) { for (int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { if (aug[i]) @@ -725,6 +734,27 @@ bool SharedDatabase::GetInventory(uint32 account_id, char *name, Inventory *inv) return GetSharedBank(account_id, inv, false); } +std::map SharedDatabase::GetItemRecastTimestamps(uint32 char_id) +{ + std::map timers; + std::string query = StringFormat("SELECT recast_type,timestamp FROM character_item_recast WHERE id=%u", char_id); + auto results = QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return timers; + + for (auto row = results.begin(); row != results.end(); ++row) + timers[atoul(row[0])] = atoul(row[1]); + return timers; // RVO or move assigned +} + +void SharedDatabase::ClearOldRecastTimestamps(uint32 char_id) +{ + // This actually isn't strictly live-like. Live your recast timestamps are forever + std::string query = + StringFormat("DELETE FROM character_item_recast WHERE id = %u and timestamp < UNIX_TIMESTAMP()", char_id); + QueryDatabase(query); +} + void SharedDatabase::GetItemsCount(int32 &item_count, uint32 &max_id) { item_count = -1; diff --git a/common/shareddb.h b/common/shareddb.h index 073c385e8..99eba666a 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -11,6 +11,7 @@ #include "fixed_memory_variable_hash_set.h" #include +#include class EvolveInfo; class Inventory; @@ -69,6 +70,8 @@ class SharedDatabase : public Database bool SetSharedPlatinum(uint32 account_id, int32 amount_to_add); bool GetInventory(uint32 char_id, Inventory* inv); bool GetInventory(uint32 account_id, char* name, Inventory* inv); + std::map GetItemRecastTimestamps(uint32 char_id); + void ClearOldRecastTimestamps(uint32 char_id); bool SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin); diff --git a/common/version.h b/common/version.h index 7044ba6e5..5841a4119 100644 --- a/common/version.h +++ b/common/version.h @@ -30,7 +30,7 @@ Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9072 +#define CURRENT_BINARY_DATABASE_VERSION 9073 #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ #ifndef WIN32 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 820a79bd4..be99178a3 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -326,6 +326,7 @@ 9070|2015_01_28_quest_debug_log_category.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Quest Debug'|empty| 9071|2015_01_29_merc_stats_table_update.sql|SHOW COLUMNS FROM `merc_stats` LIKE 'statscale'|empty| 9072|2015_01_30_merc_attack_delay.sql|SHOW COLUMNS FROM `merc_stats` LIKE 'attack_delay'|empty| +9073|2015_01_31_character_item_recast.sql|SHOW TABLES LIKE 'character_item_recast'|empty| # 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/2015_01_31_character_item_recast.sql b/utils/sql/git/required/2015_01_31_character_item_recast.sql new file mode 100644 index 000000000..fbc222608 --- /dev/null +++ b/utils/sql/git/required/2015_01_31_character_item_recast.sql @@ -0,0 +1,7 @@ +CREATE TABLE `character_item_recast` ( + `id` int(11) UNSIGNED NOT NULL DEFAULT 0, + `recast_type` smallint(11) UNSIGNED NOT NULL DEFAULT 0, + `timestamp` int(11) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY(`id`, `recast_type`), + KEY `id` (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = latin1; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c4e449db5..a3062891b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1392,6 +1392,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (RuleB(Character, SharedBankPlat)) m_pp.platinum_shared = database.GetSharedPlatinum(this->AccountID()); + database.ClearOldRecastTimestamps(cid); /* Clear out our old recast timestamps to keep the DB clean */ loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 36e585ea2..0dd4ed1f3 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -965,6 +965,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a Save(); } + auto timestamps = database.GetItemRecastTimestamps(client->CharacterID()); outapp->priority = 6; client->QueuePacket(outapp); safe_delete(outapp); @@ -973,6 +974,8 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a const Item_Struct* item = database.GetItem(pkitem); ItemInst* inst = database.CreateItem(item, item->MaxCharges); if(inst) { + if (item->RecastDelay) + inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); client->SendItemPacket(EmuConstants::CORPSE_BEGIN, inst, ItemPacketLoot); safe_delete(inst); } @@ -1004,6 +1007,8 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a if(client && item) { ItemInst* inst = database.CreateItem(item, item_data->charges, item_data->aug_1, item_data->aug_2, item_data->aug_3, item_data->aug_4, item_data->aug_5, item_data->aug_6, item_data->attuned); if(inst) { + if (item->RecastDelay) + inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); // MainGeneral1 is the corpse inventory start offset for Ti(EMu) - CORPSE_END = MainGeneral1 + MainCursor client->SendItemPacket(i + EmuConstants::CORPSE_BEGIN, inst, ItemPacketLoot); safe_delete(inst); @@ -1460,4 +1465,4 @@ void Corpse::LoadPlayerCorpseDecayTime(uint32 corpse_db_id){ else { corpse_graveyard_timer.SetTimer(3000); } -} \ No newline at end of file +} diff --git a/zone/spells.cpp b/zone/spells.cpp index bf4da6688..1b1cb16f4 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1242,6 +1242,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, { //Can we start the timer here? I don't see why not. CastToClient()->GetPTimers().Start((pTimerItemStart + recasttype), recastdelay); + database.UpdateItemRecastTimestamps(CastToClient()->CharacterID(), recasttype, + CastToClient()->GetPTimers().Get(pTimerItemStart + recasttype)->GetReadyTimestamp()); } } @@ -2270,11 +2272,15 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 { ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot); if(itm && itm->GetItem()->RecastDelay > 0){ - CastToClient()->GetPTimers().Start((pTimerItemStart + itm->GetItem()->RecastType), itm->GetItem()->RecastDelay); + auto recast_type = itm->GetItem()->RecastType; + CastToClient()->GetPTimers().Start((pTimerItemStart + recast_type), itm->GetItem()->RecastDelay); + database.UpdateItemRecastTimestamps( + CastToClient()->CharacterID(), recast_type, + CastToClient()->GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp()); EQApplicationPacket *outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; ird->recast_delay = itm->GetItem()->RecastDelay; - ird->recast_type = itm->GetItem()->RecastType; + ird->recast_type = recast_type; CastToClient()->QueuePacket(outapp); safe_delete(outapp); } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index bb81a0fc4..56236f1b8 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2999,6 +2999,14 @@ void ZoneDatabase::RemoveTempFactions(Client *client) { QueryDatabase(query); } +void ZoneDatabase::UpdateItemRecastTimestamps(uint32 char_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); + QueryDatabase(query); +} + void ZoneDatabase::LoadPetInfo(Client *client) { // Load current pet and suspended pet diff --git a/zone/zonedb.h b/zone/zonedb.h index 5c3ed3d0e..5e1a55248 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -256,6 +256,7 @@ public: void LoadPetInfo(Client *c); void SavePetInfo(Client *c); void RemoveTempFactions(Client *c); + void UpdateItemRecastTimestamps(uint32 char_id, uint32 recast_type, uint32 timestamp); /* Character Data Loaders */ bool LoadCharacterFactionValues(uint32 character_id, faction_map & val_list);