diff --git a/source/WorldServer/Entity.cpp b/source/WorldServer/Entity.cpp
index 0fa247a..76d646a 100644
--- a/source/WorldServer/Entity.cpp
+++ b/source/WorldServer/Entity.cpp
@@ -793,7 +793,7 @@ void Entity::GetWeaponDamage(Item* item, int32* low_damage, int32* high_damage)
Skill* masterySkill = ((Player*)this)->skill_list.GetSkill(item->generic_info.skill_req2);
if(masterySkill) {
- LogWrite(PLAYER__ERROR, 0, "Player", "Item has skill %s %u requirement", masterySkill->name.data.c_str(), item->generic_info.skill_req2);
+ LogWrite(PLAYER__DEBUG, 0, "Player", "Item %s has skill %s %u requirement", item->name.c_str(), masterySkill->name.data.c_str(), item->generic_info.skill_req2);
int16 skillID = master_item_list.GetItemStatIDByName(masterySkill->name.data);
int32 skill_chance = (int32)CalculateSkillWithBonus((char*)masterySkill->name.data.c_str(), master_item_list.GetItemStatIDByName(masterySkill->name.data), false);
if(skill_chance >= min_level_skill && skill_chance < rec_level_skill) {
diff --git a/source/WorldServer/LuaInterface.cpp b/source/WorldServer/LuaInterface.cpp
index 5dd3175..b2e4f5b 100644
--- a/source/WorldServer/LuaInterface.cpp
+++ b/source/WorldServer/LuaInterface.cpp
@@ -1623,6 +1623,7 @@ void LuaInterface::DeletePendingSpells(bool all) {
for (del_itr = tmp_deletes.begin(); del_itr != tmp_deletes.end(); del_itr++) {
spell = *del_itr;
+ spells_pending_delete.erase(spell);
SetLuaUserDataStale(spell);
RemoveCurrentSpell(spell->state, spell, false);
@@ -1639,9 +1640,10 @@ void LuaInterface::DeletePendingSpells(bool all) {
if(!targetZone)
continue;
- spellDeleted = true;
+ if(!spellDeleted)
+ targetZone->GetSpellProcess()->DeleteActiveSpell(spell, true);
- targetZone->GetSpellProcess()->DeleteActiveSpell(spell, true);
+ spellDeleted = true;
}
spell->MSpellTargets.releasereadlock(__FUNCTION__, __LINE__);
}
@@ -1650,8 +1652,6 @@ void LuaInterface::DeletePendingSpells(bool all) {
spell->zone->GetSpellProcess()->DeleteActiveSpell(spell, true);
}
}
-
- spells_pending_delete.erase(spell);
if (spell->spell->IsCopiedSpell())
{
@@ -2694,7 +2694,7 @@ bool LuaInterface::RunRegionScript(string script_name, const char* function_name
void LuaInterface::AddPendingSpellDelete(LuaSpell* spell) {
MSpellDelete.lock();
if ( spells_pending_delete.count(spell) == 0 )
- spells_pending_delete[spell] = Timer::GetCurrentTime2() + 10000;
+ spells_pending_delete[spell] = Timer::GetCurrentTime2() + 100;
MSpellDelete.unlock();
}
diff --git a/source/WorldServer/Rules/Rules.cpp b/source/WorldServer/Rules/Rules.cpp
index 3b0f8b3..a8f04e6 100644
--- a/source/WorldServer/Rules/Rules.cpp
+++ b/source/WorldServer/Rules/Rules.cpp
@@ -362,6 +362,8 @@ void RuleManager::Init()
RULE_INIT(R_Zone, UseMapUnderworldCoords, "1"); // use maps lowest Y coordinate to establish underworld markers
RULE_INIT(R_Zone, MapUnderworldCoordOffset, "-200.0"); // adds (or in the case of negative value subtracts) so that the underworld marker is lower when map is using its lowest Y coordinate
+ RULE_INIT(R_Zone, SharedZoneMaxPlayers, "30"); // max players in a shared zone (non instanced) before splitting to another zone, city_zone flagged are exempt
+
RULE_INIT(R_Loot, LootRadius, "5.0");
RULE_INIT(R_Loot, AutoDisarmChest, "1");
RULE_INIT(R_Loot, ChestTriggerRadiusGroup, "10.0"); // radius at which chest will trigger against group members
diff --git a/source/WorldServer/Rules/Rules.h b/source/WorldServer/Rules/Rules.h
index c13cc05..83eaf1e 100644
--- a/source/WorldServer/Rules/Rules.h
+++ b/source/WorldServer/Rules/Rules.h
@@ -208,7 +208,8 @@ enum RuleType {
HOTime,
UseMapUnderworldCoords,
MapUnderworldCoordOffset,
-
+ SharedZoneMaxPlayers,
+
/* LOOT */
LootRadius,
AutoDisarmChest, // if enabled disarm only works if you right click and disarm, clicking and opening chest won't attempt auto disarm
diff --git a/source/WorldServer/Spawn.cpp b/source/WorldServer/Spawn.cpp
index 6bbe24d..534b154 100644
--- a/source/WorldServer/Spawn.cpp
+++ b/source/WorldServer/Spawn.cpp
@@ -150,6 +150,7 @@ Spawn::Spawn(){
reset_movement = false;
respawn_offset_low = 0;
respawn_offset_high = 0;
+ duplicated_spawn = true;
ResetKnockedBack();
}
diff --git a/source/WorldServer/Spawn.h b/source/WorldServer/Spawn.h
index d8ff917..8c3e0ed 100644
--- a/source/WorldServer/Spawn.h
+++ b/source/WorldServer/Spawn.h
@@ -905,7 +905,8 @@ public:
void SetRespawnOffsetLow(sint32 time);
sint32 GetRespawnOffsetHigh();
void SetRespawnOffsetHigh(sint32 time);
-
+ bool DuplicatedSpawn() { return duplicated_spawn; }
+ void SetDuplicateSpawn(bool val) { duplicated_spawn = val; }
int32 GetExpireTime() { return expire_time; }
void SetExpireTime(int32 new_expire_time) { expire_time = new_expire_time; }
int32 GetExpireOffsetTime();
@@ -1505,6 +1506,7 @@ private:
int32 respawn;
sint32 respawn_offset_low;
sint32 respawn_offset_high;
+ bool duplicated_spawn;
int32 expire_time;
int32 expire_offset;
float x_offset;
diff --git a/source/WorldServer/SpawnLists.h b/source/WorldServer/SpawnLists.h
index 7617fef..546989b 100644
--- a/source/WorldServer/SpawnLists.h
+++ b/source/WorldServer/SpawnLists.h
@@ -48,6 +48,7 @@ struct SpawnEntry{
int32 respawn;
sint32 respawn_offset_low;
sint32 respawn_offset_high;
+ bool duplicated_spawn;
int32 expire_time;
int32 expire_offset;
//devn00b: added spawn location overrides, added these to accomodate.
diff --git a/source/WorldServer/Web/PeerManager.cpp b/source/WorldServer/Web/PeerManager.cpp
index 8fe88a6..8be9d95 100644
--- a/source/WorldServer/Web/PeerManager.cpp
+++ b/source/WorldServer/Web/PeerManager.cpp
@@ -23,9 +23,11 @@ along with EQ2Emu. If not, see .
#include "../net.h"
#include "../PlayerGroups.h"
#include "HTTPSClientPool.h"
+#include "../Rules/Rules.h"
extern NetConnection net;
extern HTTPSClientPool peer_https_pool;
+extern RuleManager rule_manager;
// HealthCheck method definitions
void HealthCheck::updateStatus(HealthStatus newStatus) {
@@ -214,8 +216,11 @@ std::string PeerManager::getZonePeerId(const std::string& inc_zone_name, int32 i
else if (!instance_zone && inc_zone_name.length() > 0 && strncasecmp(zone_name.c_str(), inc_zone_name.c_str(), inc_zone_name.length()) == 0) {
match = true;
}
-
- if(!instance_zone && num_players >= 30 && !city_zone) {
+
+ int32 max_players = rule_manager.GetZoneRule(zone_id, R_Zone, SharedZoneMaxPlayers)->GetInt32();
+ if(max_players < 1) // default of 30
+ max_players = 30;
+ if(!instance_zone && num_players >= max_players && !city_zone) {
match = false;
setZonePeerData(opt_details, peerId, peer->worldAddr, peer->internalWorldAddr, peer->worldPort, peer->webAddr, peer->webPort, zone_file_name, zone_name, zone_id, instance_id,
safe_x, safe_y, safe_z, safe_heading, lock_state, min_status, min_level, max_level, min_version, default_lockout_time, default_reenter_time, instance_type, num_players, city_zone);
@@ -239,6 +244,50 @@ std::string PeerManager::getZonePeerId(const std::string& inc_zone_name, int32 i
return fullZoneId;
}
+int32 PeerManager::getZoneHighestDuplicateId(const std::string& inc_zone_name, int32 inc_zone_id) {
+ int32 highestID = 0;
+ bool matched_zone = false;
+ for (auto& [peerId, peer] : peers) {
+ if (peer->healthCheck.status != HealthStatus::OK)
+ continue;
+ try {
+ std::lock_guard lock(peer->dataMutex);
+ for (const auto& zone : peer->zone_tree->get_child("Zones")) {
+ // Access each field within the current zone
+ std::string zone_name = zone.second.get("zone_name");
+ bool instance_zone = zone.second.get("instance_zone") == "true";
+ std::string zone_file_name = zone.second.get("zone_file_name");
+ int32 zone_id = zone.second.get("zone_id");
+ int32 instance_id = zone.second.get("instance_id");
+ int32 duplicate_id = zone.second.get("duplicated_id");
+
+ bool match = false;
+ if (!instance_zone && inc_zone_id > 0 && zone_id == inc_zone_id) {
+ match = true;
+ }
+ else if (!instance_zone && inc_zone_name.length() > 0 && strncasecmp(zone_name.c_str(), inc_zone_name.c_str(), inc_zone_name.length()) == 0) {
+ match = true;
+ }
+
+ if (match) {
+ matched_zone = true;
+ if(duplicate_id > highestID)
+ highestID = duplicate_id;
+ }
+ }
+ }
+ catch (const std::exception& e) {
+ LogWrite(PEERING__ERROR, 0, "Peering", "%s: Error Parsing Zones for %s:%u", __FUNCTION__, peer->webAddr.c_str(), peer->webPort);
+ }
+ }
+
+ if(matched_zone) {
+ highestID++;
+ }
+
+ return highestID;
+}
+
void PeerManager::handlePrimaryConflict(const std::string& reconnectingPeerId) {
// Compare IDs or priorities to decide on the primary role
auto currentPrimary = getCurrentPrimary();
diff --git a/source/WorldServer/Web/PeerManager.h b/source/WorldServer/Web/PeerManager.h
index b1119cf..1a0f80b 100644
--- a/source/WorldServer/Web/PeerManager.h
+++ b/source/WorldServer/Web/PeerManager.h
@@ -182,6 +182,7 @@ public:
void SendPeersGuildChannelMessage(int32 guild_id, std::string fromName, std::string message, int16 channel, int32 language_id = 0);
void sendZonePeerList(Client* client);
std::string getZonePeerId(const std::string& inc_zone_name, int32 inc_zone_id, int32 inc_instance_id, ZoneChangeDetails* opt_details = nullptr, bool only_always_loaded = false);
+ int32 getZoneHighestDuplicateId(const std::string& inc_zone_name, int32 inc_zone_id);
void setZonePeerData(ZoneChangeDetails* opt_details);
void setPrimary(const std::string& id);
bool hasPrimary();
diff --git a/source/WorldServer/Web/WorldWeb.cpp b/source/WorldServer/Web/WorldWeb.cpp
index b8ee205..f410007 100644
--- a/source/WorldServer/Web/WorldWeb.cpp
+++ b/source/WorldServer/Web/WorldWeb.cpp
@@ -504,6 +504,7 @@ void ZoneList::PopulateZoneList(boost::property_tree::ptree& pt) {
zone_pt.put("instance_type", static_cast(tmp->GetInstanceType()));
zone_pt.put("always_loaded", tmp->AlwaysLoaded());
zone_pt.put("duplicated_zone", tmp->DuplicatedZone());
+ zone_pt.put("duplicated_id", tmp->DuplicatedID());
maintree.push_back(std::make_pair("", zone_pt));
}
@@ -629,6 +630,7 @@ void World::Web_worldhandle_startzone(const http::request& re
int32 zoneId = 0;
std::string zoneName("");
bool alwaysLoaded = false, duplicatedZone = false;
+ int32 duplicatedID = 0;
int32 minLevel = 0, maxLevel = 0, avgLevel = 0, firstLevel = 0;
if (auto inst_id = json_tree.get_optional("instance_id")) {
instanceId = inst_id.get();
@@ -650,6 +652,10 @@ void World::Web_worldhandle_startzone(const http::request& re
duplicatedZone = duplicated_zone.get();
}
+ if (auto duplicated_id = json_tree.get_optional("duplicated_id")) {
+ duplicatedID = duplicated_id.get();
+ }
+
if (auto level = json_tree.get_optional("min_level")) {
minLevel = level.get();
}
@@ -672,7 +678,16 @@ void World::Web_worldhandle_startzone(const http::request& re
if (!instanceId) {
if ((zone_list.GetZone(&details, zoneId, zoneName, true, false, false, false, false, alwaysLoaded, false, duplicatedZone))) {
if(details.zonePtr) {
- ((ZoneServer*)details.zonePtr)->SetDuplicatedZone(duplicatedZone);
+ ZoneServer* tmpZone = ((ZoneServer*)details.zonePtr);
+ tmpZone->SetDuplicatedZone(duplicatedZone);
+ tmpZone->SetDuplicatedID(duplicatedID);
+ if(duplicatedZone) {
+ std::string desc = "";
+ if(tmpZone->GetZoneDescription())
+ desc = std::string(tmpZone->GetZoneDescription());
+ desc += " " + std::to_string(duplicatedID);
+ tmpZone->SetZoneDescription((char*)desc.c_str());
+ }
}
success = 1;
}
diff --git a/source/WorldServer/World.cpp b/source/WorldServer/World.cpp
index f6bcb95..f252a4b 100644
--- a/source/WorldServer/World.cpp
+++ b/source/WorldServer/World.cpp
@@ -699,8 +699,12 @@ bool ZoneList::GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::
if(!check_instances && tmp->IsInstanceZone())
continue;
+ int32 max_players = rule_manager.GetZoneRule(tmp->GetZoneID(), R_Zone, SharedZoneMaxPlayers)->GetInt32();
+ if(max_players < 1) // default of 30
+ max_players = 30;
+
if(!tmp->isZoneShuttingDown() && ((opt_zone_id > 0 && tmp->GetZoneID() == opt_zone_id) || (opt_zone_name.length() > 0 && strncasecmp(tmp->GetZoneName(), opt_zone_name.c_str(), opt_zone_name.length())==0))){
- if(tmp->NumPlayers() < 30 || tmp->IsCityZone()) {
+ if(tmp->NumPlayers() < max_players || tmp->IsCityZone()) {
ret = tmp;
if(increment_zone) {
ret->IncrementIncomingClients();
@@ -719,8 +723,10 @@ bool ZoneList::GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::
if(!ret && check_peers) {
std::string peerId = peer_manager.getZonePeerId(opt_zone_name, opt_zone_id, 0, zone_details, only_always_loaded);
if(peerId.size() > 0) {
-
- if(zone_details->instanceType == 0 && zone_details->numPlayers >= 30 && !zone_details->isCityZone) {
+ int32 max_players = rule_manager.GetZoneRule(zone_details->zoneId, R_Zone, SharedZoneMaxPlayers)->GetInt32();
+ if(max_players < 1) // default of 30
+ max_players = 30;
+ if(zone_details->instanceType == 0 && zone_details->numPlayers >= max_players && !zone_details->isCityZone) {
LogWrite(WORLD__WARNING, 0, "World", "Peer %s is providing zone %s for zone %s id %u, however the zone is full, omitting result.", peerId.c_str(), zone_details->zoneName.c_str(), opt_zone_name.c_str(), opt_zone_id);
hadFullZone = true;
}
@@ -750,6 +756,7 @@ bool ZoneList::GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::
root.put("zone_id", std::to_string(opt_zone_id));
root.put("always_loaded", only_always_loaded);
root.put("duplicated_zone", hadFullZone);
+ root.put("duplicated_id", zone_list.GetHighestDuplicateID(opt_zone_name, opt_zone_id));
root.put("min_level", minLevel);
root.put("max_level", maxLevel);
@@ -772,6 +779,14 @@ bool ZoneList::GetZone(ZoneChangeDetails* zone_details, int32 opt_zone_id, std::
tmp = new ZoneServer(opt_zone_name.c_str());
database.LoadZoneInfo(tmp, minLevel, maxLevel, avgLevel, firstLevel);
tmp->SetDuplicatedZone(hadFullZone);
+ if(hadFullZone) {
+ tmp->SetDuplicatedID(zone_list.GetHighestDuplicateID(opt_zone_name, opt_zone_id));
+ std::string desc = "";
+ if(tmp->GetZoneDescription())
+ desc = std::string(tmp->GetZoneDescription());
+ desc += " " + std::to_string(tmp->DuplicatedID());
+ tmp->SetZoneDescription((char*)desc.c_str());
+ }
tmp->Init();
tmp->SetAlwaysLoaded(only_always_loaded);
}
@@ -835,6 +850,8 @@ bool ZoneList::GetZoneByInstance(ZoneChangeDetails* zone_details, int32 instance
root.put("zone_name", zonename);
root.put("zone_id", std::to_string(zone_id));
root.put("always_loaded", false);
+ root.put("duplicated_zone", false); // instances dont duplicate, only shared zones
+ root.put("duplicated_id", 0);
root.put("min_level", minLevel);
root.put("max_level", maxLevel);
@@ -1590,6 +1607,40 @@ void ZoneList::ReloadSpawns() {
MZoneList.releasereadlock(__FUNCTION__, __LINE__);
}
+
+int32 ZoneList::GetHighestDuplicateID(const std::string& inc_zone_name, int32 inc_zone_id)
+{
+ list::iterator zone_iter;
+ ZoneServer* tmp = 0;
+ int32 highest_id = peer_manager.getZoneHighestDuplicateId(inc_zone_name, inc_zone_id);
+ MZoneList.readlock(__FUNCTION__, __LINE__);
+ bool match = false;
+ bool matched_peer = (highest_id>0);
+ for(zone_iter=zlist.begin(); zone_iter!=zlist.end(); zone_iter++)
+ {
+ tmp = *zone_iter;
+
+ if(tmp->IsInstanceZone())
+ continue;
+
+ if(((inc_zone_id > 0 && tmp->GetZoneID() == inc_zone_id) || (inc_zone_name.length() > 0 && strncasecmp(tmp->GetZoneName(), inc_zone_name.c_str(), inc_zone_name.length())==0))){
+ if(tmp->DuplicatedID() > highest_id) {
+ highest_id = tmp->DuplicatedID();
+ matched_peer = false;
+ }
+
+ match = true;
+ }
+ }
+
+ MZoneList.releasereadlock(__FUNCTION__, __LINE__);
+
+ if(match && !matched_peer)
+ highest_id++;
+
+ return highest_id;
+}
+
bool World::ReportBug(string data, char* player_name, int32 account_id, const char* spawn_name, int32 spawn_id, int32 zone_id){
//loginserver
vector list;
diff --git a/source/WorldServer/World.h b/source/WorldServer/World.h
index 2d823b8..5230078 100644
--- a/source/WorldServer/World.h
+++ b/source/WorldServer/World.h
@@ -511,7 +511,9 @@ class ZoneList {
void ShutDownZones();
void ReloadMail();
void ReloadSpawns();
-
+
+ int32 GetHighestDuplicateID(const std::string& inc_zone_name, int32 inc_zone_id);
+
void WatchdogHeartbeat();
void SendTimeUpdate();
diff --git a/source/WorldServer/WorldDatabase.cpp b/source/WorldServer/WorldDatabase.cpp
index 09a3a5f..c325f71 100644
--- a/source/WorldServer/WorldDatabase.cpp
+++ b/source/WorldServer/WorldDatabase.cpp
@@ -3278,6 +3278,8 @@ void WorldDatabase::LoadSpecialZones(){
if(zone) {
LogWrite(ZONE__INFO, 0, "Zone", "Static zone %s will be spundown due to another peer taking over.", row[1]);
zone->SetAlwaysLoaded(false);
+ zone->SetDuplicatedZone(true);
+ zone->SetDuplicatedID(zone_list.GetHighestDuplicateID(std::string(zone->GetZoneName()), zone->GetZoneID()));
}
}
}
@@ -3465,25 +3467,26 @@ int32 WorldDatabase::ProcessSpawnLocations(ZoneServer* zone, const char* sql_que
entry->respawn = atoul(row[11]);
entry->respawn_offset_low = atoi(row[12]);
entry->respawn_offset_high = atoi(row[13]);
- entry->expire_time = atoul(row[16]);
- entry->expire_offset = atoul(row[17]);
+ entry->duplicated_spawn = atoul(row[14]);
+ entry->expire_time = atoul(row[17]);
+ entry->expire_offset = atoul(row[18]);
//devn00b add stat overrides. Just a slight increase in size. Used in ZoneServer::AddNPCSpawn.
- entry->lvl_override = atoul(row[21]);
- entry->hp_override = atoul(row[22]);
- entry->mp_override = atoul(row[23]);
- entry->str_override = atoul(row[24]);
- entry->sta_override = atoul(row[25]);
- entry->wis_override = atoul(row[26]);
- entry->int_override = atoul(row[27]);
- entry->agi_override = atoul(row[28]);
- entry->heat_override = atoul(row[29]);
- entry->cold_override = atoul(row[30]);
- entry->magic_override = atoul(row[31]);
- entry->mental_override = atoul(row[32]);
- entry->divine_override = atoul(row[33]);
- entry->disease_override = atoul(row[34]);
- entry->poison_override = atoul(row[35]);
- entry->difficulty_override = atoul(row[36]);
+ entry->lvl_override = atoul(row[22]);
+ entry->hp_override = atoul(row[23]);
+ entry->mp_override = atoul(row[24]);
+ entry->str_override = atoul(row[25]);
+ entry->sta_override = atoul(row[26]);
+ entry->wis_override = atoul(row[27]);
+ entry->int_override = atoul(row[28]);
+ entry->agi_override = atoul(row[29]);
+ entry->heat_override = atoul(row[30]);
+ entry->cold_override = atoul(row[31]);
+ entry->magic_override = atoul(row[32]);
+ entry->mental_override = atoul(row[33]);
+ entry->divine_override = atoul(row[34]);
+ entry->disease_override = atoul(row[35]);
+ entry->poison_override = atoul(row[36]);
+ entry->difficulty_override = atoul(row[37]);
spawn_location->x = atof(row[2]);
spawn_location->y = atof(row[3]);
spawn_location->z = atof(row[4]);
@@ -3491,12 +3494,12 @@ int32 WorldDatabase::ProcessSpawnLocations(ZoneServer* zone, const char* sql_que
spawn_location->y_offset = atof(row[6]);
spawn_location->z_offset = atof(row[7]);
spawn_location->heading = atof(row[8]);
- spawn_location->pitch = atof(row[18]);
- spawn_location->roll = atof(row[19]);
- spawn_location->conditional = atoi(row[20]);
+ spawn_location->pitch = atof(row[19]);
+ spawn_location->roll = atof(row[20]);
+ spawn_location->conditional = atoi(row[21]);
spawn_location->total_percentage += entry->spawn_percentage;
- spawn_location->grid_id = strtoul(row[14], NULL, 0);
- spawn_location->placement_id = strtoul(row[15], NULL, 0);
+ spawn_location->grid_id = strtoul(row[15], NULL, 0);
+ spawn_location->placement_id = strtoul(row[16], NULL, 0);
spawn_location->AddSpawn(entry);
}
@@ -3535,21 +3538,21 @@ void WorldDatabase::LoadSpawns(ZoneServer* zone)
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter LoadSpawns");
if(zone->GetInstanceID() == 0) {
- npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
- objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
- widgets = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_widgets sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET);
- signs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_signs ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN);
- ground_spawns = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_ground sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN);
+ npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
+ objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
+ widgets = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_widgets sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET);
+ signs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_signs ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN);
+ ground_spawns = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_ground sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%u and slp.instance_id=%u ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN);
spawn_groups = LoadSpawnLocationGroups(zone);
spawn_group_associations = LoadSpawnLocationGroupAssociations(zone);
spawn_group_chances = LoadSpawnGroupChances(zone);
}
else {
- npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
- objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
- widgets = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_widgets sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET);
- signs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_signs ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN);
- ground_spawns = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_ground sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN);
+ npcs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_npcs sn where sn.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_NPC);
+ objects = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_objects so where so.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_OBJECT);
+ widgets = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_widgets sw where sw.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_WIDGET);
+ signs = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_signs ss where ss.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_SIGN);
+ ground_spawns = ProcessSpawnLocations(zone, "SELECT sln.id, sle.id, slp.x, slp.y, slp.z, slp.x_offset, slp.y_offset, slp.z_offset, slp.heading, sle.spawn_id, sle.spawnpercentage, slp.respawn, slp.respawn_offset_low, slp.respawn_offset_high, slp.duplicated_spawn, slp.grid_id, slp.id, slp.expire_timer, slp.expire_offset, slp.pitch, slp.roll, sle.condition, slp.lvl_override, slp.hp_override, slp.mp_override, slp.str_override, slp.sta_override, slp.wis_override, slp.int_override, slp.agi_override, slp.heat_override, slp.cold_override, slp.magic_override, slp.mental_override, slp.divine_override, slp.disease_override, slp.poison_override, difficulty_override FROM spawn_location_placement slp, spawn_location_name sln, spawn_location_entry sle, spawn_ground sg where sg.spawn_id = sle.spawn_id and sln.id = sle.spawn_location_id and sln.id = slp.spawn_location_id and slp.zone_id=%i and (slp.instance_id = 0 or slp.instance_id=%u) ORDER BY sln.id, sle.id", SPAWN_ENTRY_TYPE_GROUNDSPAWN);
spawn_groups = LoadSpawnLocationGroups(zone);
spawn_group_associations = LoadSpawnLocationGroupAssociations(zone);
spawn_group_chances = LoadSpawnGroupChances(zone);
diff --git a/source/WorldServer/zoneserver.cpp b/source/WorldServer/zoneserver.cpp
index c2db488..e8519fd 100644
--- a/source/WorldServer/zoneserver.cpp
+++ b/source/WorldServer/zoneserver.cpp
@@ -185,6 +185,7 @@ ZoneServer::ZoneServer(const char* name) {
is_initialized = false;
isInstance = false;
duplicated_zone = false;
+ duplicated_id = 0;
}
typedef map ChangedSpawnMapType;
@@ -2525,7 +2526,9 @@ Spawn* ZoneServer::ProcessSpawnLocation(SpawnLocation* spawnlocation, mapentities[i]->spawn_percentage == 0)
continue;
-
+ if(DuplicatedZone() && !spawnlocation->entities[i]->duplicated_spawn) {
+ return nullptr; // dupe public/shared zone, we have turned off duplicating spawns for this location
+ }
int32 spawnTime = 1;
@@ -2994,6 +2997,7 @@ NPC* ZoneServer::AddNPCSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnentr
npc->SetRespawnTime(spawnentry->respawn);
npc->SetRespawnOffsetLow(spawnentry->respawn_offset_low);
npc->SetRespawnOffsetHigh(spawnentry->respawn_offset_high);
+ npc->SetDuplicateSpawn(spawnentry->duplicated_spawn);
npc->SetExpireTime(spawnentry->expire_time);
//devn00b add overrides for some spawns
@@ -3417,6 +3421,7 @@ Sign* ZoneServer::AddSignSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnen
sign->SetRespawnTime(spawnentry->respawn);
sign->SetRespawnOffsetLow(spawnentry->respawn_offset_low);
sign->SetRespawnOffsetHigh(spawnentry->respawn_offset_high);
+ sign->SetDuplicateSpawn(spawnentry->duplicated_spawn);
sign->SetExpireTime(spawnentry->expire_time);
if (spawnentry->expire_time > 0)
AddSpawnExpireTimer(sign, spawnentry->expire_time, spawnentry->expire_offset);
@@ -3448,6 +3453,7 @@ Widget* ZoneServer::AddWidgetSpawn(SpawnLocation* spawnlocation, SpawnEntry* spa
widget->SetRespawnTime(spawnentry->respawn);
widget->SetRespawnOffsetLow(spawnentry->respawn_offset_low);
widget->SetRespawnOffsetHigh(spawnentry->respawn_offset_high);
+ widget->SetDuplicateSpawn(spawnentry->duplicated_spawn);
widget->SetExpireTime(spawnentry->expire_time);
widget->SetSpawnOrigHeading(widget->GetHeading());
if (spawnentry->expire_time > 0)
@@ -3474,6 +3480,7 @@ Object* ZoneServer::AddObjectSpawn(SpawnLocation* spawnlocation, SpawnEntry* spa
object->SetRespawnTime(spawnentry->respawn);
object->SetRespawnOffsetLow(spawnentry->respawn_offset_low);
object->SetRespawnOffsetHigh(spawnentry->respawn_offset_high);
+ object->SetDuplicateSpawn(spawnentry->duplicated_spawn);
object->SetExpireTime(spawnentry->expire_time);
if (spawnentry->expire_time > 0)
AddSpawnExpireTimer(object, spawnentry->expire_time, spawnentry->expire_offset);
@@ -3499,6 +3506,7 @@ GroundSpawn* ZoneServer::AddGroundSpawn(SpawnLocation* spawnlocation, SpawnEntry
spawn->SetRespawnTime(spawnentry->respawn);
spawn->SetRespawnOffsetLow(spawnentry->respawn_offset_low);
spawn->SetRespawnOffsetHigh(spawnentry->respawn_offset_high);
+ spawn->SetDuplicateSpawn(spawnentry->duplicated_spawn);
spawn->SetExpireTime(spawnentry->expire_time);
if(spawn->GetRandomizeHeading()) {
@@ -5280,7 +5288,7 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
else if ( dead->IsObject ( ) )
database.CreateInstanceSpawnRemoved(dead->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_OBJECT, dead->GetRespawnTime(),dead->GetZone()->GetInstanceID());
}
- else if(!groupMemberAlive && dead->GetSpawnLocationID() > 0) {
+ else if(!groupMemberAlive && dead->GetSpawnLocationID() > 0 && !DuplicatedZone()) {
if(dead->IsNPC())
database.CreatePersistedRespawn(dead->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_NPC,dead->GetRespawnTime(),GetZoneID());
else if(dead->IsObject())
diff --git a/source/WorldServer/zoneserver.h b/source/WorldServer/zoneserver.h
index 639f70a..25edddb 100644
--- a/source/WorldServer/zoneserver.h
+++ b/source/WorldServer/zoneserver.h
@@ -532,9 +532,11 @@ public:
inline bool IsCityZone() { return cityzone; }
inline bool AlwaysLoaded() { return always_loaded; }
inline bool DuplicatedZone() { return duplicated_zone; }
+ inline int32 DuplicatedID() { return duplicated_id; }
void SetCityZone(bool val) { cityzone = val; }
void SetAlwaysLoaded(bool val) { always_loaded = val; }
void SetDuplicatedZone(bool val) { duplicated_zone = val; }
+ void SetDuplicatedID(int32 id) { duplicated_id = id; }
int32 NumPlayers() { return pNumPlayers; }
void SetMinimumStatus(sint16 minStatus) { minimumStatus = minStatus; }
sint16 GetMinimumStatus() { return minimumStatus; }
@@ -939,6 +941,7 @@ private:
bool cityzone;
bool always_loaded;
bool duplicated_zone;
+ int32 duplicated_id;
bool isInstance;
std::atomic pNumPlayers;