1
0

Added support to create duplicated zones of public zones (non city zone). Additionally spawn locations can be limited to the primary public zone and not replicated (for epics).

This commit is contained in:
Emagi 2024-12-23 08:46:11 -05:00
parent 7f138c5bef
commit 42173ceef2
15 changed files with 189 additions and 50 deletions

View File

@ -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) {

View File

@ -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__);
}
@ -1651,8 +1653,6 @@ void LuaInterface::DeletePendingSpells(bool all) {
}
}
spells_pending_delete.erase(spell);
if (spell->spell->IsCopiedSpell())
{
RemoveCustomSpell(spell->spell->GetSpellID());
@ -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();
}

View File

@ -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

View File

@ -208,6 +208,7 @@ enum RuleType {
HOTime,
UseMapUnderworldCoords,
MapUnderworldCoordOffset,
SharedZoneMaxPlayers,
/* LOOT */
LootRadius,

View File

@ -150,6 +150,7 @@ Spawn::Spawn(){
reset_movement = false;
respawn_offset_low = 0;
respawn_offset_high = 0;
duplicated_spawn = true;
ResetKnockedBack();
}

View File

@ -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;

View File

@ -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.

View File

@ -23,9 +23,11 @@ along with EQ2Emu. If not, see <http://www.gnu.org/licenses/>.
#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) {
@ -215,7 +217,10 @@ std::string PeerManager::getZonePeerId(const std::string& inc_zone_name, int32 i
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<std::mutex> 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<std::string>("zone_name");
bool instance_zone = zone.second.get<std::string>("instance_zone") == "true";
std::string zone_file_name = zone.second.get<std::string>("zone_file_name");
int32 zone_id = zone.second.get<int32>("zone_id");
int32 instance_id = zone.second.get<int32>("instance_id");
int32 duplicate_id = zone.second.get<int32>("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();

View File

@ -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();

View File

@ -504,6 +504,7 @@ void ZoneList::PopulateZoneList(boost::property_tree::ptree& pt) {
zone_pt.put("instance_type", static_cast<int8>(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<http::string_body>& 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<int32>("instance_id")) {
instanceId = inst_id.get();
@ -650,6 +652,10 @@ void World::Web_worldhandle_startzone(const http::request<http::string_body>& re
duplicatedZone = duplicated_zone.get();
}
if (auto duplicated_id = json_tree.get_optional<int32>("duplicated_id")) {
duplicatedID = duplicated_id.get();
}
if (auto level = json_tree.get_optional<int32>("min_level")) {
minLevel = level.get();
}
@ -672,7 +678,16 @@ void World::Web_worldhandle_startzone(const http::request<http::string_body>& 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;
}

View File

@ -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<ZoneServer*>::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<string> list;

View File

@ -512,6 +512,8 @@ class ZoneList {
void ReloadMail();
void ReloadSpawns();
int32 GetHighestDuplicateID(const std::string& inc_zone_name, int32 inc_zone_id);
void WatchdogHeartbeat();
void SendTimeUpdate();

View File

@ -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);

View File

@ -185,6 +185,7 @@ ZoneServer::ZoneServer(const char* name) {
is_initialized = false;
isInstance = false;
duplicated_zone = false;
duplicated_id = 0;
}
typedef map <int32, bool> ChangedSpawnMapType;
@ -2525,7 +2526,9 @@ Spawn* ZoneServer::ProcessSpawnLocation(SpawnLocation* spawnlocation, map<int32,
{
if(spawnlocation->entities[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())

View File

@ -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<int32> pNumPlayers;