From 16e341906d36ea30d30e2bf9f60ba050b48694ee Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Wed, 9 Apr 2025 21:43:18 -0500 Subject: [PATCH] [Fix] Zone State Spawn2 Location Restore (#4844) * [Fix] Zone State Spawn2 Location Restore * Update zone_save_state.cpp * Update zone_save_state.cpp * Update zone_save_state.cpp * Update zone_save_state.cpp --- zone/spawn2.cpp | 8 ++++++- zone/spawn2.h | 2 ++ zone/zone_save_state.cpp | 48 ++++++++++++++++++++++++++++++++++------ 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index 455064ebc..9127ab3e6 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -277,7 +277,13 @@ bool Spawn2::Process() { } } - NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), GravityBehavior::Water); + // zone state restore + if (m_stored_location != glm::vec4(0, 0, -1000, 0)) { + loc = m_stored_location; + m_stored_location = glm::vec4(0, 0, -1000, 0); + } + + NPC *npc = new NPC(tmp, this, loc, GravityBehavior::Water); npcthis = npc; diff --git a/zone/spawn2.h b/zone/spawn2.h index 0cb2572ce..f5b31022a 100644 --- a/zone/spawn2.h +++ b/zone/spawn2.h @@ -79,6 +79,7 @@ public: inline void SetResumedFromZoneSuspend(bool resumed) { m_resumed_from_zone_suspend = resumed; } inline void SetEntityVariables(std::map vars) { m_entity_variables = vars; } inline void SetResumedNPCID(uint32 npc_id) { m_resumed_npc_id = npc_id; } + inline void SetStoredLocation(const glm::vec4& loc) { m_stored_location = loc; } protected: friend class Zone; @@ -108,6 +109,7 @@ private: bool m_resumed_from_zone_suspend = false; uint32 m_resumed_npc_id = 0; std::map m_entity_variables = {}; + glm::vec4 m_stored_location = {0, 0, -1000, 0}; // use -1000 to indicate unset/zero-state }; class SpawnCondition { diff --git a/zone/zone_save_state.cpp b/zone/zone_save_state.cpp index f5e97a571..3401bc5e0 100644 --- a/zone/zone_save_state.cpp +++ b/zone/zone_save_state.cpp @@ -5,6 +5,7 @@ #include "corpse.h" #include "zone.h" #include "zone_save_state.h" +#include "../common/repositories/spawn2_repository.h" // IsZoneStateValid checks if the zone state is valid // if these fields are all empty or zero value for an entire zone state, it's considered invalid @@ -440,8 +441,28 @@ bool Zone::LoadZoneState( zone->initgrids_timer.Trigger(); zone->Process(); + // load base spawn2 data for spawn locations + std::vector spawn2_ids; + for (auto &s: spawn_states) { + if (s.spawn2_id > 0) { + spawn2_ids.push_back(std::to_string(s.spawn2_id)); + } + } + + std::vector spawn2s; + if (!spawn2_ids.empty()) { + spawn2s = Spawn2Repository::GetWhere( + content_db, + fmt::format( + "id IN ({})", + Strings::Join(spawn2_ids, ",") + ) + ); + + LogZoneState("Loaded [{}] spawn2s", spawn2s.size()); + } + // spawn2 - int count = 0; for (auto &s: spawn_states) { if (s.spawngroup_id == 0 || s.is_corpse || s.is_zone) { continue; @@ -463,13 +484,26 @@ bool Zone::LoadZoneState( } } + // find spawn 2 by id + Spawn2Repository::Spawn2 spawn2; + for (auto &sp: spawn2s) { + if (sp.id == s.spawn2_id) { + spawn2 = sp; + break; + } + } + + if (!spawn2.id) { + LogZoneState("Failed to load spawn2 data for spawn2_id [{}]", s.spawn2_id); + } + auto new_spawn = new Spawn2( s.spawn2_id, s.spawngroup_id, - s.x, - s.y, - s.z, - s.heading, + spawn2.id > 0 ? spawn2.x : s.x, + spawn2.id > 0 ? spawn2.y : s.y, + spawn2.id > 0 ? spawn2.z : s.z, + spawn2.id > 0 ? spawn2.heading : s.heading, s.respawn_time, s.variance, spawn_time_left, @@ -481,14 +515,14 @@ bool Zone::LoadZoneState( (EmuAppearance) s.anim ); + new_spawn->SetStoredLocation(glm::vec4(s.x, s.y, s.z, s.heading)); + if (spawn_time_left == 0) { new_spawn->SetResumedNPCID(s.npc_id); new_spawn->SetResumedFromZoneSuspend(true); new_spawn->SetEntityVariables(GetVariablesDeserialized(s.entity_variables)); } - count++; - spawn2_list.Insert(new_spawn); new_spawn->Process(); auto n = new_spawn->GetNPC();