From 10f67d103bfcb009d7ad7d1b63879b4ca5c71bb7 Mon Sep 17 00:00:00 2001 From: regneq Date: Sat, 6 Sep 2025 12:01:41 -0700 Subject: [PATCH] fixed ground spawn that would automagically reappear just by zoning or relogging. added 3 groundspawn rules. decay timer, disarm decay timer, and random respawn. --- common/ruletypes.h | 6 ++++++ zone/entity.cpp | 8 +++++--- zone/npc.cpp | 2 +- zone/object.cpp | 45 ++++++++++++++++++++++++++++++++++++++------- zone/object.h | 2 ++ 5 files changed, 52 insertions(+), 11 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 1e08a9cbd..a6cfecd4f 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -1178,6 +1178,12 @@ RULE_INT(EvolvingItems, DelayUponEquipping, 30000, "Delay in ms before an evolvi RULE_BOOL(EvolvingItems, DestroyAugmentsOnEvolve, false, "If this is enabled, any augments in an item will be destroyed when the item evolves. Otherwise, send augments to the player via the parcel system (requires that the Parcel System be enabled).") RULE_CATEGORY_END() +RULE_CATEGORY(Groundspawns) +RULE_INT(Groundspawns, DecayTime, 300000, "Decay time of player dropped items.") +RULE_INT(Groundspawns, DisarmDecayTime, 300000, "Decay time of weapons dropped due to disarm") +RULE_BOOL(Groundspawns, RandomSpawn, true, "Determines if groundspawns with random spawn locs will periodically despawn and respawn elsewhere.") +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/zone/entity.cpp b/zone/entity.cpp index 37fa9b4ad..e7bc3a66f 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1483,9 +1483,11 @@ void EntityList::SendZoneObjects(Client *client) { auto it = object_list.begin(); while (it != object_list.end()) { - auto app = new EQApplicationPacket; - it->second->CreateSpawnPacket(app); - client->FastQueuePacket(&app); + if (!it->second->IsGroundSpawn() || !it->second->RespawnTimerEnabled()) { + auto app = new EQApplicationPacket; + it->second->CreateSpawnPacket(app); + client->FastQueuePacket(&app); + } ++it; } } diff --git a/zone/npc.cpp b/zone/npc.cpp index 8f12645b7..8d63a97c8 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1838,7 +1838,7 @@ void NPC::Disarm(Client* client, int chance) { CalcBonuses(); if (inst) { // create a ground item - Object* object = new Object(inst, GetX(), GetY(), GetZ(), 0.0f, 300000); + Object* object = new Object(inst, GetX(), GetY(), GetZ(), 0.0f, RuleI(Groundspawns, DisarmDecayTime)); entity_list.AddObject(object, true); object->StartDecay(); safe_delete(inst); diff --git a/zone/object.cpp b/zone/object.cpp index 6da171b23..86cc6944a 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -53,7 +53,8 @@ Object::Object( bool fix_z ) : respawn_timer(0), -decay_timer(300000) +decay_timer(RuleI(Groundspawns, DecayTime)), +random_timer(0) { user = nullptr; last_user = nullptr; @@ -78,6 +79,7 @@ decay_timer(300000) memset(m_display_name, 0, sizeof(m_display_name)); respawn_timer.Disable(); + random_timer.Disable(); // Set drop_id to zero - it will be set when added to zone with SetID() m_data.drop_id = 0; @@ -104,7 +106,8 @@ Object::Object( bool fix_z ) : respawn_timer(respawn_timer_ * 1000), -decay_timer(300000) +decay_timer(RuleI(Groundspawns, DecayTime)), +random_timer(respawn_timer) { user = nullptr; @@ -131,6 +134,12 @@ decay_timer(300000) m_data.zone_id = zone->GetZoneID(); respawn_timer.Disable(); + if (!RuleB(Groundspawns, RandomSpawn) || m_min_x == m_max_x || m_min_y == m_max_y) { + random_timer.Disable(); + } + else { + random_timer.Start(); + } strcpy(m_data.object_name, name.c_str()); @@ -152,7 +161,8 @@ Object::Object( const EQ::ItemInstance* inst ) : respawn_timer(0), -decay_timer(300000) +decay_timer(RuleI(Groundspawns, DecayTime)), +random_timer(0) { user = nullptr; last_user = nullptr; @@ -185,6 +195,7 @@ decay_timer(300000) decay_timer.Start(); respawn_timer.Disable(); + random_timer.Disable(); // Hardcoded portion for unknown members m_data.unknown024 = 0x7f001194; @@ -226,7 +237,8 @@ Object::Object( bool fix_z ) : respawn_timer(0), -decay_timer(decay_time) +decay_timer(decay_time), +random_timer(0) { user = nullptr; last_user = nullptr; @@ -255,6 +267,7 @@ decay_timer(decay_time) } respawn_timer.Disable(); + random_timer.Disable(); // Hardcoded portion for unknown members m_data.unknown024 = 0x7f001194; @@ -298,7 +311,8 @@ Object::Object( uint32 decay_time ) : respawn_timer(0), -decay_timer(decay_time) +decay_timer(decay_time), +random_timer(0) { user = nullptr; last_user = nullptr; @@ -331,6 +345,7 @@ decay_timer(decay_time) } respawn_timer.Disable(); + random_timer.Disable(); // Hardcoded portion for unknown members m_data.unknown024 = 0x7f001194; @@ -533,8 +548,24 @@ bool Object::Process(){ return false; } - if (m_ground_spawn && respawn_timer.Check()){ - RandomSpawn(true); + if (m_ground_spawn) { + if (respawn_timer.Enabled()) { + // respawn_timer is enabled if item was picked up, and waiting to respawn + if (respawn_timer.Check()) { + // when random_timer is enabled, RandomSpawn() will send a despawn packet + bool rand_respawn = random_timer.Enabled(); + random_timer.Disable(); + RandomSpawn(true); + respawn_timer.Disable(); + // re-start random_timer if it was running + if (rand_respawn) { + random_timer.Start(); + } + } + } + else if (random_timer.Check()) { + RandomSpawn(true); + } } if (user && !entity_list.GetClientByCharID(user->CharacterID())) { diff --git a/zone/object.h b/zone/object.h index 5e66c3d19..27753dbdd 100644 --- a/zone/object.h +++ b/zone/object.h @@ -221,6 +221,7 @@ public: std::vector GetEntityVariables(); void SetEntityVariable(std::string variable_name, std::string variable_value); bool EntityVariableExists(std::string variable_name); + bool RespawnTimerEnabled() { return respawn_timer.Enabled(); }; protected: void ResetState(); // Set state back to original @@ -245,6 +246,7 @@ protected: Client *user; Client *last_user; + Timer random_timer; Timer respawn_timer; Timer decay_timer; void FixZ();