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;