diff --git a/common/database.h b/common/database.h index 226d471c9..4e5613a48 100644 --- a/common/database.h +++ b/common/database.h @@ -166,6 +166,7 @@ public: void GetCharactersInInstance(uint16 instance_id, std::list &character_ids); void PurgeExpiredInstances(); void SetInstanceDuration(uint16 instance_id, uint32 new_duration); + void CleanupInstanceCorpses(); /* Adventure related. */ diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index 0fd55df3a..a81d12cf9 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5270,6 +5270,20 @@ MODIFY COLUMN `level` tinyint(3) UNSIGNED NOT NULL FIRST, MODIFY COLUMN `class` tinyint(2) UNSIGNED NOT NULL AFTER `level`; )", .content_schema_update = true + }, + ManifestEntry{ + .version = 9259, + .description = "2024_01_13_corpse_rez_overhaul.sql", + .check = "SHOW COLUMNS FROM `character_corpses` LIKE 'rez_time'", + .condition = "empty", + .match = "", + .sql = R"( +ALTER TABLE `character_corpses` +ADD COLUMN `rez_time` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `wc_9`, +ADD COLUMN `gm_exp` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `rez_time`, +ADD COLUMN `killed_by` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `gm_exp`, +ADD COLUMN `rezzable` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 AFTER `killed_by`; +)" } // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ diff --git a/common/database_instances.cpp b/common/database_instances.cpp index 5a0b8c83a..7d90f0923 100644 --- a/common/database_instances.cpp +++ b/common/database_instances.cpp @@ -570,3 +570,23 @@ void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration) InstanceListRepository::UpdateOne(*this, i); } + +void Database::CleanupInstanceCorpses() { + auto l = InstanceListRepository::GetWhere( + *this, + "never_expires = 0" + ); + + if (l.empty()) { + return; + } + + std::vector instance_ids; + for (const auto& e : l) { + instance_ids.emplace_back(std::to_string(e.id)); + } + + const auto imploded_instance_ids = Strings::Implode(",", instance_ids); + + CharacterCorpsesRepository::BuryInstances(*this, imploded_instance_ids); +} diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index d158fbc36..d9d4cb7ae 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -140,6 +140,7 @@ namespace Logs { DataBuckets, Zoning, EqTime, + Corpses, MaxCategoryID /* Don't Remove this */ }; @@ -239,6 +240,7 @@ namespace Logs { "DataBuckets", "Zoning", "EqTime", + "Corpses", }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index b4c90bbbb..70c652414 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -824,6 +824,16 @@ OutF(LogSys, Logs::Detail, Logs::EqTime, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogCorpses(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::Corpses))\ + OutF(LogSys, Logs::General, Logs::Corpses, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogCorpsesDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::Corpses))\ + OutF(LogSys, Logs::Detail, Logs::Corpses, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.IsLogEnabled(debug_level, log_category))\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/repositories/base/base_character_corpses_repository.h b/common/repositories/base/base_character_corpses_repository.h index b2cbfe96f..dd8cacb5b 100644 --- a/common/repositories/base/base_character_corpses_repository.h +++ b/common/repositories/base/base_character_corpses_repository.h @@ -66,6 +66,10 @@ public: uint32_t wc_7; uint32_t wc_8; uint32_t wc_9; + uint32_t rez_time; + uint32_t gm_exp; + uint32_t killed_by; + uint8_t rezzable; }; static std::string PrimaryKey() @@ -123,6 +127,10 @@ public: "wc_7", "wc_8", "wc_9", + "rez_time", + "gm_exp", + "killed_by", + "rezzable", }; } @@ -176,6 +184,10 @@ public: "wc_7", "wc_8", "wc_9", + "rez_time", + "gm_exp", + "killed_by", + "rezzable", }; } @@ -263,6 +275,10 @@ public: e.wc_7 = 0; e.wc_8 = 0; e.wc_9 = 0; + e.rez_time = 0; + e.gm_exp = 0; + e.killed_by = 0; + e.rezzable = 0; return e; } @@ -346,6 +362,10 @@ public: e.wc_7 = row[44] ? static_cast(strtoul(row[44], nullptr, 10)) : 0; e.wc_8 = row[45] ? static_cast(strtoul(row[45], nullptr, 10)) : 0; e.wc_9 = row[46] ? static_cast(strtoul(row[46], nullptr, 10)) : 0; + e.rez_time = row[47] ? static_cast(strtoul(row[47], nullptr, 10)) : 0; + e.gm_exp = row[48] ? static_cast(strtoul(row[48], nullptr, 10)) : 0; + e.killed_by = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; + e.rezzable = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; return e; } @@ -425,6 +445,10 @@ public: v.push_back(columns[44] + " = " + std::to_string(e.wc_7)); v.push_back(columns[45] + " = " + std::to_string(e.wc_8)); v.push_back(columns[46] + " = " + std::to_string(e.wc_9)); + v.push_back(columns[47] + " = " + std::to_string(e.rez_time)); + v.push_back(columns[48] + " = " + std::to_string(e.gm_exp)); + v.push_back(columns[49] + " = " + std::to_string(e.killed_by)); + v.push_back(columns[50] + " = " + std::to_string(e.rezzable)); auto results = db.QueryDatabase( fmt::format( @@ -493,6 +517,10 @@ public: v.push_back(std::to_string(e.wc_7)); v.push_back(std::to_string(e.wc_8)); v.push_back(std::to_string(e.wc_9)); + v.push_back(std::to_string(e.rez_time)); + v.push_back(std::to_string(e.gm_exp)); + v.push_back(std::to_string(e.killed_by)); + v.push_back(std::to_string(e.rezzable)); auto results = db.QueryDatabase( fmt::format( @@ -569,6 +597,10 @@ public: v.push_back(std::to_string(e.wc_7)); v.push_back(std::to_string(e.wc_8)); v.push_back(std::to_string(e.wc_9)); + v.push_back(std::to_string(e.rez_time)); + v.push_back(std::to_string(e.gm_exp)); + v.push_back(std::to_string(e.killed_by)); + v.push_back(std::to_string(e.rezzable)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -649,6 +681,10 @@ public: e.wc_7 = row[44] ? static_cast(strtoul(row[44], nullptr, 10)) : 0; e.wc_8 = row[45] ? static_cast(strtoul(row[45], nullptr, 10)) : 0; e.wc_9 = row[46] ? static_cast(strtoul(row[46], nullptr, 10)) : 0; + e.rez_time = row[47] ? static_cast(strtoul(row[47], nullptr, 10)) : 0; + e.gm_exp = row[48] ? static_cast(strtoul(row[48], nullptr, 10)) : 0; + e.killed_by = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; + e.rezzable = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; all_entries.push_back(e); } @@ -720,6 +756,10 @@ public: e.wc_7 = row[44] ? static_cast(strtoul(row[44], nullptr, 10)) : 0; e.wc_8 = row[45] ? static_cast(strtoul(row[45], nullptr, 10)) : 0; e.wc_9 = row[46] ? static_cast(strtoul(row[46], nullptr, 10)) : 0; + e.rez_time = row[47] ? static_cast(strtoul(row[47], nullptr, 10)) : 0; + e.gm_exp = row[48] ? static_cast(strtoul(row[48], nullptr, 10)) : 0; + e.killed_by = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; + e.rezzable = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; all_entries.push_back(e); } @@ -841,6 +881,10 @@ public: v.push_back(std::to_string(e.wc_7)); v.push_back(std::to_string(e.wc_8)); v.push_back(std::to_string(e.wc_9)); + v.push_back(std::to_string(e.rez_time)); + v.push_back(std::to_string(e.gm_exp)); + v.push_back(std::to_string(e.killed_by)); + v.push_back(std::to_string(e.rezzable)); auto results = db.QueryDatabase( fmt::format( @@ -910,6 +954,10 @@ public: v.push_back(std::to_string(e.wc_7)); v.push_back(std::to_string(e.wc_8)); v.push_back(std::to_string(e.wc_9)); + v.push_back(std::to_string(e.rez_time)); + v.push_back(std::to_string(e.gm_exp)); + v.push_back(std::to_string(e.killed_by)); + v.push_back(std::to_string(e.rezzable)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/repositories/character_corpses_repository.h b/common/repositories/character_corpses_repository.h index 111238a96..72f833a2d 100644 --- a/common/repositories/character_corpses_repository.h +++ b/common/repositories/character_corpses_repository.h @@ -65,7 +65,7 @@ public: fmt::format( "UPDATE `{}` SET `is_buried` = 1 WHERE `is_buried` = 0 AND (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(time_of_death)) > {} AND time_of_death != 0", TableName(), - RuleI(Character, CorpseDecayTimeMS) / 1000 + RuleI(Character, CorpseDecayTime) / 1000 ) ); diff --git a/common/ruletypes.h b/common/ruletypes.h index 3a42859e2..8c665cfad 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -47,8 +47,11 @@ RULE_INT(Character, DeathExpLossMultiplier, 3, "Adjust how much experience is lo RULE_BOOL(Character, DeathKeepLevel, false, "Players can not drop below 0% experience from death.") RULE_BOOL(Character, UseDeathExpLossMult, false, "Setting to control whether DeathExpLossMultiplier or the code default is used: (Level x Level / 18.0) x 12000") RULE_BOOL(Character, UseOldRaceRezEffects, false, "Older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore") -RULE_INT(Character, CorpseDecayTimeMS, 10800000, "Time after which the corpse decays (milliseconds)") -RULE_INT(Character, CorpseResTimeMS, 10800000, "Time after which the corpse can no longer be resurrected (milliseconds)") +RULE_INT(Character, CorpseDecayTime, 604800000, "Time after which the corpse decays (milliseconds) DEFAULT: 604800000 (7 Days)") +RULE_INT( Character, EmptyCorpseDecayTime, 10800000, "Time after which an empty corpse decays (milliseconds) DEFAULT: 10800000 (3 Hours)") +RULE_INT(Character, CorpseResTime, 10800000, "Time after which the corpse can no longer be resurrected (milliseconds) DEFAULT: 10800000 (3 Hours)") +RULE_INT( Character, DuelCorpseResTime, 600000, "Time before cant res corpse after a duel (milliseconds) DEFAULT: 600000 (10 Minutes)") +RULE_INT( Character, CorpseOwnerOnlineTime, 30000, "How often corpse will check if its owner is online DEFAULT: 30000 (30 Seconds)") RULE_BOOL(Character, LeaveCorpses, true, "Setting whether you leave a corpse behind") RULE_BOOL(Character, LeaveNakedCorpses, false, "Setting whether you leave a corpse without items") RULE_INT(Character, MaxDraggedCorpses, 2, "Maximum number of corpses you can drag at once") @@ -580,10 +583,10 @@ RULE_REAL(Combat, BashACBonusDivisor, 25.0, "this divides the AC value contribut RULE_CATEGORY_END() RULE_CATEGORY(NPC) -RULE_INT(NPC, MinorNPCCorpseDecayTimeMS, 450000, "NPC corpse decay time, if NPC below level 55 (milliseconds)") -RULE_INT(NPC, MajorNPCCorpseDecayTimeMS, 1500000, "NPC corpse decay time, if NPC equal or greater than level 55 (milliseconds)") +RULE_INT(NPC, MinorNPCCorpseDecayTime, 450000, "NPC corpse decay time, if NPC below level 55 (milliseconds)") +RULE_INT(NPC, MajorNPCCorpseDecayTime, 1500000, "NPC corpse decay time, if NPC equal or greater than level 55 (milliseconds)") RULE_INT(NPC, CorpseUnlockTimer, 150000, "Time after which corpses are unlocked for everyone to loot (milliseconds)") -RULE_INT(NPC, EmptyNPCCorpseDecayTimeMS, 0, "NPC corpse decay time, if no items are left on the corpse (milliseconds)") +RULE_INT(NPC, EmptyNPCCorpseDecayTime, 0, "NPC corpse decay time, if no items are left on the corpse (milliseconds)") RULE_BOOL(NPC, UseItemBonusesForNonPets, true, "Switch whether item bonuses should be used for NPCs who are not pets") RULE_BOOL(NPC, UseBaneDamage, false, "If NPCs can't inherently hit the target we don't add bane/magic dmg which isn't exactly the same as PCs") RULE_INT(NPC, SayPauseTimeInSec, 5, "Time span in which an NPC pauses his movement after a Say event without aggro (seconds)") diff --git a/common/servertalk.h b/common/servertalk.h index 3602b72b1..1b0d98623 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -84,6 +84,7 @@ #define ServerOP_UpdateSpawn 0x003f #define ServerOP_SpawnStatusChange 0x0040 #define ServerOP_DropClient 0x0041 // DropClient +#define ServerOP_IsOwnerOnline 0x0042 #define ServerOP_DepopAllPlayersCorpses 0x0060 #define ServerOP_QGlobalUpdate 0x0061 #define ServerOP_QGlobalDelete 0x0062 @@ -1653,6 +1654,14 @@ struct ServerRequestTellQueue_Struct { char name[64]; }; +struct ServerIsOwnerOnline_Struct { + char name[64]; + uint32 corpse_id; + uint16 zone_id; + uint8 online; + uint32 account_id; +}; + struct UCSServerStatus_Struct { uint8 available; // non-zero=true, 0=false union { diff --git a/common/shareddb.cpp b/common/shareddb.cpp index e5b4c3bbe..64100da6f 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1495,7 +1495,7 @@ int SharedDatabase::DeleteStalePlayerCorpses() { *this, fmt::format( "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(time_of_death)) > {} AND time_of_death != 0", - RuleI(Character, CorpseDecayTimeMS) / 1000 + RuleI(Character, CorpseDecayTime) / 1000 ) ) ); diff --git a/common/spdat.h b/common/spdat.h index 7cf7d1346..fc019d3e2 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -212,6 +212,7 @@ #define SPELL_ANCIENT_CRY_OF_CHAOS 5032 #define SPELL_BLOODTHIRST 8476 #define SPELL_AMPLIFICATION 2603 +#define SPELL_DIVINE_REZ 2738 // discipline IDs. #define DISC_UNHOLY_AURA 4520 diff --git a/common/version.h b/common/version.h index 7bd693702..1259aa618 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 9258 +#define CURRENT_BINARY_DATABASE_VERSION 9259 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9042 diff --git a/world/world_boot.cpp b/world/world_boot.cpp index 99c3b9033..c1220e4e9 100644 --- a/world/world_boot.cpp +++ b/world/world_boot.cpp @@ -449,8 +449,12 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv) ->LoadTaskData() ->LoadSharedTaskState(); + LogInfo("Purging expired shared tasks"); shared_task_manager.PurgeExpiredSharedTasks(); + LogInfo("Cleaning up instance corpses"); + database.CleanupInstanceCorpses(); + return true; } diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 324ca8743..865d24b7a 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1427,6 +1427,31 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { RuleManager::Instance()->LoadRules(&database, "default", true); break; } + case ServerOP_IsOwnerOnline: { + if (pack->size != sizeof(ServerIsOwnerOnline_Struct)) { + break; + } + + auto o = (ServerIsOwnerOnline_Struct*) pack->pBuffer; + auto cle = client_list.FindCLEByAccountID(o->account_id); + + o->online = cle ? 1 : 0; + + if (o->online) { + LogCorpsesDetail( + "ServerOP_IsOwnerOnline account_id [{}] corpse name [{}] found to be online, sending online update to zone_id [{}]", + o->account_id, + o->name, + o->zone_id + ); + } + + auto zs = zoneserver_list.FindByZoneID(o->zone_id); + if (zs) { + zs->SendPacket(pack); + } + break; + } case ServerOP_ReloadContentFlags: { zoneserver_list.SendPacket(pack); content_service.SetExpansionContext()->ReloadContentFlags(); diff --git a/zone/attack.cpp b/zone/attack.cpp index f05fef3ea..3581054bd 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1718,16 +1718,12 @@ void Client::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::Skill } } -bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill) +bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, KilledByTypes killed_by) { - if (!ClientFinishedLoading()) { + if (!ClientFinishedLoading() || dead) { return false; } - if (dead) { - return false; //cant die more than once... - } - if (!spell) { spell = SPELL_UNKNOWN; } @@ -1735,7 +1731,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill if (parse->PlayerHasQuestSub(EVENT_DEATH)) { const auto& export_string = fmt::format( "{} {} {} {}", - killerMob ? killerMob->GetID() : 0, + killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast(attack_skill) @@ -1749,7 +1745,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill } } - if (killerMob && killerMob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) { + if (killer_mob && killer_mob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) { char val1[20] = { 0 }; entity_list.MessageCloseString( @@ -1758,14 +1754,14 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill RuleI(Range, DamageMessages), Chat::NonMelee, /* 283 */ HIT_NON_MELEE, /* %1 hit %2 for %3 points of non-melee damage. */ - killerMob->GetCleanName(), /* Message1 */ + killer_mob->GetCleanName(), /* Message1 */ GetCleanName(), /* Message2 */ ConvertArray(damage, val1)/* Message3 */ ); } int exploss = 0; - LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", killerMob ? killerMob->GetName() : "Unknown", damage, spell, attack_skill); + LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", killer_mob ? killer_mob->GetName() : "Unknown", damage, spell, attack_skill); // #1: Send death packet to everyone uint8 killed_level = GetLevel(); @@ -1785,7 +1781,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill EQApplicationPacket app(OP_Death, sizeof(Death_Struct)); Death_Struct* d = (Death_Struct*)app.pBuffer; d->spawn_id = GetID(); - d->killer_id = killerMob ? killerMob->GetID() : 0; + d->killer_id = killer_mob ? killer_mob->GetID() : 0; d->corpseid = GetID(); d->bindzoneid = m_pp.binds[0].zone_id; d->spell_id = IsValidSpell(spell) ? spell : 0xffffffff; @@ -1812,42 +1808,45 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill GetMerc()->Suspend(); } - if (killerMob) { - if (killerMob->IsNPC()) { - if (parse->HasQuestSub(killerMob->GetNPCTypeID(), EVENT_SLAY)) { - parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0); + if (killer_mob) { + if (killer_mob->IsNPC()) { + if (parse->HasQuestSub(killer_mob->GetNPCTypeID(), EVENT_SLAY)) { + parse->EventNPC(EVENT_SLAY, killer_mob->CastToNPC(), this, "", 0); } - if (emoteid) { - killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this); + killed_by = KilledByTypes::Killed_NPC; + + auto emote_id = killer_mob->GetEmoteID(); + if (emote_id) { + killer_mob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this); } - killerMob->TrySpellOnKill(killed_level, spell); - } else if (killerMob->IsBot()) { + killer_mob->TrySpellOnKill(killed_level, spell); + } else if (killer_mob->IsBot()) { if (parse->BotHasQuestSub(EVENT_SLAY)) { - parse->EventBot(EVENT_SLAY, killerMob->CastToBot(), this, "", 0); + parse->EventBot(EVENT_SLAY, killer_mob->CastToBot(), this, "", 0); } - killerMob->TrySpellOnKill(killed_level, spell); + killer_mob->TrySpellOnKill(killed_level, spell); } if ( - killerMob->IsClient() && - (IsDueling() || killerMob->CastToClient()->IsDueling()) + killer_mob->IsClient() && + (IsDueling() || killer_mob->CastToClient()->IsDueling()) ) { SetDueling(false); SetDuelTarget(0); if ( - killerMob->IsClient() && - killerMob->CastToClient()->IsDueling() && - killerMob->CastToClient()->GetDuelTarget() == GetID() + killer_mob->IsClient() && + killer_mob->CastToClient()->IsDueling() && + killer_mob->CastToClient()->GetDuelTarget() == GetID() ) { //if duel opponent killed us... - killerMob->CastToClient()->SetDueling(false); - killerMob->CastToClient()->SetDuelTarget(0); - entity_list.DuelMessage(killerMob, this, false); - } - else { + killer_mob->CastToClient()->SetDueling(false); + killer_mob->CastToClient()->SetDuelTarget(0); + entity_list.DuelMessage(killer_mob, this, false); + killed_by = KilledByTypes::Killed_DUEL; + } else { //otherwise, we just died, end the duel. Mob* who = entity_list.GetMob(GetDuelTarget()); if (who && who->IsClient()) { @@ -1855,6 +1854,8 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill who->CastToClient()->SetDuelTarget(0); } } + } else if (killer_mob->IsClient()) { + killed_by = KilledByTypes::Killed_PVP; } } @@ -1906,12 +1907,11 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill if ((GetLevel() < RuleI(Character, DeathExpLossLevel)) || (GetLevel() > RuleI(Character, DeathExpLossMaxLevel)) || IsBecomeNPC()) { exploss = 0; - } else if (killerMob) { - if (killerMob->IsClient()) { - exploss = 0; - } else if (killerMob->GetOwner() && killerMob->GetOwner()->IsClient()) { - exploss = 0; - } else if (killerMob->IsBot()) { + } else if (killer_mob) { + if ( + killer_mob->IsOfClientBot() || + (killer_mob->GetOwner() && killer_mob->GetOwner()->IsOfClientBot()) + ) { exploss = 0; } } @@ -1964,11 +1964,11 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill RuleB(Character, LeaveNakedCorpses) ) { // creating the corpse takes the cash/items off the player too - new_corpse = new Corpse(this, exploss); + new_corpse = new Corpse(this, exploss, killed_by); std::string tmp; database.GetVariable("ServerType", tmp); - if (tmp[0] == '1' && tmp[1] == '\0' && killerMob && killerMob->IsClient()) { + if (tmp[0] == '1' && tmp[1] == '\0' && killer_mob && killer_mob->IsClient()) { database.GetVariable("PvPreward", tmp); auto reward = Strings::ToInt(tmp); if (reward == 3) { @@ -1986,8 +1986,8 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill new_corpse->SetPlayerKillItemID(0); } - if (killerMob->CastToClient()->isgrouped) { - auto* group = entity_list.GetGroupByClient(killerMob->CastToClient()); + if (killer_mob->CastToClient()->isgrouped) { + auto* group = entity_list.GetGroupByClient(killer_mob->CastToClient()); if (group) { for (int i = 0; i < 6; i++) { if (group->members[i]) { @@ -2058,15 +2058,15 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill /* QS: PlayerLogDeaths */ if (RuleB(QueryServ, PlayerLogDeaths)) { const char * killer_name = ""; - if (killerMob && killerMob->GetCleanName()) { killer_name = killerMob->GetCleanName(); } + if (killer_mob && killer_mob->GetCleanName()) { killer_name = killer_mob->GetCleanName(); } std::string event_desc = StringFormat("Died in zoneid:%i instid:%i by '%s', spellid:%i, damage:%i", GetZoneID(), GetInstanceID(), killer_name, spell, damage); QServ->PlayerLogEvent(Player_Log_Deaths, CharacterID(), event_desc); } if (player_event_logs.IsEventEnabled(PlayerEvent::DEATH)) { auto e = PlayerEvent::DeathEvent{ - .killer_id = killerMob ? static_cast(killerMob->GetID()) : static_cast(0), - .killer_name = killerMob ? killerMob->GetCleanName() : "No Killer", + .killer_id = killer_mob ? static_cast(killer_mob->GetID()) : static_cast(0), + .killer_name = killer_mob ? killer_mob->GetCleanName() : "No Killer", .damage = damage, .spell_id = spell, .spell_name = IsValidSpell(spell) ? spells[spell].name : "No Spell", @@ -2080,7 +2080,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill if (parse->PlayerHasQuestSub(EVENT_DEATH_COMPLETE)) { const auto& export_string = fmt::format( "{} {} {} {}", - killerMob ? killerMob->GetID() : 0, + killer_mob ? killer_mob->GetID() : 0, damage, spell, static_cast(attack_skill) @@ -2092,6 +2092,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill } return true; } + //SYNC WITH: tune.cpp, mob.h TuneNPCAttack bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) { @@ -2373,7 +2374,7 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp } } -bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill) +bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, KilledByTypes killed_by) { LogCombat( "Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", @@ -2836,8 +2837,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy &NPCTypedata, ( level > 54 ? - RuleI(NPC, MajorNPCCorpseDecayTimeMS) : - RuleI(NPC, MinorNPCCorpseDecayTimeMS) + RuleI(NPC, MajorNPCCorpseDecayTime) : + RuleI(NPC, MinorNPCCorpseDecayTime) ) ); diff --git a/zone/beacon.h b/zone/beacon.h index b9ab63623..0c65d46b5 100644 --- a/zone/beacon.h +++ b/zone/beacon.h @@ -34,7 +34,7 @@ public: ~Beacon(); //abstract virtual function implementations requird by base abstract class - virtual bool Death(Mob* killerMob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill) { return true; } + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC) { return true; } virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) { return; } virtual bool HasRaid() { return false; } virtual bool HasGroup() { return false; } diff --git a/zone/bot.cpp b/zone/bot.cpp index 283f03840..70ced6516 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -435,8 +435,9 @@ Bot::Bot( SetMana(0); SpellOnTarget(resurrection_sickness_spell_id, this); // Rezz effects } else { - SetHP(GetMaxHP()); - SetMana(GetMaxMana()); + SetHP(GetMaxHP() / 20); + SetMana(GetMaxMana() / 20); + SetEndurance(GetMaxEndurance() / 20); } } @@ -4480,20 +4481,24 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* } } -bool Bot::Death(Mob *killerMob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill) { - if (!NPC::Death(killerMob, damage, spell_id, attack_skill)) { +bool Bot::Death(Mob *killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by) +{ + if (!NPC::Death(killer_mob, damage, spell_id, attack_skill)) { return false; } Mob *my_owner = GetBotOwner(); + if (my_owner && my_owner->IsClient() && my_owner->CastToClient()->GetBotOption(Client::booDeathMarquee)) { - if (killerMob) - my_owner->CastToClient()->SendMarqueeMessage(Chat::White, 510, 0, 1000, 3000, StringFormat("%s has been slain by %s", GetCleanName(), killerMob->GetCleanName())); - else + if (killer_mob) { + my_owner->CastToClient()->SendMarqueeMessage(Chat::White, 510, 0, 1000, 3000, StringFormat("%s has been slain by %s", GetCleanName(), killer_mob->GetCleanName())); + } else { my_owner->CastToClient()->SendMarqueeMessage(Chat::White, 510, 0, 1000, 3000, StringFormat("%s has been slain", GetCleanName())); + } } const auto c = entity_list.GetCorpseByID(GetID()); + if (c) { c->Depop(); } @@ -4507,19 +4512,19 @@ bool Bot::Death(Mob *killerMob, int64 damage, uint16 spell_id, EQ::skills::Skill if (parse->BotHasQuestSub(EVENT_DEATH_COMPLETE)) { const auto& export_string = fmt::format( "{} {} {} {}", - killerMob ? killerMob->GetID() : 0, + killer_mob ? killer_mob->GetID() : 0, damage, spell_id, static_cast(attack_skill) ); - parse->EventBot(EVENT_DEATH_COMPLETE, this, killerMob, export_string, 0); + parse->EventBot(EVENT_DEATH_COMPLETE, this, killer_mob, export_string, 0); } Zone(); entity_list.RemoveBot(GetID()); -return true; + return true; } void Bot::Damage(Mob *from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic, eSpecialAttacks special) { diff --git a/zone/bot.h b/zone/bot.h index 473eef291..f269b597b 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -131,7 +131,7 @@ public: Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData, int32 expansion_bitmask); //abstract virtual override function implementations requird by base abstract class - bool Death(Mob* killerMob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill) override; + bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC) override; void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) override; diff --git a/zone/client.h b/zone/client.h index aaad0657e..375025406 100644 --- a/zone/client.h +++ b/zone/client.h @@ -261,7 +261,7 @@ public: bool GotoPlayerRaid(const std::string& player_name); //abstract virtual function implementations required by base abstract class - virtual bool Death(Mob* killerMob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill); + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC); virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None); virtual bool HasRaid() { return (GetRaid() ? true : false); } virtual bool HasGroup() { return (GetGroup() ? true : false); } @@ -1459,7 +1459,13 @@ public: void BuryPlayerCorpses(); int64 GetCorpseCount() { return database.GetCharacterCorpseCount(CharacterID()); } uint32 GetCorpseID(int corpse) { return database.GetCharacterCorpseID(CharacterID(), corpse); } - uint32 GetCorpseItemAt(int corpse_id, int slot_id) { return database.GetCharacterCorpseItemAt(corpse_id, slot_id); } + uint32 GetCorpseItemAt(int corpse_id, int slot_id) { + if (!corpse_id) { + return 0; + } + return database.GetCharacterCorpseItemAt(corpse_id, slot_id); + } + void SuspendMinion(int value); void Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration); void NotifyNewTitlesAvailable(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 8bf6164ce..c7b691419 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5280,28 +5280,87 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) } } - uint32 decay_time = t->GetDecayTime(); - if (decay_time) { - const std::string& time_string = Strings::SecondsToTime(decay_time, true); - Message( - Chat::NPCQuestSay, - fmt::format( - "This corpse will decay in {}.", - Strings::ToLower(time_string) - ).c_str() - ); - - if (t->IsPlayerCorpse()) { - Message( + uint32 days, hours, minutes, seconds, remaining_time = 0; + if (t && t->IsNPCCorpse()) { + remaining_time = t->GetDecayTime(); + if (remaining_time != 0) { + seconds = (remaining_time / 1000) % 60; + minutes = (remaining_time / 60000) % 60; + MessageString( Chat::NPCQuestSay, - fmt::format( - "This corpse {} be resurrected.", - t->IsRezzed() ? "cannot" : "can" - ).c_str() + CORPSE_DECAY_TIME_MINUTE, + std::to_string(minutes).c_str(), + std::to_string(seconds).c_str() ); + } else { + MessageString(Chat::NPCQuestSay, CORPSE_DECAY_NOW); + } + } else if (t && t->IsPlayerCorpse()) { + remaining_time = t->GetRemainingRezTime(); + if (!t->IsRezzed()) { + if (remaining_time > 0) { + seconds = (remaining_time / 1000) % 60; + minutes = (remaining_time / 60000) % 60; + hours = (remaining_time / 3600000) % 24; + if (hours) { + MessageString( + Chat::White, + CORPSE_REZ_TIME_HOUR, + std::to_string(hours).c_str(), + std::to_string(minutes).c_str(), + std::to_string(seconds).c_str() + ); + } else { + MessageString( + Chat::White, + CORPSE_REZ_TIME_MINUTE, + std::to_string(minutes).c_str(), + std::to_string(seconds).c_str() + ); + } + hours = 0; + } else { + MessageString(Chat::White, CORPSE_TOO_OLD); + } + } else { + Message(Chat::White, "This corpse has already accepted a resurrection."); + } + + remaining_time = t->GetDecayTime(); + if (remaining_time != 0) { + seconds = (remaining_time / 1000) % 60; + minutes = (remaining_time / 60000) % 60; + hours = (remaining_time / 3600000) % 24; + days = remaining_time / 86400000; + + if (days) { + MessageString( + Chat::White, + CORPSE_DECAY_TIME_DAY, + std::to_string(days).c_str(), + std::to_string(hours).c_str(), + std::to_string(minutes).c_str(), + std::to_string(seconds).c_str() + ); + } else if (hours) { + MessageString( + Chat::White, + CORPSE_DECAY_TIME_HOUR, + std::to_string(hours).c_str(), + std::to_string(minutes).c_str(), + std::to_string(seconds).c_str() + ); + } else { + MessageString( + Chat::White, + CORPSE_DECAY_TIME_MINUTE, + std::to_string(minutes).c_str(), + std::to_string(seconds).c_str() + ); + } + } else { + MessageString(Chat::White, CORPSE_DECAY_NOW); } - } else { - MessageString(Chat::NPCQuestSay, CORPSE_DECAY_NOW); } } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 9e7673ce1..75947a5f4 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1049,18 +1049,21 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I RuleI(Character, OldResurrectionSicknessSpellID) : RuleI(Character, ResurrectionSicknessSpellID) ); - SpellOnTarget(resurrection_sickness_spell_id, this); // Rezz effects - } - else { + SpellOnTarget(resurrection_sickness_spell_id, this); + } else if (SpellID == SPELL_DIVINE_REZ) { SetHP(GetMaxHP()); SetMana(GetMaxMana()); + SetEndurance(GetMaxEndurance()); + } else { + SetHP(GetMaxHP() / 20); + SetMana(GetMaxMana() / 20); + SetEndurance(GetMaxEndurance() / 20); } - if(spells[SpellID].base_value[0] < 100 && spells[SpellID].base_value[0] > 0 && PendingRezzXP > 0) - { + + if(spells[SpellID].base_value[0] < 100 && spells[SpellID].base_value[0] > 0 && PendingRezzXP > 0) { SetEXP(((int)(GetEXP()+((float)((PendingRezzXP / 100) * spells[SpellID].base_value[0])))), GetAAXP(),true); - } - else if (spells[SpellID].base_value[0] == 100 && PendingRezzXP > 0) { + } else if (spells[SpellID].base_value[0] == 100 && PendingRezzXP > 0) { SetEXP((GetEXP() + PendingRezzXP), GetAAXP(), true); } diff --git a/zone/common.h b/zone/common.h index d5b2f9a95..90bd2629d 100644 --- a/zone/common.h +++ b/zone/common.h @@ -226,6 +226,12 @@ enum class LootRequestType : uint8 { AllowedPVPDefined, }; +enum class KilledByTypes : uint8 { + Killed_NPC = 0, + Killed_DUEL = 1, + Killed_PVP = 2 +}; + namespace Journal { enum class SpeakMode : uint8 { Raw = 0, // this just uses the raw message diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 85f84db60..764eefd08 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1,33 +1,11 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* -New class for handeling corpses and everything associated with them. -Child of the Mob class. --Quagmire -*/ #ifdef _WINDOWS - #if (!defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER < 1900)) - #define snprintf _snprintf - #define vsnprintf _vsnprintf - #endif - #define strncasecmp _strnicmp - #define strcasecmp _stricmp +#if (!defined(_MSC_VER) || (defined(_MSC_VER) && _MSC_VER < 1900)) +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#endif +#define strncasecmp _strnicmp +#define strcasecmp _stricmp #endif #include "../common/data_verification.h" @@ -50,137 +28,58 @@ Child of the Mob class. #include "string_ids.h" #include "worldserver.h" #include "../common/events/player_event_logs.h" +#include "../common/repositories/character_corpses_repository.h" +#include "../common/repositories/character_corpse_items_repository.h" #include -extern EntityList entity_list; -extern Zone* zone; -extern WorldServer worldserver; +extern EntityList entity_list; +extern Zone *zone; +extern WorldServer worldserver; extern npcDecayTimes_Struct npcCorpseDecayTimes[100]; -void Corpse::SendEndLootErrorPacket(Client* client) { +void Corpse::SendEndLootErrorPacket(Client *client) +{ auto outapp = new EQApplicationPacket(OP_LootComplete, 0); client->QueuePacket(outapp); safe_delete(outapp); } -void Corpse::SendLootReqErrorPacket(Client* client, LootResponse response) { +void Corpse::SendLootReqErrorPacket(Client *client, LootResponse response) +{ auto outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct)); - moneyOnCorpseStruct* d = (moneyOnCorpseStruct*) outapp->pBuffer; - d->response = static_cast(response); - d->unknown1 = 0x5a; - d->unknown2 = 0x40; + auto *d = (moneyOnCorpseStruct *) outapp->pBuffer; + d->response = static_cast(response); + d->unknown1 = 0x5a; + d->unknown2 = 0x40; client->QueuePacket(outapp); safe_delete(outapp); } -Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard, uint32 guild_consent_id) { - CharacterCorpseEntry ce; - if (!database.LoadCharacterCorpseData(in_dbid, ce)) { - LogDebug("Unable to create a corpse entity for [{}] [{}] [{}]", in_dbid, in_charid, in_charname); - return nullptr; - } - - LootItems itemlist; - for (auto &item: ce.items) { - auto tmp = new LootItem; - - tmp->equip_slot = item.equip_slot; - tmp->item_id = item.item_id; - tmp->charges = item.charges; - tmp->lootslot = item.lootslot; - tmp->aug_1 = item.aug_1; - tmp->aug_2 = item.aug_2; - tmp->aug_3 = item.aug_3; - tmp->aug_4 = item.aug_4; - tmp->aug_5 = item.aug_5; - tmp->aug_6 = item.aug_6; - tmp->attuned = item.attuned; - - itemlist.push_back(tmp); - } - - /* Create Corpse Entity */ - auto pc = new Corpse( - in_dbid, // uint32 in_dbid - in_charid, // uint32 in_charid - in_charname.c_str(), // char* in_charname - &itemlist, // ItemList* in_itemlist - ce.copper, // uint32 in_copper - ce.silver, // uint32 in_silver - ce.gold, // uint32 in_gold - ce.plat, // uint32 in_plat - position, - ce.size, // float in_size - ce.gender, // uint8 in_gender - ce.race, // uint16 in_race - ce.class_, // uint8 in_class - ce.deity, // uint8 in_deity - ce.level, // uint8 in_level - ce.texture, // uint8 in_texture - ce.helmtexture, // uint8 in_helmtexture - ce.exp, // uint32 in_rezexp - was_at_graveyard // bool wasAtGraveyard - ); - - if (ce.locked) - pc->Lock(); - - /* Load Item Tints */ - pc->item_tint.Head.Color = ce.item_tint.Head.Color; - pc->item_tint.Chest.Color = ce.item_tint.Chest.Color; - pc->item_tint.Arms.Color = ce.item_tint.Arms.Color; - pc->item_tint.Wrist.Color = ce.item_tint.Wrist.Color; - pc->item_tint.Hands.Color = ce.item_tint.Hands.Color; - pc->item_tint.Legs.Color = ce.item_tint.Legs.Color; - pc->item_tint.Feet.Color = ce.item_tint.Feet.Color; - pc->item_tint.Primary.Color = ce.item_tint.Primary.Color; - pc->item_tint.Secondary.Color = ce.item_tint.Secondary.Color; - - /* Load Physical Appearance */ - pc->haircolor = ce.haircolor; - pc->beardcolor = ce.beardcolor; - pc->eyecolor1 = ce.eyecolor1; - pc->eyecolor2 = ce.eyecolor2; - pc->hairstyle = ce.hairstyle; - pc->luclinface = ce.face; - pc->beard = ce.beard; - pc->drakkin_heritage = ce.drakkin_heritage; - pc->drakkin_tattoo = ce.drakkin_tattoo; - pc->drakkin_details = ce.drakkin_details; - pc->IsRezzed(rezzed); - pc->become_npc = false; - pc->consented_guild_id = guild_consent_id; - - pc->UpdateEquipmentLight(); // itemlist populated above..need to determine actual values - - return pc; -} - Corpse::Corpse( - NPC *in_npc, - LootItems *in_itemlist, - uint32 in_npctypeid, - const NPCType **in_npctypedata, - uint32 in_decaytime + NPC *npc, + LootItems *item_list, + uint32 npc_type_id, + const NPCType **npc_type_data, + uint32 decay_time ) : Mob( "Unnamed_Corpse", // in_name "", // in_lastname 0, // in_cur_hp 0, // in_max_hp - in_npc->GetGender(), // in_gender - in_npc->GetRace(), // in_race - in_npc->GetClass(), // in_class + npc->GetGender(), // in_gender + npc->GetRace(), // in_race + npc->GetClass(), // in_class BT_Humanoid, // in_bodytype - in_npc->GetDeity(), // in_deity - in_npc->GetLevel(), // in_level - in_npc->GetNPCTypeID(), // in_npctype_id - in_npc->GetSize(), // in_size + npc->GetDeity(), // in_deity + npc->GetLevel(), // in_level + npc->GetNPCTypeID(), // in_npctype_id + npc->GetSize(), // in_size 0.0f, // in_runspeed - in_npc->GetPosition(), // position - in_npc->GetInnateLightType(), // in_light - in_npc->GetTexture(), // in_texture - in_npc->GetHelmTexture(), // in_helmtexture + npc->GetPosition(), // position + npc->GetInnateLightType(), // in_light + npc->GetTexture(), // in_texture + npc->GetHelmTexture(), // in_helmtexture 0, // in_ac 0, // in_atk 0, // in_str @@ -216,90 +115,90 @@ Corpse::Corpse( 0, // in_handtexture 0, // in_legtexture 0, // in_feettexture - (*in_npctypedata)->use_model, // in_usemodel + (*npc_type_data)->use_model, // in_usemodel false, // in_always_aggros_foes 0, // in_heroic_strikethrough false // in_keeps_sold_items ), - corpse_decay_timer(in_decaytime), - corpse_rez_timer(0), - corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), - corpse_graveyard_timer(0), - loot_cooldown_timer(10) + m_corpse_decay_timer(decay_time), + m_corpse_rezzable_timer(0), + m_corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), + m_corpse_graveyard_timer(0), + m_loot_cooldown_timer(10) { - corpse_graveyard_timer.Disable(); + m_corpse_graveyard_timer.Disable(); - is_corpse_changed = false; - is_player_corpse = false; - is_locked = false; - being_looted_by = 0xFFFFFFFF; - if (in_itemlist) { - itemlist = *in_itemlist; - in_itemlist->clear(); + m_is_corpse_changed = false; + m_is_player_corpse = false; + m_is_locked = false; + m_being_looted_by_entity_id = 0xFFFFFFFF; + if (item_list) { + m_item_list = *item_list; + item_list->clear(); } - SetCash(in_npc->GetCopper(), in_npc->GetSilver(), in_npc->GetGold(), in_npc->GetPlatinum()); + SetCash(npc->GetCopper(), npc->GetSilver(), npc->GetGold(), npc->GetPlatinum()); - npctype_id = in_npctypeid; + npctype_id = npc_type_id; SetPlayerKillItemID(0); - char_id = 0; - corpse_db_id = 0; - player_corpse_depop = false; - strcpy(corpse_name, in_npc->GetName()); - strcpy(name, in_npc->GetName()); + m_character_id = 0; + m_corpse_db_id = 0; + m_player_corpse_depop = false; + strcpy(corpse_name, npc->GetName()); + strcpy(name, npc->GetName()); - for(int count = 0; count < 100; count++) { + for (auto &npcCorpseDecayTime: npcCorpseDecayTimes) { if ( EQ::ValueWithin( level, - npcCorpseDecayTimes[count].minlvl, - npcCorpseDecayTimes[count].maxlvl + npcCorpseDecayTime.minlvl, + npcCorpseDecayTime.maxlvl ) - ) { - corpse_decay_timer.SetTimer(npcCorpseDecayTimes[count].seconds*1000); + ) { + m_corpse_decay_timer.SetTimer(npcCorpseDecayTime.seconds * 1000); break; } } if (IsEmpty()) { - corpse_decay_timer.SetTimer(RuleI(NPC,EmptyNPCCorpseDecayTimeMS)+1000); + m_corpse_decay_timer.SetTimer(RuleI(NPC, EmptyNPCCorpseDecayTime) + 1000); } - if (in_npc->HasPrivateCorpse()) { - corpse_delay_timer.SetTimer(corpse_decay_timer.GetRemainingTime() + 1000); + if (npc->HasPrivateCorpse()) { + m_corpse_delay_timer.SetTimer(m_corpse_decay_timer.GetRemainingTime() + 1000); } - for (int i = 0; i < MAX_LOOTERS; i++){ - allowed_looters[i] = 0; + for (int &allowed_looter: m_allowed_looters) { + allowed_looter = 0; } - rez_experience = 0; + m_rezzed_experience = 0; UpdateEquipmentLight(); UpdateActiveLight(); - loot_request_type = LootRequestType::Forbidden; + m_loot_request_type = LootRequestType::Forbidden; } -Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( +Corpse::Corpse(Client *c, int32 rez_exp, KilledByTypes in_killed_by) : Mob( "Unnamed_Corpse", // in_name "", // in_lastname 0, // in_cur_hp 0, // in_max_hp - client->GetGender(), // in_gender - client->GetRace(), // in_race - client->GetClass(), // in_class + c->GetGender(), // in_gender + c->GetRace(), // in_race + c->GetClass(), // in_class BT_Humanoid, // in_bodytype - client->GetDeity(), // in_deity - client->GetLevel(), // in_level + c->GetDeity(), // in_deity + c->GetLevel(), // in_level 0, // in_npctype_id - client->GetSize(), // in_size + c->GetSize(), // in_size 0, // in_runspeed - client->GetPosition(), // position - client->GetInnateLightType(), // in_light - client->GetTexture(), // in_texture - client->GetHelmTexture(), // in_helmtexture + c->GetPosition(), // position + c->GetInnateLightType(), // in_light + c->GetTexture(), // in_texture + c->GetHelmTexture(), // in_helmtexture 0, // in_ac 0, // in_atk 0, // in_str @@ -309,16 +208,16 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( 0, // in_int 0, // in_wis 0, // in_cha - client->GetPP().haircolor, // in_haircolor - client->GetPP().beardcolor, // in_beardcolor - client->GetPP().eyecolor1, // in_eyecolor1 - client->GetPP().eyecolor2, // in_eyecolor2 - client->GetPP().hairstyle, // in_hairstyle - client->GetPP().face, // in_luclinface - client->GetPP().beard, // in_beard - client->GetPP().drakkin_heritage, // in_drakkin_heritage - client->GetPP().drakkin_tattoo, // in_drakkin_tattoo - client->GetPP().drakkin_details, // in_drakkin_details + c->GetPP().haircolor, // in_haircolor + c->GetPP().beardcolor, // in_beardcolor + c->GetPP().eyecolor1, // in_eyecolor1 + c->GetPP().eyecolor2, // in_eyecolor2 + c->GetPP().hairstyle, // in_hairstyle + c->GetPP().face, // in_luclinface + c->GetPP().beard, // in_beard + c->GetPP().drakkin_heritage, // in_drakkin_heritage + c->GetPP().drakkin_tattoo, // in_drakkin_tattoo + c->GetPP().drakkin_details, // in_drakkin_details EQ::TintProfile(), // in_armor_tint 0xff, // in_aa_title 0, // in_see_invis @@ -339,54 +238,66 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( false, // in_always_aggro 0, // in_heroic_strikethrough false // in_keeps_sold_items -), - corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), - corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), - corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), - corpse_graveyard_timer(RuleI(Zone, GraveyardTimeMS)), - loot_cooldown_timer(10) +) { - PlayerProfile_Struct *pp = &client->GetPP(); - EQ::ItemInstance *item = nullptr; + PlayerProfile_Struct *pp = &c->GetPP(); + EQ::ItemInstance *item = nullptr; if (!zone->HasGraveyard()) { - corpse_graveyard_timer.Disable(); + m_corpse_graveyard_timer.Disable(); } - for (int i = 0; i < MAX_LOOTERS; i++){ - allowed_looters[i] = 0; + for (int &allowed_looter: m_allowed_looters) { + allowed_looter = 0; } - if (client->AutoConsentGroupEnabled()) { - auto* g = client->GetGroup(); - consented_group_id = g ? g->GetID() : 0; + if (c->AutoConsentGroupEnabled()) { + auto *g = c->GetGroup(); + m_consented_group_id = g ? g->GetID() : 0; } - if (client->AutoConsentRaidEnabled()) { - auto* r = client->GetRaid(); - consented_raid_id = r ? r->GetID() : 0; + if (c->AutoConsentRaidEnabled()) { + auto *r = c->GetRaid(); + m_consented_raid_id = r ? r->GetID() : 0; } - consented_guild_id = client->AutoConsentGuildEnabled() ? client->GuildID() : 0; + m_consented_guild_id = c->AutoConsentGuildEnabled() ? c->GuildID() : 0; - is_corpse_changed = true; - rez_experience = in_rezexp; - is_player_corpse = true; - is_locked = false; - being_looted_by = 0xFFFFFFFF; - char_id = client->CharacterID(); - corpse_db_id = 0; - player_corpse_depop = false; - copper = 0; - silver = 0; - gold = 0; - platinum = 0; + m_is_corpse_changed = true; + m_rezzed_experience = rez_exp; + m_is_player_corpse = true; + m_is_locked = false; + m_being_looted_by_entity_id = 0xFFFFFFFF; + m_character_id = c->CharacterID(); + m_corpse_db_id = 0; + m_player_corpse_depop = false; + m_copper = 0; + m_silver = 0; + m_gold = 0; + m_platinum = 0; + m_killed_by_type = (uint8) in_killed_by; + m_is_rezzable = true; + m_remaining_rez_time = 0; + m_is_owner_online = false; + m_account_id = c->AccountID(); + + // timers + m_corpse_decay_timer.SetTimer(RuleI(Character, CorpseDecayTime)); + m_corpse_rezzable_timer.SetTimer(RuleI(Character, CorpseResTime)); + m_corpse_delay_timer.SetTimer(RuleI(NPC, CorpseUnlockTimer)); + m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS)); + m_loot_cooldown_timer.SetTimer(10); + m_check_rezzable_timer.SetTimer(1000); + m_check_owner_online_timer.SetTimer(RuleI(Character, CorpseOwnerOnlineTime)); + + m_corpse_rezzable_timer.Disable(); + SetRezTimer(true); strcpy(corpse_name, pp->name); strcpy(name, pp->name); /* become_npc was not being initialized which led to some pretty funky things with newly created corpses */ - become_npc = false; + m_become_npc = false; SetPlayerKillItemID(0); @@ -394,14 +305,11 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( if ( (!RuleB(Character, LeaveNakedCorpses) || RuleB(Character, LeaveCorpses)) && GetLevel() >= RuleI(Character, DeathItemLossLevel) - ) { + ) { // cash // Let's not move the cash when 'RespawnFromHover = true' && 'client->GetClientVersion() < EQClientSoF' since the client doesn't. // (change to first client that supports 'death hover' mode, if not SoF.) - if ( - !RuleB(Character, RespawnFromHover) || - client->ClientVersion() < EQ::versions::ClientVersion::SoF - ) { + if (!RuleB(Character, RespawnFromHover) || c->ClientVersion() < EQ::versions::ClientVersion::SoF) { auto corpse_copper = pp->copper; auto corpse_silver = pp->silver; auto corpse_gold = pp->gold; @@ -428,7 +336,7 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( } // get their tints - memcpy(&item_tint.Slot, &client->GetPP().item_tint, sizeof(item_tint)); + memcpy(&m_item_tint.Slot, &c->GetPP().item_tint, sizeof(m_item_tint)); // TODO soulbound items need not be added to corpse, but they need // to go into the regular slots on the player, out of bags @@ -439,19 +347,13 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( // without additional work to database loading of player corpses, this order is not // currently preserved and a re-work of this processing loop is not warranted. for (int i = EQ::invslot::POSSESSIONS_BEGIN; i <= EQ::invslot::POSSESSIONS_END; ++i) { - item = client->GetInv().GetItem(i); + item = c->GetInv().GetItem(i); if (!item) { continue; } - if ( - !client->IsBecomeNPC() || - ( - client->IsBecomeNPC() && - !item->GetItem()->NoRent - ) - ) { - MoveItemToCorpse(client, item, i, removed_list); + if (!c->IsBecomeNPC() || (c->IsBecomeNPC() && !item->GetItem()->NoRent)) { + MoveItemToCorpse(c, item, i, removed_list); } } @@ -460,11 +362,11 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( // this should not be modified to include the entire range of invtype::TYPE_POSSESSIONS slots by default.. // ..due to the possibility of 'hidden' items from client version bias..or, possibly, soul-bound items (WoW?) if (!removed_list.empty()) { - std::list::const_iterator iter = removed_list.begin(); + auto iter = removed_list.begin(); if (iter != removed_list.end()) { std::stringstream ss(""); - ss << "DELETE FROM `inventory` WHERE `charid` = " << client->CharacterID(); + ss << "DELETE FROM `inventory` WHERE `charid` = " << c->CharacterID(); ss << " AND `slotid` IN (" << (*iter); ++iter; @@ -478,12 +380,12 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( } } - auto start = client->GetInv().cursor_cbegin(); - auto finish = client->GetInv().cursor_cend(); - database.SaveCursor(client->CharacterID(), start, finish); + auto start = c->GetInv().cursor_cbegin(); + auto finish = c->GetInv().cursor_cend(); + database.SaveCursor(c->CharacterID(), start, finish); - client->CalcBonuses(); - client->Save(); + c->CalcBonuses(); + c->Save(); IsRezzed(false); Save(); @@ -499,7 +401,7 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( UpdateEquipmentLight(); UpdateActiveLight(); - loot_request_type = LootRequestType::Forbidden; + m_loot_request_type = LootRequestType::Forbidden; IsRezzed(false); Save(); @@ -532,7 +434,7 @@ void Corpse::MoveItemToCorpse(Client *client, EQ::ItemInstance *inst, int16 equi for (int16 sub_index = EQ::invbag::SLOT_BEGIN; sub_index <= EQ::invbag::SLOT_END; ++sub_index) { int16 real_bag_slot = EQ::InventoryProfile::CalcSlotId(equipSlot, sub_index); - auto bag_inst = client->GetInv().GetItem(real_bag_slot); + auto bag_inst = client->GetInv().GetItem(real_bag_slot); if (bag_inst == nullptr) { continue; } AddItem( @@ -561,24 +463,48 @@ void Corpse::MoveItemToCorpse(Client *client, EQ::ItemInstance *inst, int16 equi } // To be called from LoadFromDBData -Corpse::Corpse(uint32 in_dbid, uint32 in_charid, const char* in_charname, LootItems* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, const glm::vec4& position, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture, uint32 in_rezexp, bool wasAtGraveyard) : Mob( +Corpse::Corpse( + uint32 corpse_id, + uint32 character_id, + const char *character_name, + LootItems *item_list, + uint32 copper, + uint32 silver, + uint32 gold, + uint32 platinum, + const glm::vec4 &position, + float size, + uint8 gender, + uint16 race, + uint8 class_, + uint8 deity, + uint8 level, + uint8 texture, + uint8 helm_texture, + uint32 rez_exp, + uint32 gm_rez_exp, + KilledByTypes killed_by, + bool is_rezzable, + uint32 rez_remaining_time, + bool was_at_graveyard +) : Mob( "Unnamed_Corpse", // in_name "", // in_lastname 0, // in_cur_hp 0, // in_max_hp - in_gender, // in_gender - in_race, // in_race - in_class, // in_class + gender, // in_gender + race, // in_race + class_, // in_class BT_Humanoid, // in_bodytype - in_deity, // in_deity - in_level, // in_level + deity, // in_deity + level, // in_level 0, // in_npctype_id - in_size, // in_size + size, // in_size 0.0f, // in_runspeed position, // position 0, // in_light - in_texture, // in_texture - in_helmtexture, // in_helmtexture + texture, // in_texture + helm_texture, // in_helmtexture 0, // in_ac 0, // in_atk 0, // in_str @@ -618,99 +544,111 @@ Corpse::Corpse(uint32 in_dbid, uint32 in_charid, const char* in_charname, LootIt false, // in_always_aggros_foes 0, // in_heroic_strikethrough false // in_keeps_sold_items -), - corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), - corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), - corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), - corpse_graveyard_timer(RuleI(Zone, GraveyardTimeMS)), - loot_cooldown_timer(10) +) { - LoadPlayerCorpseDecayTime(in_dbid); + LoadPlayerCorpseDecayTime(corpse_id); - if (!zone->HasGraveyard() || wasAtGraveyard) { - corpse_graveyard_timer.Disable(); + if (!zone->HasGraveyard() || was_at_graveyard) { + m_corpse_graveyard_timer.Disable(); } - is_corpse_changed = false; - is_player_corpse = true; - is_locked = false; - being_looted_by = 0xFFFFFFFF; - corpse_db_id = in_dbid; - player_corpse_depop = false; - char_id = in_charid; - itemlist = *in_itemlist; - in_itemlist->clear(); + strcpy(corpse_name, character_name); + strcpy(name, character_name); - strcpy(corpse_name, in_charname); - strcpy(name, in_charname); + m_copper = copper; + m_silver = silver; + m_gold = gold; + m_platinum = platinum; + m_rezzed_experience = rez_exp; + m_gm_rezzed_experience = gm_rez_exp; + m_killed_by_type = (uint8) killed_by; + m_is_rezzable = is_rezzable; + m_remaining_rez_time = rez_remaining_time; + m_is_owner_online = false; + m_is_corpse_changed = false; + m_is_player_corpse = true; + m_is_locked = false; + m_being_looted_by_entity_id = 0xFFFFFFFF; + m_corpse_db_id = corpse_id; + m_player_corpse_depop = false; + m_character_id = character_id; + m_item_list = *item_list; + item_list->clear(); - copper = in_copper; - silver = in_silver; - gold = in_gold; - platinum = in_plat; + // timers + m_corpse_decay_timer.SetTimer(RuleI(Character, CorpseDecayTime)); + m_corpse_rezzable_timer.SetTimer(RuleI(Character, CorpseResTime)); + m_corpse_delay_timer.SetTimer(RuleI(NPC, CorpseUnlockTimer)); + m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS)); + m_loot_cooldown_timer.SetTimer(10); + m_check_owner_online_timer.SetTimer(RuleI(Character, CorpseOwnerOnlineTime)); + m_check_rezzable_timer.SetTimer(1000); + m_corpse_rezzable_timer.Disable(); - rez_experience = in_rezexp; + SetRezTimer(); - for (int i = 0; i < MAX_LOOTERS; i++){ - allowed_looters[i] = 0; + for (int &allowed_looter: m_allowed_looters) { + allowed_looter = 0; } SetPlayerKillItemID(0); - UpdateEquipmentLight(); UpdateActiveLight(); - loot_request_type = LootRequestType::Forbidden; + m_loot_request_type = LootRequestType::Forbidden; } -Corpse::~Corpse() { - if (is_player_corpse && !(player_corpse_depop && corpse_db_id == 0)) { +Corpse::~Corpse() +{ + if (m_is_player_corpse && !(m_player_corpse_depop && m_corpse_db_id == 0)) { Save(); } LootItems::iterator cur, end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - LootItem * item = *cur; + cur = m_item_list.begin(); + end = m_item_list.end(); + for (; cur != end; ++cur) { + LootItem *item = *cur; safe_delete(item); } - itemlist.clear(); + m_item_list.clear(); } /* this needs to be called AFTER the entity_id is set the client does this too, so it's unchangable */ -void Corpse::CalcCorpseName() { +void Corpse::CalcCorpseName() +{ EntityList::RemoveNumbers(name); char tmp[64]; - if (is_player_corpse){ + if (m_is_player_corpse) { snprintf(tmp, sizeof(tmp), "'s corpse%d", GetID()); } - else{ + else { snprintf(tmp, sizeof(tmp), "`s_corpse%d", GetID()); } name[(sizeof(name) - 1) - strlen(tmp)] = 0; strcat(name, tmp); } -bool Corpse::Save() { - if (!is_player_corpse) { +bool Corpse::Save() +{ + if (!m_is_player_corpse) { return true; } - if (!is_corpse_changed) { + if (!m_is_corpse_changed) { return true; } CharacterCorpseEntry ce; ce.size = size; - ce.locked = is_locked; - ce.copper = copper; - ce.silver = silver; - ce.gold = gold; - ce.plat = platinum; + ce.locked = m_is_locked; + ce.copper = m_copper; + ce.silver = m_silver; + ce.gold = m_gold; + ce.plat = m_platinum; ce.race = race; ce.class_ = class_; ce.gender = gender; @@ -718,8 +656,12 @@ bool Corpse::Save() { ce.level = level; ce.texture = texture; ce.helmtexture = helmtexture; - ce.exp = rez_experience; - ce.item_tint = item_tint; + ce.exp = m_rezzed_experience; + ce.gm_exp = m_gm_exp; + ce.killed_by = m_killed_by_type; + ce.rezzable = m_is_rezzable; + ce.rez_time = m_remaining_rez_time; + ce.item_tint = m_item_tint; ce.haircolor = haircolor; ce.beardcolor = beardcolor; ce.eyecolor2 = eyecolor1; @@ -730,7 +672,7 @@ bool Corpse::Save() { ce.drakkin_tattoo = drakkin_tattoo; ce.drakkin_details = drakkin_details; - for (auto &item: itemlist) { + for (auto &item: m_item_list) { CharacterCorpseItemEntry e; e.item_id = item->item_id; @@ -752,66 +694,96 @@ bool Corpse::Save() { ce.items.emplace_back(std::move(e)); } - /* Create New Corpse*/ - if (corpse_db_id == 0) { - corpse_db_id = database.SaveCharacterCorpse(char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), ce, m_Position, consented_guild_id); + if (m_corpse_db_id == 0) { + m_corpse_db_id = database.SaveCharacterCorpse( + m_character_id, + corpse_name, + zone->GetZoneID(), + zone->GetInstanceID(), + ce, + m_Position, + m_consented_guild_id + ); } - /* Update Corpse Data */ - else{ - corpse_db_id = database.UpdateCharacterCorpse(corpse_db_id, char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), ce, m_Position, consented_guild_id, IsRezzed()); + else { + m_corpse_db_id = database.UpdateCharacterCorpse( + m_corpse_db_id, + m_character_id, + corpse_name, + zone->GetZoneID(), + zone->GetInstanceID(), + ce, + m_Position, + m_consented_guild_id, + IsRezzed() + ); } return true; } -void Corpse::Delete() { - if (IsPlayerCorpse() && corpse_db_id != 0) - database.DeleteCharacterCorpse(corpse_db_id); - - corpse_db_id = 0; - player_corpse_depop = true; -} - -void Corpse::Bury() { - if (IsPlayerCorpse() && corpse_db_id != 0) - database.BuryCharacterCorpse(corpse_db_id); - corpse_db_id = 0; - player_corpse_depop = true; -} - -void Corpse::DepopNPCCorpse() { - if (IsNPCCorpse()) - player_corpse_depop = true; -} - -void Corpse::DepopPlayerCorpse() { - player_corpse_depop = true; -} - -void Corpse::AddConsentName(std::string consent_player_name) +void Corpse::Delete() { - for (const auto& consented_player_name : consented_player_names) { + if (IsPlayerCorpse() && m_corpse_db_id != 0) { + database.DeleteCharacterCorpse(m_corpse_db_id); + } + + m_corpse_db_id = 0; + m_player_corpse_depop = true; +} + +void Corpse::Bury() +{ + if (IsPlayerCorpse() && m_corpse_db_id != 0) { + database.BuryCharacterCorpse(m_corpse_db_id); + } + + m_corpse_db_id = 0; + m_player_corpse_depop = true; +} + +void Corpse::DepopNPCCorpse() +{ + if (IsNPCCorpse()) { + m_player_corpse_depop = true; + } +} + +void Corpse::DepopPlayerCorpse() +{ + m_player_corpse_depop = true; +} + +void Corpse::AddConsentName(const std::string &consent_player_name) +{ + for (const auto &consented_player_name: m_consented_player_names) { if (strcasecmp(consented_player_name.c_str(), consent_player_name.c_str()) == 0) { return; } } - consented_player_names.emplace_back(consent_player_name); + + m_consented_player_names.emplace_back(consent_player_name); } -void Corpse::RemoveConsentName(std::string consent_player_name) +void Corpse::RemoveConsentName(const std::string &consent_player_name) { - consented_player_names.erase(std::remove_if(consented_player_names.begin(), consented_player_names.end(), - [consent_player_name](const std::string& consented_player_name) { - return strcasecmp(consented_player_name.c_str(), consent_player_name.c_str()) == 0; - } - ), consented_player_names.end()); + m_consented_player_names.erase( + std::remove_if( + m_consented_player_names.begin(), m_consented_player_names.end(), + [consent_player_name](const std::string &consented_player_name) { + return strcasecmp(consented_player_name.c_str(), consent_player_name.c_str()) == 0; + } + ), m_consented_player_names.end() + ); } -uint32 Corpse::CountItems() { - return itemlist.size(); +uint32 Corpse::CountItems() +{ + return m_item_list.size(); } -void Corpse::AddItem(uint32 itemnum, +void Corpse::AddItem( + uint32 itemnum, uint16 charges, int16 slot, uint32 aug1, @@ -821,47 +793,49 @@ void Corpse::AddItem(uint32 itemnum, uint32 aug5, uint32 aug6, bool attuned, - const std::string& custom_data, + const std::string &custom_data, uint32 ornamenticon, uint32 ornamentidfile, - uint32 ornament_hero_model) { + uint32 ornament_hero_model +) +{ - if (!database.GetItem(itemnum)) + if (!database.GetItem(itemnum)) { return; + } - is_corpse_changed = true; + m_is_corpse_changed = true; - auto item = new LootItem; + auto i = new LootItem; - item->item_id = itemnum; - item->charges = charges; - item->equip_slot = slot; - item->aug_1 = aug1; - item->aug_2 = aug2; - item->aug_3 = aug3; - item->aug_4 = aug4; - item->aug_5 = aug5; - item->aug_6 = aug6; - item->attuned = attuned; - item->custom_data = custom_data; - item->ornamenticon = ornamenticon; - item->ornamentidfile = ornamentidfile; - item->ornament_hero_model = ornament_hero_model; + i->item_id = itemnum; + i->charges = charges; + i->equip_slot = slot; + i->aug_1 = aug1; + i->aug_2 = aug2; + i->aug_3 = aug3; + i->aug_4 = aug4; + i->aug_5 = aug5; + i->aug_6 = aug6; + i->attuned = attuned; + i->custom_data = custom_data; + i->ornamenticon = ornamenticon; + i->ornamentidfile = ornamentidfile; + i->ornament_hero_model = ornament_hero_model; - itemlist.push_back(item); + m_item_list.push_back(i); UpdateEquipmentLight(); } -LootItem* Corpse::GetItem(uint16 lootslot, LootItem** bag_item_data) { - LootItem *sitem = nullptr, *sitem2 = nullptr; +LootItem *Corpse::GetItem(uint16 lootslot, LootItem **bag_item_data) +{ + LootItem *sitem = nullptr; + LootItem *sitem2 = nullptr; - LootItems::iterator cur, end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - if((*cur)->lootslot == lootslot) { - sitem = *cur; + for (const auto &item: m_item_list) { + if (item->lootslot == lootslot) { + sitem = item; break; } } @@ -869,12 +843,10 @@ LootItem* Corpse::GetItem(uint16 lootslot, LootItem** bag_item_data) { if (sitem && bag_item_data && EQ::InventoryProfile::SupportsContainers(sitem->equip_slot)) { int16 bagstart = EQ::InventoryProfile::CalcSlotId(sitem->equip_slot, EQ::invbag::SLOT_BEGIN); - cur = itemlist.begin(); - end = itemlist.end(); - for (; cur != end; ++cur) { - sitem2 = *cur; - if (sitem2->equip_slot >= bagstart && sitem2->equip_slot < bagstart + 10) { - bag_item_data[sitem2->equip_slot - bagstart] = sitem2; + // convert above code to for loop + for (const auto &item: m_item_list) { + if (item->equip_slot >= bagstart && item->equip_slot < bagstart + 10) { + bag_item_data[item->equip_slot - bagstart] = item; } } } @@ -882,13 +854,10 @@ LootItem* Corpse::GetItem(uint16 lootslot, LootItem** bag_item_data) { return sitem; } -uint32 Corpse::GetWornItem(int16 equipSlot) const { - LootItems::const_iterator cur, end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - LootItem * item = *cur; - if (item->equip_slot == equipSlot) { +uint32 Corpse::GetWornItem(int16 equip_slot) const +{ + for (const auto &item: m_item_list) { + if (item->equip_slot == equip_slot) { return item->item_id; } } @@ -896,45 +865,46 @@ uint32 Corpse::GetWornItem(int16 equipSlot) const { return 0; } -void Corpse::RemoveItem(uint16 lootslot) { - if (lootslot == 0xFFFF) +void Corpse::RemoveItem(uint16 lootslot) +{ + if (lootslot == 0xFFFF) { return; + } - LootItems::iterator cur, end; - cur = itemlist.begin(); - end = itemlist.end(); - for (; cur != end; ++cur) { - LootItem * sitem = *cur; - if (sitem->lootslot == lootslot) { - RemoveItem(sitem); + for (const auto &item: m_item_list) { + if (item->lootslot == lootslot) { + RemoveItem(item); return; } } } -void Corpse::RemoveItem(LootItem* item_data) +void Corpse::RemoveItem(LootItem *item_data) { - for (auto iter = itemlist.begin(); iter != itemlist.end(); ++iter) { + for (auto iter = m_item_list.begin(); iter != m_item_list.end(); ++iter) { auto sitem = *iter; if (sitem != item_data) { continue; } - is_corpse_changed = true; - itemlist.erase(iter); + m_is_corpse_changed = true; + m_item_list.erase(iter); uint8 material = EQ::InventoryProfile::CalcMaterialFromSlot(sitem->equip_slot); // autos to unsigned char - if (material != EQ::textures::materialInvalid) + if (material != EQ::textures::materialInvalid) { SendWearChange(material); + } UpdateEquipmentLight(); - if (UpdateActiveLight()) + if (UpdateActiveLight()) { SendAppearancePacket(AppearanceType::Light, GetActiveLightType()); + } safe_delete(sitem); return; } } -void Corpse::RemoveItemByID(uint32 item_id, int quantity) { +void Corpse::RemoveItemByID(uint32 item_id, int quantity) +{ if (!database.GetItem(item_id)) { return; } @@ -943,9 +913,9 @@ void Corpse::RemoveItemByID(uint32 item_id, int quantity) { return; } - int removed_count = 0; - for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { - LootItem * sitem = *current_item; + int removed_count = 0; + for (auto current_item = m_item_list.begin(); current_item != m_item_list.end(); ++current_item) { + LootItem *sitem = *current_item; if (removed_count == quantity) { break; } @@ -954,18 +924,20 @@ void Corpse::RemoveItemByID(uint32 item_id, int quantity) { int stack_size = sitem->charges > 1 ? sitem->charges : 1; if ((removed_count + stack_size) <= quantity) { removed_count += stack_size; - is_corpse_changed = true; - itemlist.erase(current_item); - } else { + m_is_corpse_changed = true; + m_item_list.erase(current_item); + } + else { int amount_left = (quantity - removed_count); if (amount_left > 0) { if (stack_size > amount_left) { removed_count += amount_left; sitem->charges -= amount_left; - is_corpse_changed = true; - } else if (stack_size == amount_left) { + m_is_corpse_changed = true; + } + else if (stack_size == amount_left) { removed_count += amount_left; - itemlist.erase(current_item); + m_item_list.erase(current_item); } } } @@ -973,76 +945,105 @@ void Corpse::RemoveItemByID(uint32 item_id, int quantity) { } } -void Corpse::SetCash(uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_platinum) { - copper = in_copper; - silver = in_silver; - gold = in_gold; - platinum = in_platinum; - is_corpse_changed = true; +void Corpse::SetCash(uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_platinum) +{ + m_copper = in_copper; + m_silver = in_silver; + m_gold = in_gold; + m_platinum = in_platinum; + m_is_corpse_changed = true; } -void Corpse::RemoveCash() { - copper = 0; - silver = 0; - gold = 0; - platinum = 0; - is_corpse_changed = true; +void Corpse::RemoveCash() +{ + m_copper = 0; + m_silver = 0; + m_gold = 0; + m_platinum = 0; + m_is_corpse_changed = true; } -bool Corpse::IsEmpty() const { - if (copper != 0 || silver != 0 || gold != 0 || platinum != 0) +bool Corpse::IsEmpty() const +{ + if (m_copper != 0 || m_silver != 0 || m_gold != 0 || m_platinum != 0) { return false; + } - return itemlist.empty(); + return m_item_list.empty(); } -bool Corpse::Process() { - if (player_corpse_depop) +bool Corpse::Process() +{ + if (m_player_corpse_depop) { return false; + } - if (corpse_delay_timer.Check()) { - for (int i = 0; i < MAX_LOOTERS; i++) - allowed_looters[i] = 0; - corpse_delay_timer.Disable(); + if (m_check_owner_online_timer.Check() && m_is_rezzable) { + CheckIsOwnerOnline(); + } + + if (m_corpse_delay_timer.Check()) { + for (int &allowed_looter: m_allowed_looters) { + allowed_looter = 0; + } + m_corpse_delay_timer.Disable(); return true; } - if (corpse_graveyard_timer.Check()) { + if (m_corpse_graveyard_timer.Check()) { MovePlayerCorpseToGraveyard(); - corpse_graveyard_timer.Disable(); + m_corpse_graveyard_timer.Disable(); return false; } - /* - if(corpse_res_timer.Check()) { - can_rez = false; - corpse_res_timer.Disable(); + + // Player is offline. If rez timer is enabled, disable it and save corpse. + if (m_is_rezzable && m_check_rezzable_timer.Check()) { + if (!m_is_owner_online) { + if (m_corpse_rezzable_timer.Enabled()) { + m_remaining_rez_time = m_corpse_rezzable_timer.GetRemainingTime(); + m_corpse_rezzable_timer.Disable(); + m_is_corpse_changed = true; + Save(); + } + } + else { //Player is online. If rez timer is disabled, enable it. + if (m_corpse_rezzable_timer.Enabled()) { + m_remaining_rez_time = m_corpse_rezzable_timer.GetRemainingTime(); + } + else { + SetRezTimer(); + } + } + } + + if (m_corpse_rezzable_timer.Check()) { + CompleteResurrection(true); } - */ /* This is when a corpse hits decay timer and does checks*/ - if (corpse_decay_timer.Check()) { + if (m_corpse_decay_timer.Check()) { /* NPC */ - if (IsNPCCorpse()){ - corpse_decay_timer.Disable(); + if (IsNPCCorpse()) { + m_corpse_decay_timer.Disable(); return false; } /* Client */ - if (!RuleB(Zone, EnableShadowrest)){ + if (!RuleB(Zone, EnableShadowrest)) { Delete(); } else { - if (database.BuryCharacterCorpse(corpse_db_id)) { + if (database.BuryCharacterCorpse(m_corpse_db_id)) { Save(); - player_corpse_depop = true; - corpse_db_id = 0; - LogDebug("Tagged [{}] player corpse has buried", GetName()); + m_player_corpse_depop = true; + m_corpse_db_id = 0; + LogCorpses("Tagged [{}] player corpse has buried", GetName()); } else { LogError("Unable to bury [{}] player corpse", GetName()); return true; } } - corpse_decay_timer.Disable(); + m_corpse_decay_timer.Disable(); return false; } @@ -1051,160 +1052,167 @@ bool Corpse::Process() { void Corpse::ResetDecayTimer() { - int decay_ms = level > 54 ? RuleI(NPC, MajorNPCCorpseDecayTimeMS) : RuleI(NPC, MinorNPCCorpseDecayTimeMS); + int decay_ms = level > 54 ? RuleI(NPC, MajorNPCCorpseDecayTime) : RuleI(NPC, MinorNPCCorpseDecayTime); - if (IsPlayerCorpse()) - { - decay_ms = RuleI(Character, CorpseDecayTimeMS); + if (IsPlayerCorpse()) { + decay_ms = RuleI(Character, CorpseDecayTime); } - else if (IsEmpty()) - { - decay_ms = RuleI(NPC, EmptyNPCCorpseDecayTimeMS) + 1000; + else if (IsEmpty()) { + decay_ms = RuleI(NPC, EmptyNPCCorpseDecayTime) + 1000; } - else - { - for (const npcDecayTimes_Struct& decay_time : npcCorpseDecayTimes) - { - if (level >= decay_time.minlvl && level <= decay_time.maxlvl) - { + else { + for (const npcDecayTimes_Struct &decay_time: npcCorpseDecayTimes) { + if (level >= decay_time.minlvl && level <= decay_time.maxlvl) { decay_ms = decay_time.seconds * 1000; break; } } } - corpse_decay_timer.SetTimer(decay_ms); + m_corpse_decay_timer.SetTimer(decay_ms); } -void Corpse::SetDecayTimer(uint32 decaytime) { - if (decaytime == 0) - corpse_decay_timer.Trigger(); - else - corpse_decay_timer.Start(decaytime); +void Corpse::SetDecayTimer(uint32 decay_time) +{ + if (decay_time == 0) { + m_corpse_decay_timer.Trigger(); + } + else { + m_corpse_decay_timer.Start(decay_time); + } } -bool Corpse::CanPlayerLoot(int charid) { +bool Corpse::CanPlayerLoot(int character_id) +{ uint8 looters = 0; - for (int i = 0; i < MAX_LOOTERS; i++) { - if (allowed_looters[i] != 0){ + for (int allowed_looter: m_allowed_looters) { + if (allowed_looter != 0) { looters++; } - if (allowed_looters[i] == charid) + if (allowed_looter == character_id) { return true; + } } - /* If we have no looters, obviously client can loot */ + return looters == 0; } -void Corpse::AllowPlayerLoot(Mob *them, uint8 slot) { - if(slot >= MAX_LOOTERS) +void Corpse::AllowPlayerLoot(Mob *them, uint8 slot) +{ + if (slot >= MAX_LOOTERS) { return; - if(them == nullptr || !them->IsClient()) + } + if (them == nullptr || !them->IsClient()) { return; + } - allowed_looters[slot] = them->CastToClient()->CharacterID(); + m_allowed_looters[slot] = them->CastToClient()->CharacterID(); } -void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* app) { - if (!client) { +void Corpse::MakeLootRequestPackets(Client *c, const EQApplicationPacket *app) +{ + if (!c) { return; } // Added 12/08. Started compressing loot struct on live. - if (player_corpse_depop) { - SendLootReqErrorPacket(client, LootResponse::SomeoneElse); + if (m_player_corpse_depop) { + SendLootReqErrorPacket(c, LootResponse::SomeoneElse); return; } - if (IsPlayerCorpse() && !corpse_db_id) { // really should try to resave in this case + if (IsPlayerCorpse() && !m_corpse_db_id) { // really should try to resave in this case // SendLootReqErrorPacket(client, 0); - client->Message(Chat::Red, "Warning: Corpse's dbid = 0! Corpse will not survive zone shutdown!"); + c->Message(Chat::Red, "Warning: Corpse's dbid = 0! Corpse will not survive zone shutdown!"); std::cout << "Error: PlayerCorpse::MakeLootRequestPackets: dbid = 0!" << std::endl; // return; } - if (is_locked && client->Admin() < AccountStatus::GMAdmin) { - SendLootReqErrorPacket(client, LootResponse::SomeoneElse); - client->Message(Chat::Red, "Error: Corpse locked by GM."); + if (m_is_locked && c->Admin() < AccountStatus::GMAdmin) { + SendLootReqErrorPacket(c, LootResponse::SomeoneElse); + c->Message(Chat::Red, "Error: Corpse locked by GM."); return; } - if (!being_looted_by || (being_looted_by != 0xFFFFFFFF && !entity_list.GetID(being_looted_by))) { - being_looted_by = 0xFFFFFFFF; + if (!m_being_looted_by_entity_id || + (m_being_looted_by_entity_id != 0xFFFFFFFF && !entity_list.GetID(m_being_looted_by_entity_id))) { + m_being_looted_by_entity_id = 0xFFFFFFFF; } - if (DistanceSquaredNoZ(client->GetPosition(), m_Position) > 625) { - SendLootReqErrorPacket(client, LootResponse::TooFar); + if (DistanceSquaredNoZ(c->GetPosition(), m_Position) > 625) { + SendLootReqErrorPacket(c, LootResponse::TooFar); return; } - if (being_looted_by != 0xFFFFFFFF && being_looted_by != client->GetID()) { - SendLootReqErrorPacket(client, LootResponse::SomeoneElse); + if (m_being_looted_by_entity_id != 0xFFFFFFFF && m_being_looted_by_entity_id != c->GetID()) { + SendLootReqErrorPacket(c, LootResponse::SomeoneElse); return; } // all loot session disqualifiers should occur before this point as not to interfere with any current looter - loot_request_type = LootRequestType::Forbidden; + m_loot_request_type = LootRequestType::Forbidden; // loot_request_type is scoped to class Corpse and reset on a per-loot session basis - if (client->GetGM()) { - if (client->Admin() >= AccountStatus::GMAdmin) { - loot_request_type = LootRequestType::GMAllowed; + if (c->GetGM()) { + if (c->Admin() >= AccountStatus::GMAdmin) { + m_loot_request_type = LootRequestType::GMAllowed; - } else { - loot_request_type = LootRequestType::GMPeek; + } + else { + m_loot_request_type = LootRequestType::GMPeek; } } else { if (IsPlayerCorpse()) { - if (char_id == client->CharacterID()) { - loot_request_type = LootRequestType::Self; + if (m_character_id == c->CharacterID()) { + m_loot_request_type = LootRequestType::Self; } - else if (CanPlayerLoot(client->CharacterID())) { + else if (CanPlayerLoot(c->CharacterID())) { if (GetPlayerKillItem() == -1) { - loot_request_type = LootRequestType::AllowedPVPAll; + m_loot_request_type = LootRequestType::AllowedPVPAll; - } else if (GetPlayerKillItem() == 1) { - loot_request_type = LootRequestType::AllowedPVPSingle; + } + else if (GetPlayerKillItem() == 1) { + m_loot_request_type = LootRequestType::AllowedPVPSingle; - } else if (GetPlayerKillItem() > 1) { - loot_request_type = LootRequestType::AllowedPVPDefined; + } + else if (GetPlayerKillItem() > 1) { + m_loot_request_type = LootRequestType::AllowedPVPDefined; } } } - else if ((IsNPCCorpse() || become_npc) && CanPlayerLoot(client->CharacterID())) { - loot_request_type = LootRequestType::AllowedPVE; + else if ((IsNPCCorpse() || m_become_npc) && CanPlayerLoot(c->CharacterID())) { + m_loot_request_type = LootRequestType::AllowedPVE; } } - LogInventory("MakeLootRequestPackets() LootRequestType [{}] for [{}]", (int) loot_request_type, client->GetName()); + LogInventory( + "m_loot_request_type [{}] for [{}]", + (int) m_loot_request_type, + c->GetName() + ); - if (loot_request_type == LootRequestType::Forbidden) { - SendLootReqErrorPacket(client, LootResponse::NotAtThisTime); + if (m_loot_request_type == LootRequestType::Forbidden) { + SendLootReqErrorPacket(c, LootResponse::NotAtThisTime); return; } - being_looted_by = client->GetID(); - client->CommonBreakInvisible(); // we should be "all good" so lets break invis now instead of earlier before all error checking is done + m_being_looted_by_entity_id = c->GetID(); + c->CommonBreakInvisible(); // we should be "all good" so lets break invis now instead of earlier before all error checking is done // process coin - bool loot_coin = false; + bool loot_coin = false; std::string tmp; if (database.GetVariable("LootCoin", tmp)) { loot_coin = (tmp[0] == 1 && tmp[1] == '\0'); } - if (loot_request_type == LootRequestType::GMPeek || loot_request_type == LootRequestType::GMAllowed) { - if ( - GetPlatinum() || - GetGold() || - GetSilver() || - GetCopper() - ) { - client->Message( + if (m_loot_request_type == LootRequestType::GMPeek || m_loot_request_type == LootRequestType::GMAllowed) { + if (GetPlatinum() || GetGold() || GetSilver() || GetCopper()) { + c->Message( Chat::Yellow, fmt::format( "This corpse contains {}.", @@ -1216,244 +1224,260 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a ) ).c_str() ); - } else { - client->Message(Chat::Yellow, "This corpse contains no money."); + } + else { + c->Message(Chat::Yellow, "This corpse contains no money."); } auto outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct)); - moneyOnCorpseStruct* d = (moneyOnCorpseStruct*)outapp->pBuffer; + auto *d = (moneyOnCorpseStruct *) outapp->pBuffer; d->response = static_cast(LootResponse::Normal); d->unknown1 = 0x42; d->unknown2 = 0xef; - d->copper = 0; - d->silver = 0; - d->gold = 0; + d->copper = 0; + d->silver = 0; + d->gold = 0; d->platinum = 0; outapp->priority = 6; - client->QueuePacket(outapp); + c->QueuePacket(outapp); safe_delete(outapp); } else { auto outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct)); - moneyOnCorpseStruct* d = (moneyOnCorpseStruct*)outapp->pBuffer; + auto *d = (moneyOnCorpseStruct *) outapp->pBuffer; d->response = static_cast(LootResponse::Normal); d->unknown1 = 0x42; d->unknown2 = 0xef; - Group* cgroup = client->GetGroup(); + Group *cgroup = c->GetGroup(); // this can be reworked into a switch and/or massaged to include specialized pve loot rules based on 'LootRequestType' - if (!IsPlayerCorpse() && client->IsGrouped() && client->AutoSplitEnabled() && cgroup) { - d->copper = 0; - d->silver = 0; - d->gold = 0; + if (!IsPlayerCorpse() && c->IsGrouped() && c->AutoSplitEnabled() && cgroup) { + d->copper = 0; + d->silver = 0; + d->gold = 0; d->platinum = 0; - cgroup->SplitMoney(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), client); + cgroup->SplitMoney(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), c); } else { - d->copper = GetCopper(); - d->silver = GetSilver(); - d->gold = GetGold(); + d->copper = GetCopper(); + d->silver = GetSilver(); + d->gold = GetGold(); d->platinum = GetPlatinum(); - client->AddMoneyToPP(GetCopper(), GetSilver(), GetGold(), GetPlatinum()); + c->AddMoneyToPP(GetCopper(), GetSilver(), GetGold(), GetPlatinum()); } RemoveCash(); Save(); outapp->priority = 6; - client->QueuePacket(outapp); + c->QueuePacket(outapp); safe_delete(outapp); } // process items - auto timestamps = database.GetItemRecastTimestamps(client->CharacterID()); + auto timestamps = database.GetItemRecastTimestamps(c->CharacterID()); - if (loot_request_type == LootRequestType::AllowedPVPDefined) { + if (m_loot_request_type == LootRequestType::AllowedPVPDefined) { auto pkitemid = GetPlayerKillItem(); - auto pkitem = database.GetItem(pkitemid); - auto pkinst = database.CreateItem(pkitem, pkitem->MaxCharges); + auto pkitem = database.GetItem(pkitemid); + auto pkinst = database.CreateItem(pkitem, pkitem->MaxCharges); if (pkinst) { 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->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); - client->SendItemPacket(EQ::invslot::CORPSE_BEGIN, pkinst, ItemPacketLoot); + c->SendItemPacket(EQ::invslot::CORPSE_BEGIN, pkinst, ItemPacketLoot); safe_delete(pkinst); } else { LogInventory("MakeLootRequestPackets() PlayerKillItem [{}] not found", pkitemid); - client->Message(Chat::Red, "PlayerKillItem (id: %i) could not be found!", pkitemid); + c->Message(Chat::Red, "PlayerKillItem (id: %i) could not be found!", pkitemid); } - client->QueuePacket(app); + c->QueuePacket(app); return; } - auto loot_slot = EQ::invslot::CORPSE_BEGIN; - auto corpse_mask = client->GetInv().GetLookup()->CorpseBitmask; + auto loot_slot = EQ::invslot::CORPSE_BEGIN; + auto corpse_mask = c->GetInv().GetLookup()->CorpseBitmask; - for (auto item_data : itemlist) { + for (auto i: m_item_list) { // every loot session must either set all items' lootslots to 'invslot::SLOT_INVALID' // or to a valid enumerated client-versioned corpse slot (lootslot is not equip_slot) - item_data->lootslot = 0xFFFF; + i->lootslot = 0xFFFF; // align server and client corpse slot mappings so translators can function properly - while (loot_slot <= EQ::invslot::CORPSE_END && (((uint64)1 << loot_slot) & corpse_mask) == 0) + while (loot_slot <= EQ::invslot::CORPSE_END && (((uint64) 1 << loot_slot) & corpse_mask) == 0) ++loot_slot; - if (loot_slot > EQ::invslot::CORPSE_END) + if (loot_slot > EQ::invslot::CORPSE_END) { continue; - - if (IsPlayerCorpse()) { - if (loot_request_type == LootRequestType::AllowedPVPSingle && loot_slot != EQ::invslot::CORPSE_BEGIN) - continue; - - if (item_data->equip_slot < EQ::invslot::POSSESSIONS_BEGIN || item_data->equip_slot > EQ::invslot::POSSESSIONS_END) - continue; } - const auto *item = database.GetItem(item_data->item_id); + if (IsPlayerCorpse()) { + if (m_loot_request_type == LootRequestType::AllowedPVPSingle && loot_slot != EQ::invslot::CORPSE_BEGIN) { + continue; + } + + if (i->equip_slot < EQ::invslot::POSSESSIONS_BEGIN || + i->equip_slot > EQ::invslot::POSSESSIONS_END) { + continue; + } + } + + const auto *item = database.GetItem(i->item_id); + auto 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, - item_data->custom_data, - item_data->ornamenticon, - item_data->ornamentidfile, - item_data->ornament_hero_model + i->charges, + i->aug_1, + i->aug_2, + i->aug_3, + i->aug_4, + i->aug_5, + i->aug_6, + i->attuned, + i->custom_data, + i->ornamenticon, + i->ornamentidfile, + i->ornament_hero_model ); - if (!inst) + if (!inst) { continue; + } if (item->RecastDelay) { if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM) { inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); - } else { + } + else { inst->SetRecastTimestamp(timestamps.count(item->ID) ? timestamps.at(item->ID) : 0); } } LogInventory("MakeLootRequestPackets() Slot [{}], Item [{}]", loot_slot, item->Name); - client->SendItemPacket(loot_slot, inst, ItemPacketLoot); + c->SendItemPacket(loot_slot, inst, ItemPacketLoot); safe_delete(inst); - item_data->lootslot = loot_slot++; + i->lootslot = loot_slot++; } // Disgrace: Client seems to require that we send the packet back... - client->QueuePacket(app); + c->QueuePacket(app); // This is required for the 'Loot All' feature to work for SoD clients. I expect it is to tell the client that the // server has now sent all the items on the corpse. - if (client->ClientVersion() >= EQ::versions::ClientVersion::SoD) - SendLootReqErrorPacket(client, LootResponse::LootAll); + if (c->ClientVersion() >= EQ::versions::ClientVersion::SoD) { + SendLootReqErrorPacket(c, LootResponse::LootAll); + } } -void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) +void Corpse::LootCorpseItem(Client *c, const EQApplicationPacket *app) { - if (!client) { + if (!c) { return; } - auto lootitem = (LootingItem_Struct *)app->pBuffer; + auto lootitem = (LootingItem_Struct *) app->pBuffer; - LogInventory("LootItem() LootRequestType [{}], Slot [{}] for [{}]", (int) loot_request_type, lootitem->slot_id, client->GetName()); + LogInventory( + "m_loot_request_type [{}] slot_id [{}] for [{}]", + (int) m_loot_request_type, + lootitem->slot_id, + c->GetName() + ); - if (loot_request_type < LootRequestType::GMAllowed) { // LootRequestType::Forbidden and LootRequestType::GMPeek - client->QueuePacket(app); - SendEndLootErrorPacket(client); + if (m_loot_request_type < LootRequestType::GMAllowed) { // LootRequestType::Forbidden and LootRequestType::GMPeek + c->QueuePacket(app); + SendEndLootErrorPacket(c); // unlock corpse for others - if (IsBeingLootedBy(client)) { + if (IsBeingLootedBy(c)) { ResetLooter(); } return; } - if (!loot_cooldown_timer.Check()) { - client->QueuePacket(app); - SendEndLootErrorPacket(client); + if (!m_loot_cooldown_timer.Check()) { + c->QueuePacket(app); + SendEndLootErrorPacket(c); // unlock corpse for others - if (IsBeingLootedBy(client)) { + if (IsBeingLootedBy(c)) { ResetLooter(); } return; } /* To prevent item loss for a player using 'Loot All' who doesn't have inventory space for all their items. */ - if (RuleB(Character, CheckCursorEmptyWhenLooting) && !client->GetInv().CursorEmpty()) { - client->Message(Chat::Red, "You may not loot an item while you have an item on your cursor."); - client->QueuePacket(app); - SendEndLootErrorPacket(client); + if (RuleB(Character, CheckCursorEmptyWhenLooting) && !c->GetInv().CursorEmpty()) { + c->Message(Chat::Red, "You may not loot an item while you have an item on your cursor."); + c->QueuePacket(app); + SendEndLootErrorPacket(c); /* Unlock corpse for others */ - if (IsBeingLootedBy(client)) { + if (IsBeingLootedBy(c)) { ResetLooter(); } return; } - if (!IsBeingLootedBy(client)) { - client->QueuePacket(app); - SendEndLootErrorPacket(client); + if (!IsBeingLootedBy(c)) { + c->QueuePacket(app); + SendEndLootErrorPacket(c); return; } - if (IsPlayerCorpse() && !CanPlayerLoot(client->CharacterID()) && !become_npc && - (char_id != client->CharacterID() && client->Admin() < AccountStatus::GMLeadAdmin)) { - client->Message(Chat::Red, "Error: This is a player corpse and you dont own it."); - client->QueuePacket(app); - SendEndLootErrorPacket(client); + if (IsPlayerCorpse() && !CanPlayerLoot(c->CharacterID()) && !m_become_npc && + (m_character_id != c->CharacterID() && c->Admin() < AccountStatus::GMLeadAdmin)) { + c->Message(Chat::Red, "Error: This is a player corpse and you dont own it."); + c->QueuePacket(app); + SendEndLootErrorPacket(c); return; } - if (is_locked && client->Admin() < AccountStatus::GMAdmin) { - client->QueuePacket(app); - SendLootReqErrorPacket(client, LootResponse::SomeoneElse); - client->Message(Chat::Red, "Error: Corpse locked by GM."); + if (m_is_locked && c->Admin() < AccountStatus::GMAdmin) { + c->QueuePacket(app); + SendLootReqErrorPacket(c, LootResponse::SomeoneElse); + c->Message(Chat::Red, "Error: Corpse locked by GM."); return; } - if (IsPlayerCorpse() && (char_id != client->CharacterID()) && CanPlayerLoot(client->CharacterID()) && + if (IsPlayerCorpse() && (m_character_id != c->CharacterID()) && CanPlayerLoot(c->CharacterID()) && GetPlayerKillItem() == 0) { - client->Message(Chat::Red, "Error: You cannot loot any more items from this corpse."); - client->QueuePacket(app); - SendEndLootErrorPacket(client); + c->Message(Chat::Red, "Error: You cannot loot any more items from this corpse."); + c->QueuePacket(app); + SendEndLootErrorPacket(c); ResetLooter(); return; } - const EQ::ItemData *item = nullptr; - EQ::ItemInstance *inst = nullptr; - LootItem *item_data = nullptr, *bag_item_data[10] = {}; + const EQ::ItemData *item = nullptr; + EQ::ItemInstance *inst = nullptr; + LootItem *item_data = nullptr, *bag_item_data[10] = {}; memset(bag_item_data, 0, sizeof(bag_item_data)); if (GetPlayerKillItem() > 1) { item = database.GetItem(GetPlayerKillItem()); } else if (GetPlayerKillItem() == -1 || GetPlayerKillItem() == 1) { - item_data = - GetItem(lootitem->slot_id); // dont allow them to loot entire bags of items as pvp reward + item_data = GetItem(lootitem->slot_id); // dont allow them to loot entire bags of items as pvp reward } else { item_data = GetItem(lootitem->slot_id, bag_item_data); @@ -1463,24 +1487,26 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) item = database.GetItem(item_data->item_id); } - if (item != 0) { + if (item) { if (item_data) { - inst = database.CreateItem(item, item_data ? item_data->charges : 0, item_data->aug_1, + inst = database.CreateItem( + item, item_data ? item_data->charges : 0, 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, item_data->custom_data, item_data->ornamenticon, - item_data->ornamentidfile, item_data->ornament_hero_model); + item_data->ornamentidfile, item_data->ornament_hero_model + ); } else { inst = database.CreateItem(item); } } - if (client && inst) { - if (client->CheckLoreConflict(item)) { - client->MessageString(Chat::White, LOOT_LORE_ERROR); - client->QueuePacket(app); - SendEndLootErrorPacket(client); + if (c && inst) { + if (c->CheckLoreConflict(item)) { + c->MessageString(Chat::White, LOOT_LORE_ERROR); + c->QueuePacket(app); + SendEndLootErrorPacket(c); ResetLooter(); delete inst; return; @@ -1490,10 +1516,10 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { EQ::ItemInstance *itm = inst->GetAugment(i); if (itm) { - if (client->CheckLoreConflict(itm->GetItem())) { - client->MessageString(Chat::White, LOOT_LORE_ERROR); - client->QueuePacket(app); - SendEndLootErrorPacket(client); + if (c->CheckLoreConflict(itm->GetItem())) { + c->MessageString(Chat::White, LOOT_LORE_ERROR); + c->QueuePacket(app); + SendEndLootErrorPacket(c); ResetLooter(); delete inst; return; @@ -1508,7 +1534,7 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) auto controller = entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID); if (controller) { if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_LOOT_ZONE)) { - const auto& export_string = fmt::format( + const auto &export_string = fmt::format( "{} {} {} {}", inst->GetItem()->ID, inst->GetCharges(), @@ -1516,8 +1542,8 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) GetID() ); - std::vector args = { inst, this }; - if (parse->EventNPC(EVENT_LOOT_ZONE, controller, client, export_string, 0, &args) != 0) { + std::vector args = {inst, this}; + if (parse->EventNPC(EVENT_LOOT_ZONE, controller, c, export_string, 0, &args) != 0) { prevent_loot = true; } } @@ -1525,7 +1551,7 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) } if (parse->PlayerHasQuestSub(EVENT_LOOT)) { - const auto& export_string = fmt::format( + const auto &export_string = fmt::format( "{} {} {} {}", inst->GetItem()->ID, inst->GetCharges(), @@ -1533,8 +1559,8 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) GetID() ); - std::vector args = { inst, this }; - if (parse->EventPlayer(EVENT_LOOT, client, export_string, 0, &args) != 0) { + std::vector args = {inst, this}; + if (parse->EventPlayer(EVENT_LOOT, c, export_string, 0, &args) != 0) { prevent_loot = true; } } @@ -1548,23 +1574,21 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) .corpse_name = EntityList::RemoveNumbers(corpse_name) }; - RecordPlayerEventLogWithClient(client, PlayerEvent::LOOT_ITEM, e); + RecordPlayerEventLogWithClient(c, PlayerEvent::LOOT_ITEM, e); } - if (!IsPlayerCorpse()) - { + if (!IsPlayerCorpse()) { // dynamic zones may prevent looting by non-members or based on lockouts auto dz = zone->GetDynamicZone(); - if (dz && !dz->CanClientLootCorpse(client, GetNPCTypeID(), GetID())) - { + if (dz && !dz->CanClientLootCorpse(c, GetNPCTypeID(), GetID())) { prevent_loot = true; // note on live this message is only sent once on the first loot attempt of an open corpse - client->MessageString(Chat::Loot, LOOT_NOT_ALLOWED, inst->GetItem()->Name); + c->MessageString(Chat::Loot, LOOT_NOT_ALLOWED, inst->GetItem()->Name); } } if (parse->ItemHasQuestSub(inst, EVENT_LOOT)) { - const auto& export_string = fmt::format( + const auto &export_string = fmt::format( "{} {} {} {}", inst->GetItem()->ID, inst->GetCharges(), @@ -1572,35 +1596,35 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) GetID() ); - std::vector args = { inst, this }; - if (parse->EventItem(EVENT_LOOT, client, inst, this, export_string, 0, &args) != 0) { + std::vector args = {inst, this}; + if (parse->EventItem(EVENT_LOOT, c, inst, this, export_string, 0, &args) != 0) { prevent_loot = true; } } if (prevent_loot) { lootitem->auto_loot = -1; - client->QueuePacket(app); + c->QueuePacket(app); safe_delete(inst); return; } // safe to ACK now - client->QueuePacket(app); + c->QueuePacket(app); if ( !IsPlayerCorpse() && RuleB(Character, EnableDiscoveredItems) && - client && - !client->GetGM() && - !client->IsDiscovered(inst->GetItem()->ID) - ) { - client->DiscoverItem(inst->GetItem()->ID); + c && + !c->GetGM() && + !c->IsDiscovered(inst->GetItem()->ID) + ) { + c->DiscoverItem(inst->GetItem()->ID); } if (zone->adv_data) { - ServerZoneAdventureDataReply_Struct *ad = (ServerZoneAdventureDataReply_Struct *)zone->adv_data; + auto *ad = (ServerZoneAdventureDataReply_Struct *) zone->adv_data; if (ad->type == Adventure_Collect && !IsPlayerCorpse()) { if (ad->data_id == inst->GetItem()->ID) { zone->DoAdventureCountIncrease(); @@ -1609,38 +1633,42 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) } // get count for task update before it's mutated by AutoPutLootInInventory - int count = inst->IsStackable() ? inst->GetCharges() : 1; + 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(); + auto timestamps = database.GetItemRecastTimestamps(c->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 { + } + 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) { - if (!client->AutoPutLootInInventory(*inst, true, true, bag_item_data)) - client->PutLootInInventory(EQ::invslot::slotCursor, *inst, bag_item_data); + if (!c->AutoPutLootInInventory(*inst, true, true, bag_item_data)) { + c->PutLootInInventory(EQ::invslot::slotCursor, *inst, bag_item_data); + } } else { - client->PutLootInInventory(EQ::invslot::slotCursor, *inst, bag_item_data); + c->PutLootInInventory(EQ::invslot::slotCursor, *inst, bag_item_data); } /* Update any tasks that have an activity to loot this item */ if (RuleB(TaskSystem, EnableTaskSystem) && IsNPCCorpse()) { - client->UpdateTasksOnLoot(this, item->ID, count); + c->UpdateTasksOnLoot(this, item->ID, count); } /* Remove it from Corpse */ if (item_data) { /* Delete needs to be before RemoveItem because its deletes the pointer for * item_data/bag_item_data */ - database.DeleteItemOffCharacterCorpse(corpse_db_id, item_data->equip_slot, - item_data->item_id); + database.DeleteItemOffCharacterCorpse( + m_corpse_db_id, item_data->equip_slot, + item_data->item_id + ); /* Delete Item Instance */ RemoveItem(item_data->lootslot); } @@ -1651,9 +1679,11 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) if (bag_item_data[i]) { /* Delete needs to be before RemoveItem because its deletes the pointer for * item_data/bag_item_data */ - database.DeleteItemOffCharacterCorpse(corpse_db_id, + database.DeleteItemOffCharacterCorpse( + m_corpse_db_id, bag_item_data[i]->equip_slot, - bag_item_data[i]->item_id); + bag_item_data[i]->item_id + ); /* Delete Item Instance */ RemoveItem(bag_item_data[i]); } @@ -1671,65 +1701,79 @@ void Corpse::LootCorpseItem(Client *client, const EQApplicationPacket *app) linker.GenerateLink(); - client->MessageString(Chat::Loot, LOOTED_MESSAGE, linker.Link().c_str()); + c->MessageString(Chat::Loot, LOOTED_MESSAGE, linker.Link().c_str()); if (!IsPlayerCorpse()) { - Group *g = client->GetGroup(); + Group *g = c->GetGroup(); if (g != nullptr) { - g->GroupMessageString(client, Chat::Loot, OTHER_LOOTED_MESSAGE, - client->GetName(), linker.Link().c_str()); + g->GroupMessageString( + c, Chat::Loot, + OTHER_LOOTED_MESSAGE, + c->GetName(), + linker.Link().c_str() + ); } else { - Raid *r = client->GetRaid(); + Raid *r = c->GetRaid(); if (r != nullptr) { - r->RaidMessageString(client, Chat::Loot, OTHER_LOOTED_MESSAGE, - client->GetName(), linker.Link().c_str()); + r->RaidMessageString( + c, + Chat::Loot, + OTHER_LOOTED_MESSAGE, + c->GetName(), + linker.Link().c_str() + ); } } } } else { - SendEndLootErrorPacket(client); + SendEndLootErrorPacket(c); safe_delete(inst); return; } if (IsPlayerCorpse()) { - client->SendItemLink(inst); + c->SendItemLink(inst); } else { - client->SendItemLink(inst, true); + c->SendItemLink(inst, true); } safe_delete(inst); } -void Corpse::EndLoot(Client* client, const EQApplicationPacket* app) { +void Corpse::EndLoot(Client *c, const EQApplicationPacket *app) +{ auto outapp = new EQApplicationPacket; outapp->SetOpcode(OP_LootComplete); outapp->size = 0; - client->QueuePacket(outapp); + c->QueuePacket(outapp); safe_delete(outapp); - being_looted_by = 0xFFFFFFFF; - if (IsEmpty()) + m_being_looted_by_entity_id = 0xFFFFFFFF; + if (IsEmpty()) { Delete(); - else + } + else { Save(); + } } -void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { +void Corpse::FillSpawnStruct(NewSpawn_Struct *ns, Mob *ForWho) +{ Mob::FillSpawnStruct(ns, ForWho); ns->spawn.max_hp = 120; - ns->spawn.NPC = 2; + ns->spawn.NPC = 2; UpdateActiveLight(); ns->spawn.light = m_Light.Type[EQ::lightsource::LightActive]; } -void Corpse::QueryLoot(Client* to) { - if (itemlist.size() > 0) { +void Corpse::QueryLoot(Client *to) +{ + if (!m_item_list.empty()) { int player_corpse_limit = to->GetInv().GetLookup()->InventoryTypeSize.Corpse; to->Message( Chat::White, @@ -1740,8 +1784,8 @@ void Corpse::QueryLoot(Client* to) { ).c_str() ); - int item_count = 0; - for (auto current_item : itemlist) { + int item_count = 0; + for (auto current_item: m_item_list) { int item_number = (item_count + 1); if (!current_item) { LogError("Corpse::QueryLoot() - ItemList error, null item."); @@ -1766,11 +1810,11 @@ void Corpse::QueryLoot(Client* to) { current_item->item_id, ( current_item->charges > 1 ? - fmt::format( - " Amount: {}", - current_item->charges - ) : - "" + fmt::format( + " Amount: {}", + current_item->charges + ) : + "" ) ).c_str() ); @@ -1778,179 +1822,190 @@ void Corpse::QueryLoot(Client* to) { } } - if ( - platinum || - gold || - silver || - copper - ) { + if (m_platinum || m_gold || m_silver || m_copper) { to->Message( Chat::White, fmt::format( "Money | {}", Strings::Money( - platinum, - gold, - silver, - copper + m_platinum, + m_gold, + m_silver, + m_copper ) ).c_str() ); } } -bool Corpse::HasItem(uint32 item_id) { +bool Corpse::HasItem(uint32 item_id) +{ if (!database.GetItem(item_id)) { return false; } - for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { - LootItem * loot_item = *current_item; - if (!loot_item) { + for (auto i: m_item_list) { + if (!i) { LogError("Corpse::HasItem() - ItemList error, null item"); continue; } - if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { + if (!i->item_id || !database.GetItem(i->item_id)) { LogError("Corpse::HasItem() - Database error, invalid item"); continue; } - if (loot_item->item_id == item_id) { + if (i->item_id == item_id) { return true; } } return false; } -uint16 Corpse::CountItem(uint32 item_id) { +uint16 Corpse::CountItem(uint32 item_id) +{ uint16 item_count = 0; if (!database.GetItem(item_id)) { return item_count; } - for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { - LootItem * loot_item = *current_item; - if (!loot_item) { + for (auto i: m_item_list) { + if (!i) { LogError("Corpse::CountItem() - ItemList error, null item"); continue; } - if (!loot_item->item_id || !database.GetItem(loot_item->item_id)) { + if (!i->item_id || !database.GetItem(i->item_id)) { LogError("Corpse::CountItem() - Database error, invalid item"); continue; } - if (loot_item->item_id == item_id) { - item_count += loot_item->charges > 0 ? loot_item->charges : 1; + if (i->item_id == item_id) { + item_count += i->charges > 0 ? i->charges : 1; } } return item_count; } -uint32 Corpse::GetItemIDBySlot(uint16 loot_slot) { - for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { - LootItem * loot_item = *current_item; - if (loot_item->lootslot == loot_slot) { - return loot_item->item_id; +uint32 Corpse::GetItemIDBySlot(uint16 loot_slot) +{ + for (auto i: m_item_list) { + if (i->lootslot == loot_slot) { + return i->item_id; } } + return 0; } -uint16 Corpse::GetFirstLootSlotByItemID(uint32 item_id) { - for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { - LootItem * loot_item = *current_item; - if (loot_item->item_id == item_id) { - return loot_item->lootslot; +uint16 Corpse::GetFirstLootSlotByItemID(uint32 item_id) +{ + for (auto i: m_item_list) { + if (i->item_id == item_id) { + return i->lootslot; } } + return 0; } -bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { +bool Corpse::Summon(Client *c, bool spell, bool CheckDistance) +{ uint32 dist2 = 10000; // pow(100, 2); if (!spell) { - if (GetCharID() == client->CharacterID()) { - if (IsLocked() && client->Admin() < AccountStatus::GMAdmin) { - client->Message(Chat::Red, "That corpse is locked by a GM."); + if (GetCharID() == c->CharacterID()) { + if (IsLocked() && c->Admin() < AccountStatus::GMAdmin) { + c->Message(Chat::Red, "That corpse is locked by a GM."); return false; } - if (!CheckDistance || (DistanceSquaredNoZ(m_Position, client->GetPosition()) <= dist2)) { - GMMove(client->GetX(), client->GetY(), client->GetZ()); - is_corpse_changed = true; + if (!CheckDistance || (DistanceSquaredNoZ(m_Position, c->GetPosition()) <= dist2)) { + GMMove(c->GetX(), c->GetY(), c->GetZ()); + m_is_corpse_changed = true; } else { - client->MessageString(Chat::Red, CORPSE_TOO_FAR); + c->MessageString(Chat::Red, CORPSE_TOO_FAR); return false; } } - else - { - bool consented = false; - for (const auto& consented_player_name : consented_player_names) { - if (strcasecmp(client->GetName(), consented_player_name.c_str()) == 0) { + else { + bool consented = false; + for (const auto &consented_player_name: m_consented_player_names) { + if (strcasecmp(c->GetName(), consented_player_name.c_str()) == 0) { consented = true; break; } } - if (!consented && consented_guild_id && consented_guild_id != GUILD_NONE) { - if (client->GuildID() == consented_guild_id) { + if (!consented && m_consented_guild_id && m_consented_guild_id != GUILD_NONE) { + if (c->GuildID() == m_consented_guild_id) { consented = true; } } - if (!consented && consented_group_id) { - Group* grp = client->GetGroup(); - if (grp && grp->GetID() == consented_group_id) { + if (!consented && m_consented_group_id) { + Group *grp = c->GetGroup(); + if (grp && grp->GetID() == m_consented_group_id) { consented = true; } } - if (!consented && consented_raid_id) { - Raid* raid = client->GetRaid(); - if (raid && raid->GetID() == consented_raid_id) { + if (!consented && m_consented_raid_id) { + Raid *raid = c->GetRaid(); + if (raid && raid->GetID() == m_consented_raid_id) { consented = true; } } if (consented) { - if (!CheckDistance || (DistanceSquaredNoZ(m_Position, client->GetPosition()) <= dist2)) { - GMMove(client->GetX(), client->GetY(), client->GetZ()); - is_corpse_changed = true; + if (!CheckDistance || (DistanceSquaredNoZ(m_Position, c->GetPosition()) <= dist2)) { + GMMove(c->GetX(), c->GetY(), c->GetZ()); + m_is_corpse_changed = true; } else { - client->MessageString(Chat::Red, CORPSE_TOO_FAR); + c->MessageString(Chat::Red, CORPSE_TOO_FAR); return false; } } else { - client->MessageString(Chat::Red, CONSENT_DENIED); + c->MessageString(Chat::Red, CONSENT_DENIED); return false; } } } else { - GMMove(client->GetX(), client->GetY(), client->GetZ()); - is_corpse_changed = true; + GMMove(c->GetX(), c->GetY(), c->GetZ()); + m_is_corpse_changed = true; } Save(); return true; } -void Corpse::CompleteResurrection(){ - rez_experience = 0; - is_corpse_changed = true; +void Corpse::CompleteResurrection(bool timer_expired) +{ + LogCorpses("Corpse [{}] has been rezzed.", GetName()); + + m_remaining_rez_time = m_corpse_rezzable_timer.GetRemainingTime(); + + if (timer_expired) { + m_remaining_rez_time = 0; + m_is_rezzable = false; // Players can no longer rez this corpse. + m_corpse_rezzable_timer.Disable(); + } + + IsRezzed(true); // Players can rez this corpse for no XP (corpse gate) provided rezzable is true. + m_rezzed_experience = 0; + m_is_corpse_changed = true; Save(); } -void Corpse::Spawn() { +void Corpse::Spawn() +{ auto app = new EQApplicationPacket; CreateSpawnPacket(app, this); entity_list.QueueClients(this, app); safe_delete(app); } -uint32 Corpse::GetEquippedItemFromTextureSlot(uint8 material_slot) const { +uint32 Corpse::GetEquippedItemFromTextureSlot(uint8 material_slot) const +{ int16 invslot; if (material_slot > EQ::textures::LastTexture) { @@ -1958,13 +2013,15 @@ uint32 Corpse::GetEquippedItemFromTextureSlot(uint8 material_slot) const { } invslot = EQ::InventoryProfile::CalcSlotFromMaterial(material_slot); - if(invslot == INVALID_INDEX) // GetWornItem() should be returning a 0 for any invalid index... + if (invslot == INVALID_INDEX) { // GetWornItem() should be returning a 0 for any invalid index... return 0; + } return GetWornItem(invslot); } -uint32 Corpse::GetEquipmentColor(uint8 material_slot) const { +uint32 Corpse::GetEquipmentColor(uint8 material_slot) const +{ const EQ::ItemData *item = nullptr; if (material_slot > EQ::textures::LastTexture) { @@ -1972,8 +2029,8 @@ uint32 Corpse::GetEquipmentColor(uint8 material_slot) const { } item = database.GetItem(GetEquippedItemFromTextureSlot(material_slot)); - if(item != 0) { - return (item_tint.Slot[material_slot].UseTint ? item_tint.Slot[material_slot].Color : item->Color); + if (item) { + return (m_item_tint.Slot[material_slot].UseTint ? m_item_tint.Slot[material_slot].Color : item->Color); } return 0; @@ -1981,94 +2038,116 @@ uint32 Corpse::GetEquipmentColor(uint8 material_slot) const { void Corpse::UpdateEquipmentLight() { - m_Light.Type[EQ::lightsource::LightEquipment] = 0; + m_Light.Type[EQ::lightsource::LightEquipment] = 0; m_Light.Level[EQ::lightsource::LightEquipment] = 0; - for (auto iter = itemlist.begin(); iter != itemlist.end(); ++iter) { - if ((*iter)->equip_slot < EQ::invslot::EQUIPMENT_BEGIN || (*iter)->equip_slot > EQ::invslot::EQUIPMENT_END) { continue; } - if ((*iter)->equip_slot == EQ::invslot::slotAmmo) { continue; } + for (auto &i: m_item_list) { + if (i->equip_slot < EQ::invslot::EQUIPMENT_BEGIN || i->equip_slot > EQ::invslot::EQUIPMENT_END) { + continue; + } + if (i->equip_slot == EQ::invslot::slotAmmo) { + continue; + } - auto item = database.GetItem((*iter)->item_id); - if (item == nullptr) { continue; } + auto item = database.GetItem(i->item_id); + if (item == nullptr) { + continue; + } - if (EQ::lightsource::IsLevelGreater(item->Light, m_Light.Type[EQ::lightsource::LightEquipment])) + if (EQ::lightsource::IsLevelGreater(item->Light, m_Light.Type[EQ::lightsource::LightEquipment])) { m_Light.Type[EQ::lightsource::LightEquipment] = item->Light; + } } uint8 general_light_type = 0; - for (auto iter = itemlist.begin(); iter != itemlist.end(); ++iter) { - if ((*iter)->equip_slot < EQ::invslot::GENERAL_BEGIN || (*iter)->equip_slot > EQ::invslot::GENERAL_END) { continue; } - auto item = database.GetItem((*iter)->item_id); + for (auto &i: m_item_list) { + if (i->equip_slot < EQ::invslot::GENERAL_BEGIN || i->equip_slot > EQ::invslot::GENERAL_END) { + continue; + } + + auto item = database.GetItem(i->item_id); if (item == nullptr) { continue; } if (!item->IsClassCommon()) { continue; } if (item->Light < 9 || item->Light > 13) { continue; } - if (EQ::lightsource::TypeToLevel(item->Light)) + if (EQ::lightsource::TypeToLevel(item->Light)) { general_light_type = item->Light; + } } - if (EQ::lightsource::IsLevelGreater(general_light_type, m_Light.Type[EQ::lightsource::LightEquipment])) + if (EQ::lightsource::IsLevelGreater(general_light_type, m_Light.Type[EQ::lightsource::LightEquipment])) { m_Light.Type[EQ::lightsource::LightEquipment] = general_light_type; + } m_Light.Level[EQ::lightsource::LightEquipment] = EQ::lightsource::TypeToLevel(m_Light.Type[EQ::lightsource::LightEquipment]); } -void Corpse::AddLooter(Mob* who) { - for (int i = 0; i < MAX_LOOTERS; i++) { - if (allowed_looters[i] == 0) { - allowed_looters[i] = who->CastToClient()->CharacterID(); +void Corpse::AddLooter(Mob *who) +{ + for (int &looter: m_allowed_looters) { + if (looter == 0) { + looter = who->CastToClient()->CharacterID(); break; } } } -void Corpse::LoadPlayerCorpseDecayTime(uint32 corpse_db_id){ - if(!corpse_db_id) +void Corpse::LoadPlayerCorpseDecayTime(uint32 corpse_db_id) +{ + if (!corpse_db_id) { return; + } uint32 active_corpse_decay_timer = database.GetCharacterCorpseDecayTimer(corpse_db_id); - if (active_corpse_decay_timer > 0 && RuleI(Character, CorpseDecayTimeMS) > (active_corpse_decay_timer * 1000)) { - corpse_decay_timer.SetTimer(RuleI(Character, CorpseDecayTimeMS) - (active_corpse_decay_timer * 1000)); + if (active_corpse_decay_timer > 0 && RuleI(Character, CorpseDecayTime) > (active_corpse_decay_timer * 1000)) { + m_corpse_decay_timer.SetTimer(RuleI(Character, CorpseDecayTime) - (active_corpse_decay_timer * 1000)); } else { - corpse_decay_timer.SetTimer(2000); + m_corpse_decay_timer.SetTimer(2000); } if (active_corpse_decay_timer > 0 && RuleI(Zone, GraveyardTimeMS) > (active_corpse_decay_timer * 1000)) { - corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS) - (active_corpse_decay_timer * 1000)); + m_corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS) - (active_corpse_decay_timer * 1000)); } else { - corpse_graveyard_timer.SetTimer(3000); + m_corpse_graveyard_timer.SetTimer(3000); } } void Corpse::SendWorldSpawnPlayerCorpseInZone(uint32_t zone_id) { - auto pack = std::make_unique(ServerOP_SpawnPlayerCorpse, sizeof(SpawnPlayerCorpse_Struct)); - SpawnPlayerCorpse_Struct* spc = reinterpret_cast(pack->pBuffer); - spc->player_corpse_id = corpse_db_id; - spc->zone_id = zone_id; + auto pack = std::make_unique( + ServerOP_SpawnPlayerCorpse, + sizeof(SpawnPlayerCorpse_Struct) + ); + auto *spc = reinterpret_cast(pack->pBuffer); + spc->player_corpse_id = m_corpse_db_id; + spc->zone_id = zone_id; worldserver.SendPacket(pack.get()); } bool Corpse::MovePlayerCorpseToGraveyard() { - if (IsPlayerCorpse() && zone && zone->HasGraveyard()) - { + if (IsPlayerCorpse() && zone && zone->HasGraveyard()) { Save(); glm::vec4 graveyard_point = zone->GetGraveyardPoint(); uint16_t instance_id = (zone->GetZoneID() == zone->graveyard_zoneid()) ? zone->GetInstanceID() : 0; - database.SendCharacterCorpseToGraveyard(corpse_db_id, zone->graveyard_zoneid(), instance_id, graveyard_point); + database.SendCharacterCorpseToGraveyard(m_corpse_db_id, zone->graveyard_zoneid(), instance_id, graveyard_point); SendWorldSpawnPlayerCorpseInZone(zone->graveyard_zoneid()); - corpse_db_id = 0; - player_corpse_depop = true; - corpse_graveyard_timer.Disable(); + m_corpse_db_id = 0; + m_player_corpse_depop = true; + m_corpse_graveyard_timer.Disable(); + + LogCorpses( + "Moved [{}] player corpse to the designated graveyard in zone [{}]", + GetName(), + ZoneName(zone->graveyard_zoneid()) + ); - LogDebug("Moved [{}] player corpse to the designated graveyard in zone [{}]", GetName(), ZoneName(zone->graveyard_zoneid())); return true; } @@ -2077,38 +2156,269 @@ bool Corpse::MovePlayerCorpseToGraveyard() bool Corpse::MovePlayerCorpseToNonInstance() { - if (IsPlayerCorpse() && zone && zone->GetInstanceID() != 0) - { + if (IsPlayerCorpse() && zone && zone->GetInstanceID() != 0) { Save(); - database.SendCharacterCorpseToNonInstance(corpse_db_id); + database.SendCharacterCorpseToNonInstance(m_corpse_db_id); SendWorldSpawnPlayerCorpseInZone(zone->GetZoneID()); - corpse_db_id = 0; - player_corpse_depop = true; - corpse_graveyard_timer.Disable(); + m_corpse_db_id = 0; + m_player_corpse_depop = true; + m_corpse_graveyard_timer.Disable(); + + LogCorpses( + "Moved [{}] player corpse to non-instance version of zone [{}]", + GetName(), + ZoneName(zone->GetZoneID()) + ); - LogDebug("Moved [{}] player corpse to non-instance version of zone [{}]", GetName(), ZoneName(zone->GetZoneID())); return true; } return false; } -std::vector Corpse::GetLootList() { +std::vector Corpse::GetLootList() +{ std::vector corpse_items; - for (auto current_item = itemlist.begin(); current_item != itemlist.end(); ++current_item) { - LootItem * loot_item = *current_item; - if (!loot_item) { + + for (auto i: m_item_list) { + if (!i) { LogError("Corpse::GetLootList() - ItemList error, null item"); continue; } - if (std::find(corpse_items.begin(), corpse_items.end(), loot_item->item_id) != corpse_items.end()) { + if (std::find(corpse_items.begin(), corpse_items.end(), i->item_id) != corpse_items.end()) { continue; } - corpse_items.push_back(loot_item->item_id); + corpse_items.push_back(i->item_id); } return corpse_items; } + +void Corpse::SetRezTimer(bool initial_timer) +{ + LogCorpsesDetail("Checking for rezzable corpse [{}]", GetName()); + + if (!m_is_rezzable) { + if (m_corpse_rezzable_timer.Enabled()) { + m_corpse_rezzable_timer.Disable(); + } + return; + } + + CheckIsOwnerOnline(); + + if (!m_is_owner_online && !initial_timer) { + if (m_corpse_rezzable_timer.Enabled()) { + m_corpse_rezzable_timer.Disable(); + } + return; + } + + if (m_corpse_rezzable_timer.Enabled() && !initial_timer) { + return; + } + + if (initial_timer) { + uint32 timer = RuleI(Character, CorpseResTime); + if (static_cast(m_killed_by_type) == KilledByTypes::Killed_DUEL) { + timer = RuleI(Character, DuelCorpseResTime); + } + m_remaining_rez_time = timer; + } + + if (m_remaining_rez_time < 1) { + // Corpse is no longer rezzable + CompleteResurrection(true); + return; + } + + m_corpse_rezzable_timer.SetTimer(m_remaining_rez_time); +} + +void Corpse::CheckIsOwnerOnline() +{ + LogCorpsesDetail("Checking if owner is online for corpse [{}]", GetOwnerName()); + + Client *c = entity_list.GetClientByCharID(GetCharID()); + if (!c) { + // Client is not in the corpse's zone, send a packet to world to have it check. + auto pack = new ServerPacket(ServerOP_IsOwnerOnline, sizeof(ServerIsOwnerOnline_Struct)); + auto *o = (ServerIsOwnerOnline_Struct *) pack->pBuffer; + strncpy(o->name, GetOwnerName(), sizeof(o->name)); + o->corpse_id = GetID(); + o->zone_id = zone->GetZoneID(); + o->online = 0; + o->account_id = m_account_id; + worldserver.SendPacket(pack); + safe_delete(pack); + LogCorpsesDetail("Sent IsOwnerOnline packet to world for [{}]", GetName()); + } + else { + SetOwnerOnline(true); + } +} + +void Corpse::CastRezz(uint16 spell_id, Mob *caster) +{ + LogSpells( + "spell_id [{}] IsRezzed() [{}], rez_experience [{}], rez_timer enabled [{}]", + spell_id, + IsRezzed(), + m_rezzed_experience, + m_corpse_rezzable_timer.Enabled() + ); + + // refresh rezzed state from database + const auto &e = CharacterCorpsesRepository::FindOne(database, m_corpse_db_id); + if (!e.id) { + caster->MessageString(Chat::White, REZZ_ALREADY_PENDING); + return; + } + + m_rezzed_experience = e.exp; + m_gm_rezzed_experience = e.gm_exp; + m_is_rezzable = e.rezzable; + IsRezzed(e.is_rezzed); + + // Rez timer has expired, only GMs can rez at this point. (uses rezzable) + if (!IsRezzable()) { + if (caster && caster->IsClient() && !caster->CastToClient()->GetGM()) { + caster->MessageString(Chat::White, REZZ_ALREADY_PENDING); + caster->MessageString(Chat::White, CORPSE_TOO_OLD); + return; + } + } + + // Corpse has been rezzed, but timer is still active. Players can corpse gate, GMs can rez for XP. (uses is_rezzed) + if (IsRezzed()) { + auto c = caster && caster->IsClient() ? caster->CastToClient() : nullptr; + if (c) { + m_rezzed_experience = 0; + if (c->GetGM()) { + m_rezzed_experience = m_gm_rezzed_experience; + m_gm_rezzed_experience = 0; + } + } + } + + auto outapp = new EQApplicationPacket(OP_RezzRequest, sizeof(Resurrect_Struct)); + auto *r = (Resurrect_Struct *) outapp->pBuffer; + + strn0cpy(r->your_name, corpse_name, 64); + strn0cpy(r->corpse_name, name, 64); + strn0cpy(r->rezzer_name, caster->GetName(), 64); + + r->zone_id = zone->GetZoneID(); + r->instance_id = zone->GetInstanceID(); + r->spellid = spell_id; + r->x = m_Position.x; + r->y = m_Position.y; + r->z = GetFixedZ(m_Position); + r->unknown000 = 0; + r->unknown020 = 0; + r->unknown088 = 0; + + // We send this to world, because it needs to go to the player who may not be in this zone. + worldserver.RezzPlayer(outapp, m_rezzed_experience, m_corpse_db_id, OP_RezzRequest); + safe_delete(outapp); +} + +Corpse *Corpse::LoadCharacterCorpse( + const CharacterCorpsesRepository::CharacterCorpses &cc, + const glm::vec4 &position +) +{ + if (!cc.id) { + LogCorpses("Unable to create a corpse entity for character corpse_id [{}]", cc.id); + return nullptr; + } + + const auto &items = CharacterCorpseItemsRepository::GetWhere( + database, + fmt::format( + "`corpse_id` = {}", + cc.id + ) + ); + + LootItems item_list; + for (auto &i: items) { + item_list.push_back( + new LootItem{ + .item_id = i.item_id, + .equip_slot = static_cast(i.equip_slot), + .charges = static_cast(i.charges), + .lootslot = 0, + .aug_1 = i.aug_1, + .aug_2 = i.aug_2, + .aug_3 = i.aug_3, + .aug_4 = i.aug_4, + .aug_5 = i.aug_5, + .aug_6 = static_cast(i.aug_6), + .attuned = static_cast(i.attuned) + } + ); + } + + auto c = new Corpse( + cc.id, + cc.charid, + cc.charname.c_str(), + &item_list, + cc.copper, + cc.silver, + cc.gold, + cc.platinum, + position, + cc.size, + cc.gender, + cc.race, + cc.class_, + cc.deity, + cc.level, + cc.texture, + cc.helm_texture, + cc.exp, + cc.gm_exp, + static_cast(cc.killed_by), + cc.rezzable, + cc.rez_time, + RuleB(Zone, EnableShadowrest) ? false : cc.was_at_graveyard + ); + + if (cc.is_locked) { + c->Lock(); + } + + // item tints and appearance + c->m_item_tint.Head.Color = cc.wc_1; + c->m_item_tint.Chest.Color = cc.wc_2; + c->m_item_tint.Arms.Color = cc.wc_3; + c->m_item_tint.Wrist.Color = cc.wc_4; + c->m_item_tint.Hands.Color = cc.wc_5; + c->m_item_tint.Legs.Color = cc.wc_6; + c->m_item_tint.Feet.Color = cc.wc_7; + c->m_item_tint.Primary.Color = cc.wc_8; + c->m_item_tint.Secondary.Color = cc.wc_9; + c->haircolor = cc.hair_color; + c->beardcolor = cc.beard_color; + c->eyecolor1 = cc.eye_color_1; + c->eyecolor2 = cc.eye_color_2; + c->hairstyle = cc.hair_style; + c->luclinface = cc.face; + c->beard = cc.beard; + c->drakkin_heritage = cc.drakkin_heritage; + c->drakkin_tattoo = cc.drakkin_tattoo; + c->drakkin_details = cc.drakkin_details; + c->m_become_npc = false; + c->m_consented_guild_id = cc.guild_consent_id; + + c->IsRezzed(cc.is_rezzed); + + c->UpdateEquipmentLight(); + + return c; +} diff --git a/zone/corpse.h b/zone/corpse.h index 4b7c7bb40..2879cd85d 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -1,27 +1,10 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - #ifndef CORPSE_H #define CORPSE_H #include "mob.h" #include "client.h" #include "../common/loot.h" +#include "../common/repositories/character_corpses_repository.h" class EQApplicationPacket; class Group; @@ -31,73 +14,148 @@ class Raid; struct ExtraAttackOptions; struct NPCType; -namespace EQ -{ +namespace EQ { class ItemInstance; } #define MAX_LOOTERS 72 class Corpse : public Mob { - public: +public: - static void SendEndLootErrorPacket(Client* client); - static void SendLootReqErrorPacket(Client* client, LootResponse response = LootResponse::NotAtThisTime); + static void SendEndLootErrorPacket(Client *client); + static void SendLootReqErrorPacket(Client *client, LootResponse response = LootResponse::NotAtThisTime); - Corpse(NPC* in_npc, LootItems* in_itemlist, uint32 in_npctypeid, const NPCType** in_npctypedata, uint32 in_decaytime = 600000); - Corpse(Client* client, int32 in_rezexp); - Corpse(uint32 in_corpseid, uint32 in_charid, const char* in_charname, LootItems* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, const glm::vec4& position, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture, uint32 in_rezexp, bool wasAtGraveyard = false); + Corpse( + NPC *npc, + LootItems *item_list, + uint32 npc_type_id, + const NPCType **npc_type_data, + uint32 decay_time = 600000 + ); + Corpse(Client *c, int32 rez_exp, KilledByTypes killed_by = KilledByTypes::Killed_NPC); + Corpse( + uint32 corpse_id, + uint32 character_id, + const char *character_name, + LootItems *item_list, + uint32 copper, + uint32 silver, + uint32 gold, + uint32 platinum, + const glm::vec4 &position, + float size, + uint8 gender, + uint16 race, + uint8 class_, + uint8 deity, + uint8 level, + uint8 texture, + uint8 helm_texture, + uint32 rez_exp, + uint32 gm_rez_exp, + KilledByTypes killed_by, + bool is_rezzable, + uint32 rez_remaining_time, + bool was_at_graveyard = false + ); ~Corpse(); - static Corpse* LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, const glm::vec4& position, std::string time_of_death, bool rezzed, bool was_at_graveyard, uint32 guild_consent_id); /* Corpse: General */ - virtual bool Death(Mob* killerMob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill) { return true; } - virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) { return; } - bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = true, - bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) override { + virtual bool Death( + Mob *killer_mob, + int64 damage, + uint16 spell_id, + EQ::skills::SkillType attack_skill, + KilledByTypes killed_by = KilledByTypes::Killed_NPC + ) { return true; } + + virtual void Damage( + Mob *from, + int64 damage, + uint16 spell_id, + EQ::skills::SkillType attack_skill, + bool avoidable = true, + int8 buffslot = -1, + bool iBuffTic = false, + eSpecialAttacks special = eSpecialAttacks::None + ) { } + + bool Attack( + Mob *other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = true, + bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr + ) override + { return false; } - virtual bool HasRaid() { return false; } - virtual bool HasGroup() { return false; } - virtual Raid* GetRaid() { return 0; } - virtual Group* GetGroup() { return 0; } - inline uint32 GetCorpseDBID() { return corpse_db_id; } - inline char* GetOwnerName() { return corpse_name; } - bool IsEmpty() const; - bool IsCorpse() const { return true; } - bool IsPlayerCorpse() const { return is_player_corpse; } - bool IsNPCCorpse() const { return !is_player_corpse; } - bool IsBecomeNPCCorpse() const { return become_npc; } - virtual void DepopNPCCorpse(); - virtual void DepopPlayerCorpse(); - bool Process(); - bool Save(); - uint32 GetCharID() { return char_id; } - uint32 SetCharID(uint32 iCharID) { if (IsPlayerCorpse()) { return (char_id = iCharID); } return 0xFFFFFFFF; }; - uint32 GetDecayTime() { if (!corpse_decay_timer.Enabled()) return 0xFFFFFFFF; else return corpse_decay_timer.GetRemainingTime(); } - uint32 GetRezTime() { if (!corpse_rez_timer.Enabled()) return 0; else return corpse_rez_timer.GetRemainingTime(); } - void ResetDecayTimer(); - void SetDecayTimer(uint32 decay_time); - void SetConsentGroupID(uint32 group_id) { if (IsPlayerCorpse()) { consented_group_id = group_id; } } - void SetConsentRaidID(uint32 raid_id) { if (IsPlayerCorpse()) { consented_raid_id = raid_id; } } - void SetConsentGuildID(uint32 guild_id) { if (IsPlayerCorpse()) { consented_guild_id = guild_id; } } - void AddConsentName(std::string consent_player_name); - void RemoveConsentName(std::string consent_player_name); - void SendWorldSpawnPlayerCorpseInZone(uint32_t zone_id); - bool MovePlayerCorpseToGraveyard(); - bool MovePlayerCorpseToNonInstance(); - void Delete(); - void Bury(); - void CalcCorpseName(); - void LoadPlayerCorpseDecayTime(uint32 dbid); + virtual bool HasRaid() { return false; } + virtual bool HasGroup() { return false; } + virtual Raid *GetRaid() { return nullptr; } + virtual Group *GetGroup() { return nullptr; } + inline uint32 GetCorpseDBID() { return m_corpse_db_id; } + inline char *GetOwnerName() { return corpse_name; } + bool IsEmpty() const; + bool IsCorpse() const { return true; } + bool IsPlayerCorpse() const { return m_is_player_corpse; } + bool IsNPCCorpse() const { return !m_is_player_corpse; } + bool IsBecomeNPCCorpse() const { return m_become_npc; } + virtual void DepopNPCCorpse(); + virtual void DepopPlayerCorpse(); + bool Process(); + bool Save(); + + uint32 GetCharID() { return m_character_id; } + + uint32 SetCharID(uint32 iCharID) + { + if (IsPlayerCorpse()) { + return (m_character_id = iCharID); + } + return 0xFFFFFFFF; + }; + + uint32 GetDecayTime() + { + if (!m_corpse_decay_timer.Enabled()) { + return 0xFFFFFFFF; + } + else { + return m_corpse_decay_timer.GetRemainingTime(); + } + } + uint32 GetRezTime() + { + if (!m_corpse_rezzable_timer.Enabled()) { + return 0; + } + else { + return m_corpse_rezzable_timer.GetRemainingTime(); + } + } + + void ResetDecayTimer(); + void SetDecayTimer(uint32 decay_time); + void SetConsentGroupID(uint32 group_id) { if (IsPlayerCorpse()) { m_consented_group_id = group_id; }} + void SetConsentRaidID(uint32 raid_id) { if (IsPlayerCorpse()) { m_consented_raid_id = raid_id; }} + void SetConsentGuildID(uint32 guild_id) { if (IsPlayerCorpse()) { m_consented_guild_id = guild_id; }} + void AddConsentName(const std::string& consent_player_name); + void RemoveConsentName(const std::string& consent_player_name); + void SendWorldSpawnPlayerCorpseInZone(uint32_t zone_id); + bool MovePlayerCorpseToGraveyard(); + bool MovePlayerCorpseToNonInstance(); + + void Delete(); + void Bury(); + void CalcCorpseName(); + void LoadPlayerCorpseDecayTime(uint32 dbid); /* Corpse: Items */ - uint32 GetWornItem(int16 equipSlot) const; + uint32 GetWornItem(int16 equip_slot) const; LootItem *GetItem(uint16 lootslot, LootItem **bag_item_data = 0); - void SetPlayerKillItemID(int32 pk_item_id) { player_kill_item = pk_item_id; } - int32 GetPlayerKillItem() { return player_kill_item; } + void SetPlayerKillItemID(int32 pk_item_id) { m_player_kill_item = pk_item_id; } + int32 GetPlayerKillItem() { return m_player_kill_item; } void RemoveItem(uint16 lootslot); void RemoveItem(LootItem *item_data); void RemoveItemByID(uint32 item_id, int quantity = 1); @@ -121,84 +179,110 @@ class Corpse : public Mob { /* Corpse: Coin */ void SetCash(uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_platinum); void RemoveCash(); - uint32 GetCopper() { return copper; } - uint32 GetSilver() { return silver; } - uint32 GetGold() { return gold; } - uint32 GetPlatinum() { return platinum; } + uint32 GetCopper() { return m_copper; } + uint32 GetSilver() { return m_silver; } + uint32 GetGold() { return m_gold; } + uint32 GetPlatinum() { return m_platinum; } /* Corpse: Resurrection */ - bool IsRezzed() { return rez; } - void IsRezzed(bool in_rez) { rez = in_rez; } - void CastRezz(uint16 spellid, Mob* Caster); - void CompleteResurrection(); + bool IsRezzed() { return m_rez; } + void IsRezzed(bool in_rez) { m_rez = in_rez; } + void CastRezz(uint16 spell_id, Mob *caster); + void CompleteResurrection(bool timer_expired = false); + bool IsRezzable() { return m_is_rezzable; } + void SetRezTimer(bool initial_timer = false); /* Corpse: Loot */ - void QueryLoot(Client* to); - bool HasItem(uint32 item_id); - uint16 CountItem(uint32 item_id); - uint32 GetItemIDBySlot(uint16 loot_slot); - uint16 GetFirstLootSlotByItemID(uint32 item_id); + void QueryLoot(Client *to); + bool HasItem(uint32 item_id); + uint16 CountItem(uint32 item_id); + uint32 GetItemIDBySlot(uint16 loot_slot); + uint16 GetFirstLootSlotByItemID(uint32 item_id); std::vector GetLootList(); - void LootCorpseItem(Client* client, const EQApplicationPacket* app); - void EndLoot(Client* client, const EQApplicationPacket* app); - void MakeLootRequestPackets(Client* client, const EQApplicationPacket* app); - void AllowPlayerLoot(Mob *them, uint8 slot); - void AddLooter(Mob *who); - uint32 CountItems(); - bool CanPlayerLoot(int charid); + void LootCorpseItem(Client *c, const EQApplicationPacket *app); + void EndLoot(Client *c, const EQApplicationPacket *app); + void MakeLootRequestPackets(Client *c, const EQApplicationPacket *app); + void AllowPlayerLoot(Mob *them, uint8 slot); + void AddLooter(Mob *who); + uint32 CountItems(); + bool CanPlayerLoot(int character_id); - inline void Lock() { is_locked = true; } - inline void UnLock() { is_locked = false; } - inline bool IsLocked() { return is_locked; } - inline void ResetLooter() { being_looted_by = 0xFFFFFFFF; loot_request_type = LootRequestType::Forbidden; } - inline bool IsBeingLooted() { return (being_looted_by != 0xFFFFFFFF); } - inline bool IsBeingLootedBy(Client *c) { return being_looted_by == c->GetID(); } + inline void Lock() { m_is_locked = true; } + inline void UnLock() { m_is_locked = false; } + inline bool IsLocked() { return m_is_locked; } + inline void ResetLooter() + { + m_being_looted_by_entity_id = 0xFFFFFFFF; + m_loot_request_type = LootRequestType::Forbidden; + } + inline bool IsBeingLooted() { return (m_being_looted_by_entity_id != 0xFFFFFFFF); } + inline bool IsBeingLootedBy(Client *c) { return m_being_looted_by_entity_id == c->GetID(); } /* Mob */ - void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); - bool Summon(Client* client, bool spell, bool CheckDistance); + void FillSpawnStruct(NewSpawn_Struct *ns, Mob *ForWho); + bool Summon(Client *c, bool spell, bool CheckDistance); void Spawn(); - char corpse_name[64]; - uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const; - uint32 GetEquipmentColor(uint8 material_slot) const; - inline int GetRezExp() { return rez_experience; } + char corpse_name[64]; + uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const; + uint32 GetEquipmentColor(uint8 material_slot) const; + inline int64 GetRezExp() { return m_rezzed_experience; } + inline int64 GetGMRezExp() { return m_gm_rezzed_experience; } + uint8 GetKilledBy() { return m_killed_by_type; } + uint32 GetRemainingRezTime() { return m_remaining_rez_time; } virtual void UpdateEquipmentLight(); + void CheckIsOwnerOnline(); + void SetOwnerOnline(bool value) { m_is_owner_online = value; } + bool GetOwnerOnline() { return m_is_owner_online; } + + static Corpse *LoadCharacterCorpse( + const CharacterCorpsesRepository::CharacterCorpses &cc, + const glm::vec4 &position + ); + protected: void MoveItemToCorpse(Client *client, EQ::ItemInstance *inst, int16 equipSlot, std::list &removedList); private: - bool is_player_corpse; /* Determines if Player Corpse or not */ - bool is_corpse_changed; /* Determines if corpse has changed or not */ - bool is_locked; /* Determines if corpse is locked */ - int32 player_kill_item; /* Determines if Player Kill Item */ - uint32 corpse_db_id; /* Corpse Database ID (Player Corpse) */ - uint32 char_id; /* Character ID */ - uint32 consented_group_id = 0; - uint32 consented_raid_id = 0; - uint32 consented_guild_id = 0; - LootItems itemlist; /* Internal Item list used for corpses */ - uint32 copper; - uint32 silver; - uint32 gold; - uint32 platinum; - bool player_corpse_depop; /* Sets up Corpse::Process to depop the player corpse */ - uint32 being_looted_by; /* Determines what the corpse is being looted by internally for logic */ - uint32 rez_experience; /* Amount of experience that the corpse would rez for */ - bool rez; - bool become_npc; - int allowed_looters[MAX_LOOTERS]; /* People allowed to loot the corpse, character id */ - Timer corpse_decay_timer; /* The amount of time in millseconds in which a corpse will take to decay (Depop/Poof) */ - Timer corpse_rez_timer; /* The amount of time in millseconds in which a corpse can be rezzed */ - Timer corpse_delay_timer; - Timer corpse_graveyard_timer; - Timer loot_cooldown_timer; /* Delay between loot actions on the corpse entity */ - EQ::TintProfile item_tint; - std::vector consented_player_names; - - LootRequestType loot_request_type; + bool m_is_player_corpse; + bool m_is_corpse_changed; + bool m_is_locked; + int32 m_player_kill_item; + uint32 m_corpse_db_id; + uint32 m_character_id; + uint32 m_consented_group_id = 0; + uint32 m_consented_raid_id = 0; + uint32 m_consented_guild_id = 0; + LootItems m_item_list; + uint32 m_copper; + uint32 m_silver; + uint32 m_gold; + uint32 m_platinum; + bool m_player_corpse_depop; + uint32 m_being_looted_by_entity_id; + uint64 m_rezzed_experience; + uint64 m_gm_rezzed_experience; + uint64 m_gm_exp; + bool m_rez; + bool m_become_npc; + int m_allowed_looters[MAX_LOOTERS]; + Timer m_corpse_decay_timer; + Timer m_corpse_rezzable_timer; + Timer m_corpse_delay_timer; + Timer m_corpse_graveyard_timer; + Timer m_loot_cooldown_timer; + Timer m_check_owner_online_timer; + Timer m_check_rezzable_timer; + uint8 m_killed_by_type; + bool m_is_rezzable; + EQ::TintProfile m_item_tint; + uint32 m_remaining_rez_time; + bool m_is_owner_online; + std::vector m_consented_player_names; + LootRequestType m_loot_request_type; + uint32 m_account_id; }; #endif diff --git a/zone/encounter.h b/zone/encounter.h index 8e79f356d..4a3e89d72 100644 --- a/zone/encounter.h +++ b/zone/encounter.h @@ -34,7 +34,7 @@ public: ~Encounter(); //abstract virtual function implementations required by base abstract class - virtual bool Death(Mob* killerMob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill) { return true; } + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC) { return true; } virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) { return; } bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) override { diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 4962b8a5c..5b84eb2a8 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -25,6 +25,7 @@ #include "zonedb.h" #include "../common/events/player_event_logs.h" #include "bot.h" +#include "../common/repositories/character_corpse_items_repository.h" extern WorldServer worldserver; @@ -4792,26 +4793,29 @@ void Client::SummonItemIntoInventory( bool Client::HasItemOnCorpse(uint32 item_id) { - const uint32 corpse_count = GetCorpseCount(); - if (!corpse_count) { - return EQ::invslot::SLOT_INVALID; + auto corpses = CharacterCorpsesRepository::GetWhere(database, fmt::format("charid = {}", CharacterID())); + if (corpses.empty()) { + return false; } - for (int i = 0; i < corpse_count; i++) { - const uint32 corpse_id = GetCorpseID(i); + std::vector corpse_ids; + corpse_ids.reserve(corpses.size()); - for (int16 slot_id = EQ::invslot::POSSESSIONS_BEGIN; slot_id < EQ::invslot::POSSESSIONS_END; slot_id++) { - const uint32 current_item_id = GetCorpseItemAt(corpse_id, slot_id); - if (current_item_id && current_item_id == item_id) { - return true; - } - } + for (auto &corpse : corpses) { + corpse_ids.push_back(corpse.id); + } - for (int16 slot_id = EQ::invbag::GENERAL_BAGS_BEGIN; slot_id < EQ::invbag::GENERAL_BAGS_END; slot_id++) { - const uint32 current_item_id = GetCorpseItemAt(corpse_id, slot_id); - if (current_item_id && current_item_id == item_id) { - return true; - } + auto items = CharacterCorpseItemsRepository::GetWhere( + database, + fmt::format( + "corpse_id IN ({})", + Strings::Join(corpse_ids, ",") + ) + ); + + for (auto &item : items) { + if (item.item_id == item_id) { + return true; } } diff --git a/zone/merc.cpp b/zone/merc.cpp index 6376a37f1..61f24649e 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -4084,10 +4084,9 @@ Mob* Merc::GetOwnerOrSelf() { return Result; } -bool Merc::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill) +bool Merc::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, uint8 killed_by) { - if(!NPC::Death(killerMob, damage, spell, attack_skill)) - { + if (!NPC::Death(killer_mob, damage, spell, attack_skill)) { return false; } @@ -4100,8 +4099,7 @@ bool Merc::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::SkillTy // entity_list.GetCorpseByID(GetID())->Depop(); // If client is in zone, suspend merc, else depop it. - if (!Suspend()) - { + if (!Suspend()) { Depop(); } diff --git a/zone/merc.h b/zone/merc.h index 07b0fd5d5..6aa1c26c5 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -52,7 +52,7 @@ public: virtual ~Merc(); //abstract virtual function implementations requird by base abstract class - virtual bool Death(Mob* killerMob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill); + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, uint8 killed_by = 0); virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None); virtual bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr); diff --git a/zone/mob.h b/zone/mob.h index f4c09fc2a..33c5df541 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -541,7 +541,7 @@ public: bool CanClassEquipItem(uint32 item_id); bool CanRaceEquipItem(uint32 item_id); bool AffectedBySpellExcludingSlot(int slot, int effect); - virtual bool Death(Mob* killerMob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill) = 0; + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC) = 0; virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) = 0; void SetHP(int64 hp); diff --git a/zone/npc.h b/zone/npc.h index 9e936e1f2..c9f074a1b 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -130,7 +130,7 @@ public: static NPC * SpawnZonePointNodeNPC(std::string name, const glm::vec4 &position); //abstract virtual function implementations requird by base abstract class - virtual bool Death(Mob* killerMob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill); + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC); virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None); bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) override; diff --git a/zone/spells.cpp b/zone/spells.cpp index 3a9fe1b24..9a1b401d9 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -77,6 +77,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) #include "../common/data_verification.h" #include "../common/misc_functions.h" #include "../common/events/player_event_logs.h" +#include "../common/repositories/character_corpses_repository.h" #include "data_bucket.h" #include "quest_parser_collection.h" @@ -227,7 +228,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, ( !RuleB(Spells, ManaTapsRequireNPCMana) || ( - RuleB(Spells, ManaTapsRequireNPCMana) && + RuleB(Spells, ManaTapsRequireNPCMana) && GetTarget()->GetMana() == 0 ) ) @@ -3064,7 +3065,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, if (IsResurrectionEffects(spellid1)) { return 0; } - + if (spellbonuses.CompleteHealBuffBlocker && IsEffectInSpell(spellid2, SE_CompleteHeal)) { Message(0, "You must wait before you can be affected by this spell again."); return -1; @@ -4128,7 +4129,7 @@ bool Mob::SpellOnTarget( if (IsClient() && (casting_spell_aa_id == aaDireCharm || casting_spell_aa_id == aaDireCharm2 || casting_spell_aa_id == aaDireCharm3)) { StopCasting(); } - + //the above call does the message to the client if needed LogSpells("Spell [{}] can't take hold due to immunity [{}] -> [{}]", spell_id, GetName(), spelltar->GetName()); safe_delete(action_packet); @@ -4544,44 +4545,6 @@ bool Mob::SpellOnTarget( return true; } -void Corpse::CastRezz(uint16 spellid, Mob* Caster) -{ - LogSpells("Corpse::CastRezz spellid [{}], Rezzed() is [{}], rezzexp is [{}]", spellid,IsRezzed(),rez_experience); - - if(IsRezzed()){ - if(Caster && Caster->IsClient()) - Caster->Message(Chat::Red,"This character has already been resurrected."); - - return; - } - /* - if(!can_rez) { - if(Caster && Caster->IsClient()) - Caster->MessageString(Chat::White, CORPSE_TOO_OLD); - return; - } - */ - - auto outapp = new EQApplicationPacket(OP_RezzRequest, sizeof(Resurrect_Struct)); - Resurrect_Struct* rezz = (Resurrect_Struct*) outapp->pBuffer; - // Why are we truncating these names to 30 characters ? - memcpy(rezz->your_name,corpse_name,30); - memcpy(rezz->corpse_name,name,30); - memcpy(rezz->rezzer_name,Caster->GetName(),30); - rezz->zone_id = zone->GetZoneID(); - rezz->instance_id = zone->GetInstanceID(); - rezz->spellid = spellid; - rezz->x = m_Position.x; - rezz->y = m_Position.y; - rezz->z = GetFixedZ(m_Position); - rezz->unknown000 = 0x00000000; - rezz->unknown020 = 0x00000000; - rezz->unknown088 = 0x00000000; - // We send this to world, because it needs to go to the player who may not be in this zone. - worldserver.RezzPlayer(outapp, rez_experience, corpse_db_id, OP_RezzRequest); - safe_delete(outapp); -} - std::vector Mob::GetBuffSpellIDs() { std::vector l; @@ -5012,7 +4975,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) caster->MessageString(Chat::SpellFailure, TARGET_RESISTED, spells[spell_id].name); return true; } - + caster->MessageString(Chat::Red, CANNOT_CHARM_YET); // need to verify message type, not in MQ2Cast for easy look up AddToHateList(caster, 1,0,true,false,false,spell_id); return true; @@ -5191,7 +5154,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if (IsHarmTouchSpell(spell_id) && caster->IsClient() && caster->CastToClient()->FindBuff(DISC_UNHOLY_AURA)) { resist_type = RESIST_DISEASE; } - + //Get the resist chance for the target if (resist_type == RESIST_NONE || spells[spell_id].no_resist) { LogSpells("Spell was unresistable"); diff --git a/zone/string_ids.h b/zone/string_ids.h index 025c6c3c7..c1b6af479 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -171,7 +171,11 @@ #define PET_REPORT_HP 488 //I have %1 percent of my hit points left. #define PET_NO_TAUNT 489 //No longer taunting attackers, Master. #define PET_DO_TAUNT 490 //Taunting attackers as normal, Master. -#define CORPSE_DECAY1 495 //This corpse will decay in %1 minute(s) %2 seconds. +#define CORPSE_REZ_TIME_HOUR 491 //This corpse's resurrection time will expire in %1 hour(s) %2 minute(s) %3 seconds. +#define CORPSE_REZ_TIME_MINUTE 492 //This corpse's resurrection time will expire in %1 minute(s) %2 seconds. +#define CORPSE_DECAY_TIME_DAY 493 //This corpse will decay in %1 day(s) %2 hour(s) %3 minute(s) %4 seconds. +#define CORPSE_DECAY_TIME_HOUR 494 //This corpse will decay in %1 hour(s) %2 minute(s) %3 seconds. +#define CORPSE_DECAY_TIME_MINUTE 495 //This corpse will decay in %1 minute(s) %2 seconds. #define DISC_LEVEL_ERROR 503 //You must be a level %1 ... to use this discipline. #define DISCIPLINE_CANUSEIN 504 //You can use a new discipline in %1 minutes %2 seconds. #define PVP_ON 552 //You are now player kill and follow the ways of Discord. diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 2ba4c7e9f..c211908be 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1269,6 +1269,22 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } break; } + case ServerOP_IsOwnerOnline: { + auto o = (ServerIsOwnerOnline_Struct*)pack->pBuffer; + if (zone) { + if (o->zone_id != zone->GetZoneID()) { + break; + } + + Corpse* c = entity_list.GetCorpseByID(o->corpse_id); + if (c && o->online) { + c->SetOwnerOnline(true); + } else if (c) { + c->SetOwnerOnline(false); + } + } + break; + } case ServerOP_OOZGroupMessage: { ServerGroupChannelMessage_Struct* gcm = (ServerGroupChannelMessage_Struct*)pack->pBuffer; if (zone) { @@ -1587,12 +1603,14 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } case ServerOP_SpawnPlayerCorpse: { - SpawnPlayerCorpse_Struct* s = (SpawnPlayerCorpse_Struct*)pack->pBuffer; - Corpse* NewCorpse = database.LoadCharacterCorpse(s->player_corpse_id); - if (NewCorpse) - NewCorpse->Spawn(); - else + auto *s = (SpawnPlayerCorpse_Struct *) pack->pBuffer; + Corpse *c = database.LoadCharacterCorpse(s->player_corpse_id); + if (c) { + c->Spawn(); + } + else { LogError("Unable to load player corpse id [{}] for zone [{}]", s->player_corpse_id, zone->GetShortName()); + } break; } diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 203a53f39..b814d9788 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3774,7 +3774,7 @@ uint32 ZoneDatabase::SaveCharacterCorpse( const std::string& name, uint32 zone_id, uint16 instance_id, - const CharacterCorpseEntry& corpse, + const CharacterCorpseEntry& c, const glm::vec4& position, uint32 guild_consent_id ) @@ -3791,66 +3791,69 @@ uint32 ZoneDatabase::SaveCharacterCorpse( e.heading = position.w; e.guild_consent_id = guild_consent_id; e.time_of_death = std::time(nullptr); - e.is_locked = corpse.locked; - e.exp = corpse.exp; - e.size = corpse.size; - e.level = corpse.level; - e.race = corpse.race; - e.gender = corpse.gender; - e.class_ = corpse.class_; - e.deity = corpse.deity; - e.texture = corpse.texture; - e.helm_texture = corpse.helmtexture; - e.copper = corpse.copper; - e.silver = corpse.silver; - e.gold = corpse.gold; - e.platinum = corpse.plat; - e.hair_color = corpse.haircolor; - e.beard_color = corpse.beardcolor; - e.eye_color_1 = corpse.eyecolor1; - e.eye_color_2 = corpse.eyecolor2; - e.hair_style = corpse.hairstyle; - e.face = corpse.face; - e.beard = corpse.beard; - e.drakkin_heritage = corpse.drakkin_heritage; - e.drakkin_tattoo = corpse.drakkin_tattoo; - e.drakkin_details = corpse.drakkin_details; - e.wc_1 = corpse.item_tint.Head.Color; - e.wc_2 = corpse.item_tint.Chest.Color; - e.wc_3 = corpse.item_tint.Arms.Color; - e.wc_4 = corpse.item_tint.Wrist.Color; - e.wc_5 = corpse.item_tint.Hands.Color; - e.wc_6 = corpse.item_tint.Legs.Color; - e.wc_7 = corpse.item_tint.Feet.Color; - e.wc_8 = corpse.item_tint.Primary.Color; - e.wc_9 = corpse.item_tint.Secondary.Color; + e.is_locked = c.locked; + e.exp = c.exp; + e.size = c.size; + e.level = c.level; + e.race = c.race; + e.gender = c.gender; + e.class_ = c.class_; + e.deity = c.deity; + e.texture = c.texture; + e.helm_texture = c.helmtexture; + e.copper = c.copper; + e.silver = c.silver; + e.gold = c.gold; + e.platinum = c.plat; + e.hair_color = c.haircolor; + e.beard_color = c.beardcolor; + e.eye_color_1 = c.eyecolor1; + e.eye_color_2 = c.eyecolor2; + e.hair_style = c.hairstyle; + e.face = c.face; + e.beard = c.beard; + e.drakkin_heritage = c.drakkin_heritage; + e.drakkin_tattoo = c.drakkin_tattoo; + e.drakkin_details = c.drakkin_details; + e.wc_1 = c.item_tint.Head.Color; + e.wc_2 = c.item_tint.Chest.Color; + e.wc_3 = c.item_tint.Arms.Color; + e.wc_4 = c.item_tint.Wrist.Color; + e.wc_5 = c.item_tint.Hands.Color; + e.wc_6 = c.item_tint.Legs.Color; + e.wc_7 = c.item_tint.Feet.Color; + e.wc_8 = c.item_tint.Primary.Color; + e.wc_9 = c.item_tint.Secondary.Color; + e.killed_by = c.killed_by; + e.rezzable = c.rezzable; + e.rez_time = c.rez_time; e = CharacterCorpsesRepository::InsertOne(*this, e); std::vector v; - v.reserve(corpse.items.size()); + v.reserve(c.items.size()); - auto i = CharacterCorpseItemsRepository::NewEntity(); + auto ci = CharacterCorpseItemsRepository::NewEntity(); - for (const auto& item : corpse.items) { - i.corpse_id = e.id; - i.equip_slot = item.equip_slot; - i.item_id = item.item_id; - i.charges = item.charges; - i.aug_1 = item.aug_1; - i.aug_2 = item.aug_2; - i.aug_3 = item.aug_3; - i.aug_4 = item.aug_4; - i.aug_5 = item.aug_5; - i.aug_6 = item.aug_6; - i.attuned = item.attuned; - i.custom_data = item.custom_data; - i.ornamenticon = item.ornamenticon; - i.ornamentidfile = item.ornamentidfile; - i.ornament_hero_model = item.ornament_hero_model; + for (const auto& i : c.items) { + ci.corpse_id = e.id; + ci.equip_slot = i.equip_slot; + ci.item_id = i.item_id; + ci.charges = i.charges; + ci.aug_1 = i.aug_1; + ci.aug_2 = i.aug_2; + ci.aug_3 = i.aug_3; + ci.aug_4 = i.aug_4; + ci.aug_5 = i.aug_5; + ci.aug_6 = i.aug_6; + ci.attuned = i.attuned; + ci.custom_data = i.custom_data; + ci.ornamenticon = i.ornamenticon; + ci.ornamentidfile = i.ornamentidfile; + ci.ornament_hero_model = i.ornament_hero_model; - v.emplace_back(i); + v.emplace_back(ci); } if (!v.empty()) { @@ -3898,6 +3901,8 @@ uint32 ZoneDatabase::GetCharacterCorpseID(uint32 character_id, uint8 corpse_limi uint32 ZoneDatabase::GetCharacterCorpseItemAt(uint32 corpse_id, uint16 slot_id) { + LogCorpsesDetail("corpse_id [{}] slot_id [{}]", corpse_id, slot_id); + Corpse* c = LoadCharacterCorpse(corpse_id); uint32 item_id = 0; @@ -3909,76 +3914,6 @@ uint32 ZoneDatabase::GetCharacterCorpseItemAt(uint32 corpse_id, uint16 slot_id) return item_id; } -bool ZoneDatabase::LoadCharacterCorpseData(uint32 corpse_id, CharacterCorpseEntry& corpse) -{ - const auto& e = CharacterCorpsesRepository::FindOne(*this, corpse_id); - - corpse.locked = e.is_locked; - corpse.exp = e.exp; - corpse.size = e.size; - corpse.level = e.level; - corpse.race = e.race; - corpse.gender = e.gender; - corpse.class_ = e.class_; - corpse.deity = e.deity; - corpse.texture = e.texture; - corpse.helmtexture = e.helm_texture; - corpse.copper = e.copper; - corpse.silver = e.silver; - corpse.gold = e.gold; - corpse.plat = e.platinum; - corpse.haircolor = e.hair_color; - corpse.beardcolor = e.beard_color; - corpse.eyecolor1 = e.eye_color_1; - corpse.eyecolor2 = e.eye_color_2; - corpse.hairstyle = e.hair_style; - corpse.face = e.face; - corpse.beard = e.beard; - corpse.drakkin_heritage = e.drakkin_heritage; - corpse.drakkin_tattoo = e.drakkin_tattoo; - corpse.drakkin_details = e.drakkin_details; - corpse.item_tint.Head.Color = e.wc_1; - corpse.item_tint.Chest.Color = e.wc_2; - corpse.item_tint.Arms.Color = e.wc_3; - corpse.item_tint.Wrist.Color = e.wc_4; - corpse.item_tint.Hands.Color = e.wc_5; - corpse.item_tint.Legs.Color = e.wc_6; - corpse.item_tint.Feet.Color = e.wc_7; - corpse.item_tint.Primary.Color = e.wc_8; - corpse.item_tint.Secondary.Color = e.wc_9; - - const auto& l = CharacterCorpseItemsRepository::GetWhere( - *this, - fmt::format( - "`corpse_id` = {}", - corpse_id - ) - ); - - for (const auto& e : l) { - CharacterCorpseItemEntry item{ - .item_id = e.item_id, - .equip_slot = static_cast(e.equip_slot), - .charges = static_cast(e.charges), - .aug_1 = e.aug_1, - .aug_2 = e.aug_2, - .aug_3 = e.aug_3, - .aug_4 = e.aug_4, - .aug_5 = e.aug_5, - .aug_6 = static_cast(e.aug_6), - .attuned = e.attuned == 1, - .custom_data = e.custom_data, - .ornamenticon = e.ornamenticon, - .ornamentidfile = e.ornamentidfile, - .ornament_hero_model = e.ornament_hero_model - }; - - corpse.items.emplace_back(std::move(item)); - } - - return true; -} - Corpse* ZoneDatabase::SummonBuriedCharacterCorpses( uint32 character_id, uint32 zone_id, @@ -3997,23 +3932,13 @@ Corpse* ZoneDatabase::SummonBuriedCharacterCorpses( ); for (const auto& e : l) { - c = Corpse::LoadCharacterCorpseEntity( - e.id, - e.charid, - e.charname, - position, - std::to_string(e.time_of_death), - e.is_rezzed == 1, - RuleB(Zone, EnableShadowrest) ? 0 : e.was_at_graveyard, - e.guild_consent_id - ); - + c = Corpse::LoadCharacterCorpse(e, position); if (!c) { continue; } entity_list.AddCorpse(c); - c->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); + c->SetDecayTimer(RuleI(Character, CorpseDecayTime)); c->Spawn(); if (!UnburyCharacterCorpse(c->GetCorpseDBID(), zone_id, instance_id, position)) { @@ -4052,20 +3977,10 @@ bool ZoneDatabase::SummonAllCharacterCorpses( e.is_buried = 0; e.was_at_graveyard = 0; - c = Corpse::LoadCharacterCorpseEntity( - e.id, - e.charid, - e.charname, - position, - std::to_string(e.time_of_death), - e.is_rezzed == 1, - RuleB(Zone, EnableShadowrest) ? 0 : e.was_at_graveyard, - e.guild_consent_id - ); - + c = Corpse::LoadCharacterCorpse(e, position); if (c) { entity_list.AddCorpse(c); - c->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); + c->SetDecayTimer(RuleI(Character, CorpseDecayTime)); c->Spawn(); ++corpse_count; } else { @@ -4108,26 +4023,16 @@ bool ZoneDatabase::UnburyCharacterCorpse(uint32 corpse_id, uint32 zone_id, uint1 Corpse* ZoneDatabase::LoadCharacterCorpse(uint32 corpse_id) { - const auto& e = CharacterCorpsesRepository::FindOne(*this, corpse_id); - - Corpse* c = 0; - - if (!e.id) { - return c; + if (!corpse_id) { + return nullptr; } - glm::vec4 position = glm::vec4(e.x, e.y, e.z, e.heading); + const auto &e = CharacterCorpsesRepository::FindOne(*this, corpse_id); + if (!e.id) { + return nullptr; + } - c = Corpse::LoadCharacterCorpseEntity( - e.id, - e.charid, - e.charname, - position, - std::to_string(e.time_of_death), - e.is_rezzed == 1, - RuleB(Zone, EnableShadowrest) ? 0 : e.was_at_graveyard, - e.guild_consent_id - ); + auto c = Corpse::LoadCharacterCorpse(e, glm::vec4(e.x, e.y, e.z, e.heading)); entity_list.AddCorpse(c); @@ -4146,21 +4051,9 @@ bool ZoneDatabase::LoadCharacterCorpses(uint32 zone_id, uint16 instance_id) ) ); - for (const auto& e : l) { + for (const auto &e: l) { glm::vec4 position = glm::vec4(e.x, e.y, e.z, e.heading); - - entity_list.AddCorpse( - Corpse::LoadCharacterCorpseEntity( - e.id, - e.charid, - e.charname, - position, - std::to_string(e.time_of_death), - e.is_rezzed == 1, - RuleB(Zone, EnableShadowrest) ? 0 : e.was_at_graveyard, - e.guild_consent_id - ) - ); + entity_list.AddCorpse(Corpse::LoadCharacterCorpse(e, position)); } return true; diff --git a/zone/zonedb.h b/zone/zonedb.h index 8cb4f0048..2c522607e 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -307,7 +307,8 @@ struct CharacterCorpseItemEntry struct CharacterCorpseEntry { bool locked; - uint32 exp; + uint64 exp; + uint64 gm_exp; float size; uint8 level; uint32 race; @@ -328,6 +329,9 @@ struct CharacterCorpseEntry uint8 hairstyle; uint8 face; uint8 beard; + uint8 killed_by; + bool rezzable; + uint32 rez_time; uint32 drakkin_heritage; uint32 drakkin_tattoo; uint32 drakkin_details; @@ -493,7 +497,6 @@ public: bool GetDecayTimes(npcDecayTimes_Struct* npc_decay_times); uint32 GetFirstCorpseID(uint32 character_id); Corpse* LoadCharacterCorpse(uint32 corpse_id); - bool LoadCharacterCorpseData(uint32 corpse_id, CharacterCorpseEntry &corpse); bool LoadCharacterCorpses(uint32 zone_id, uint16 instance_id); void MarkCorpseAsResurrected(uint32 corpse_id); uint32 SaveCharacterCorpse(uint32 character_id, const std::string& name, uint32 zone_id, uint16 instance_id, const CharacterCorpseEntry& c, const glm::vec4& position, uint32 guild_consent_id);