Move player corpses on instance shutdown

Moves corpses to graveyard when an expired instance shuts down.
Zones without a graveyard move them to non-instance version instead.

Fixes player corpses being left inside instances that expire
before graveyards process or in instances without a graveyard
This commit is contained in:
hg 2021-01-02 19:19:31 -05:00
parent de5b7f472d
commit 6c8c81f3db
8 changed files with 118 additions and 17 deletions

View File

@ -4840,6 +4840,10 @@ void command_corpse(Client *c, const Seperator *sep)
c->Message(Chat::White, "Insufficient status to depop player corpse.");
}
else if (strcasecmp(sep->arg[1], "moveallgraveyard") == 0) {
int count = entity_list.MovePlayerCorpsesToGraveyard(true);
c->Message(Chat::White, "Moved [%d] player corpse(s) to zone graveyard", count);
}
else if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "help") == 0) {
c->Message(Chat::White, "#Corpse Sub-Commands:");
c->Message(Chat::White, " DeleteNPCCorpses");
@ -4847,6 +4851,7 @@ void command_corpse(Client *c, const Seperator *sep)
c->Message(Chat::White, " ListNPC");
c->Message(Chat::White, " ListPlayer");
c->Message(Chat::White, " Lock - GM locks the corpse - cannot be looted by non-GM");
c->Message(Chat::White, " MoveAllGraveyard - move all player corpses to zone's graveyard or non-instance");
c->Message(Chat::White, " UnLock");
c->Message(Chat::White, " RemoveCash");
c->Message(Chat::White, " InspectLoot");

View File

@ -826,22 +826,7 @@ bool Corpse::Process() {
}
if (corpse_graveyard_timer.Check()) {
if (zone->HasGraveyard()) {
Save();
player_corpse_depop = true;
database.SendCharacterCorpseToGraveyard(corpse_db_id, zone->graveyard_zoneid(),
(zone->GetZoneID() == zone->graveyard_zoneid()) ? zone->GetInstanceID() : 0, zone->GetGraveyardPoint());
corpse_graveyard_timer.Disable();
auto pack = new ServerPacket(ServerOP_SpawnPlayerCorpse, sizeof(SpawnPlayerCorpse_Struct));
SpawnPlayerCorpse_Struct* spc = (SpawnPlayerCorpse_Struct*)pack->pBuffer;
spc->player_corpse_id = corpse_db_id;
spc->zone_id = zone->graveyard_zoneid();
worldserver.SendPacket(pack);
safe_delete(pack);
LogDebug("Moved [{}] player corpse to the designated graveyard in zone [{}]", this->GetName(), ZoneName(zone->graveyard_zoneid()));
corpse_db_id = 0;
}
MovePlayerCorpseToGraveyard();
corpse_graveyard_timer.Disable();
return false;
}
@ -1643,3 +1628,53 @@ void Corpse::LoadPlayerCorpseDecayTime(uint32 corpse_db_id){
corpse_graveyard_timer.SetTimer(3000);
}
}
void Corpse::SendWorldSpawnPlayerCorpseInZone(uint32_t zone_id)
{
auto pack = std::unique_ptr<ServerPacket>(new ServerPacket(ServerOP_SpawnPlayerCorpse, sizeof(SpawnPlayerCorpse_Struct)));
SpawnPlayerCorpse_Struct* spc = reinterpret_cast<SpawnPlayerCorpse_Struct*>(pack->pBuffer);
spc->player_corpse_id = corpse_db_id;
spc->zone_id = zone_id;
worldserver.SendPacket(pack.get());
}
bool Corpse::MovePlayerCorpseToGraveyard()
{
if (IsPlayerCorpse() && zone && zone->HasGraveyard())
{
Save();
uint16_t instance_id = (zone->GetZoneID() == zone->graveyard_zoneid()) ? zone->GetInstanceID() : 0;
database.SendCharacterCorpseToGraveyard(corpse_db_id, zone->graveyard_zoneid(), instance_id, zone->GetGraveyardPoint());
SendWorldSpawnPlayerCorpseInZone(zone->graveyard_zoneid());
corpse_db_id = 0;
player_corpse_depop = true;
corpse_graveyard_timer.Disable();
LogDebug("Moved [{}] player corpse to the designated graveyard in zone [{}]", GetName(), ZoneName(zone->graveyard_zoneid()));
return true;
}
return false;
}
bool Corpse::MovePlayerCorpseToNonInstance()
{
if (IsPlayerCorpse() && zone && zone->GetInstanceID() != 0)
{
Save();
database.SendCharacterCorpseToNonInstance(corpse_db_id);
SendWorldSpawnPlayerCorpseInZone(zone->GetZoneID());
corpse_db_id = 0;
player_corpse_depop = true;
corpse_graveyard_timer.Disable();
LogDebug("Moved [{}] player corpse to non-instance version of zone [{}]", GetName(), ZoneName(zone->GetZoneID()));
return true;
}
return false;
}

View File

@ -79,6 +79,9 @@ class Corpse : public Mob {
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();

View File

@ -5225,3 +5225,43 @@ void EntityList::GateAllClientsToSafeReturn()
}
}
}
int EntityList::MovePlayerCorpsesToGraveyard(bool force_move_from_instance)
{
if (!zone)
{
return 0;
}
int moved_count = 0;
for (auto it = corpse_list.begin(); it != corpse_list.end();)
{
bool moved = false;
if (it->second && it->second->IsPlayerCorpse())
{
if (zone->HasGraveyard())
{
moved = it->second->MovePlayerCorpseToGraveyard();
}
else if (force_move_from_instance && zone->GetInstanceID() != 0)
{
moved = it->second->MovePlayerCorpseToNonInstance();
}
}
if (moved)
{
safe_delete(it->second);
free_ids.push(it->first);
it = corpse_list.erase(it);
++moved_count;
}
else
{
++it;
}
}
return moved_count;
}

View File

@ -537,6 +537,8 @@ public:
void UpdateAllTraps(bool respawn, bool repopnow = false);
void ClearTrapPointers();
int MovePlayerCorpsesToGraveyard(bool force_move_from_instance = false);
protected:
friend class Zone;
void Depop(bool StartSpawnTimer = false);

View File

@ -1497,7 +1497,10 @@ bool Zone::Process() {
{
expedition->RemoveAllMembers(false); // entity list will teleport clients out immediately
}
// todo: move corpses to non-instanced version of dz at same coords (if no graveyard)
// instance shutting down, move corpses to graveyard or non-instanced zone at same coords
entity_list.MovePlayerCorpsesToGraveyard(true);
entity_list.GateAllClientsToSafeReturn();
database.DeleteInstance(GetInstanceID());
Instance_Shutdown_Timer = new Timer(20000); //20 seconds

View File

@ -4300,6 +4300,18 @@ uint32 ZoneDatabase::SendCharacterCorpseToGraveyard(uint32 dbid, uint32 zone_id,
return dbid;
}
void ZoneDatabase::SendCharacterCorpseToNonInstance(uint32 corpse_db_id)
{
if (corpse_db_id != 0)
{
auto query = fmt::format(SQL(
UPDATE character_corpses SET instance_id = 0 WHERE id = {};
), corpse_db_id);
QueryDatabase(query);
}
}
uint32 ZoneDatabase::GetCharacterCorpseDecayTimer(uint32 corpse_db_id){
std::string query = StringFormat("SELECT(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(time_of_death)) FROM `character_corpses` WHERE `id` = %d AND NOT `time_of_death` = 0", corpse_db_id);
auto results = QueryDatabase(query);

View File

@ -376,6 +376,7 @@ public:
uint32 GetCharacterCorpseID(uint32 char_id, uint8 corpse);
uint32 GetCharacterCorpseItemAt(uint32 corpse_id, uint16 slotid);
uint32 GetPlayerCorpseTimeLeft(uint8 corpse, uint8 type);
void SendCharacterCorpseToNonInstance(uint32 corpse_db_id);
/* Faction */
bool GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction = 0);