diff --git a/zone/command.cpp b/zone/command.cpp index 2dc55221c..123d3889b 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -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"); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index e04fd6a9d..ad75521a7 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -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(new ServerPacket(ServerOP_SpawnPlayerCorpse, sizeof(SpawnPlayerCorpse_Struct))); + SpawnPlayerCorpse_Struct* spc = reinterpret_cast(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; +} diff --git a/zone/corpse.h b/zone/corpse.h index 300569f89..cd564e80d 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -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(); diff --git a/zone/entity.cpp b/zone/entity.cpp index 1ad03eea0..659048d35 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -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; +} diff --git a/zone/entity.h b/zone/entity.h index b7f90b6f9..4b0c38e7f 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -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); diff --git a/zone/zone.cpp b/zone/zone.cpp index 23e25c280..ff45da839 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -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 diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index b157aeba2..bbd929450 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -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); diff --git a/zone/zonedb.h b/zone/zonedb.h index bb6311eb0..569819100 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -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);