
Fix #18 Leashing, leading, rubberbanding issues with spawns resolved Issue #17 Work in Progress, size mod stat support in the works, setting temporary_scale in info struct seems to modify size, the pos_size values in the position struct are not for KoS and older clients. AddSpellBonus was translating values from float to sint32 early, now we take bonus values into player add bonus so that float values will be honored, as well as sint32. This applies to uncontested parry, block, dodge, riposte and the size mod.
9315 lines
320 KiB
C++
9315 lines
320 KiB
C++
/*
|
|
EQ2Emulator: Everquest II Server Emulator
|
|
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
|
|
|
|
This file is part of EQ2Emulator.
|
|
EQ2Emulator is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
EQ2Emulator is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "../common/debug.h"
|
|
#include <iostream>
|
|
using namespace std;
|
|
#include <string.h>
|
|
#include "../common/misc.h"
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <stdlib.h>
|
|
#include <regex>
|
|
#include <unordered_set>
|
|
#include "Commands/Commands.h"
|
|
#include "Zone/pathfinder_interface.h"
|
|
#include "NPC_AI.h"
|
|
|
|
#ifdef WIN32
|
|
#include <WinSock2.h>
|
|
#include <windows.h>
|
|
#include <dbghelp.h>
|
|
#pragma comment(lib,"imagehlp.lib")
|
|
#else
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#ifdef FREEBSD //Timothy Whitman - January 7, 2003
|
|
#include <sys/types.h>
|
|
#endif
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <stdarg.h>
|
|
|
|
#include "../common/unix.h"
|
|
|
|
#define SOCKET_ERROR -1
|
|
#define INVALID_SOCKET -1
|
|
|
|
extern int errno;
|
|
#endif
|
|
|
|
#include "../common/servertalk.h"
|
|
#include "../common/packet_dump.h"
|
|
#include "WorldDatabase.h"
|
|
#include "races.h"
|
|
#include "classes.h"
|
|
#include "../common/seperator.h"
|
|
#include "../common/EQStream.h"
|
|
#include "../common/EQStreamFactory.h"
|
|
#include "../common/opcodemgr.h"
|
|
#include "zoneserver.h"
|
|
#include "client.h"
|
|
#include "LoginServer.h"
|
|
#include "World.h"
|
|
#include <string>
|
|
#include <assert.h>
|
|
#include "LuaInterface.h"
|
|
#include "Factions.h"
|
|
#include "VisualStates.h"
|
|
#include "ClientPacketFunctions.h"
|
|
#include "SpellProcess.h"
|
|
#include "../common/Log.h"
|
|
#include "Rules/Rules.h"
|
|
#include "Chat/Chat.h"
|
|
#include "Tradeskills/Tradeskills.h"
|
|
#include "RaceTypes/RaceTypes.h"
|
|
#include <algorithm>
|
|
#include <random>
|
|
|
|
#include "Bots/Bot.h"
|
|
|
|
#ifdef WIN32
|
|
#define strncasecmp _strnicmp
|
|
#define strcasecmp _stricmp
|
|
#endif
|
|
|
|
// int32 numplayers = 0; // never used?
|
|
// extern bool GetZoneLongName(char* short_name, char** long_name); // never used?
|
|
// extern bool holdzones; // never used?
|
|
// extern volatile bool RunLoops; // never used in the zone server?
|
|
// extern Classes classes; // never used in the zone server?
|
|
|
|
#define NO_CATCH 1
|
|
|
|
extern WorldDatabase database;
|
|
extern sint32 numzones;
|
|
extern ClientList client_list;
|
|
extern LoginServer loginserver;
|
|
extern ZoneList zone_list;
|
|
extern World world;
|
|
extern ConfigReader configReader;
|
|
extern Commands commands;
|
|
extern LuaInterface* lua_interface;
|
|
extern MasterFactionList master_faction_list;
|
|
extern VisualStates visual_states;
|
|
extern RuleManager rule_manager;
|
|
extern Chat chat;
|
|
extern MasterRaceTypeList race_types_list;
|
|
extern MasterSpellList master_spell_list; // temp - remove later
|
|
extern MasterSkillList master_skill_list;
|
|
|
|
|
|
int32 MinInstanceID = 1000;
|
|
|
|
// JA: Moved most default values to Rules and risky initializers to ZoneServer::Init() - 2012.12.07
|
|
ZoneServer::ZoneServer(const char* name) {
|
|
incoming_clients = 0;
|
|
default_zone_map = nullptr;
|
|
|
|
MIncomingClients.SetName("ZoneServer::MIncomingClients");
|
|
|
|
depop_zone = false;
|
|
repop_zone = false;
|
|
respawns_allowed = true;
|
|
instanceID = 0;
|
|
strcpy(zone_name, name);
|
|
zoneID = 0;
|
|
rain = 0;
|
|
cityzone = false;
|
|
always_loaded = false;
|
|
locked = false; // JA: implementing /zone lock|unlock commands
|
|
pNumPlayers = 0;
|
|
LoadingData = true;
|
|
zoneShuttingDown = false;
|
|
++numzones;
|
|
revive_points = 0;
|
|
zone_motd = "";
|
|
finished_depop = true;
|
|
initial_spawn_threads_active = 0;
|
|
minimumStatus = 0;
|
|
minimumLevel = 0;
|
|
maximumLevel = 0;
|
|
minimumVersion = 0;
|
|
weather_current_severity = 0;
|
|
weather_signaled = false;
|
|
xp_mod = 0;
|
|
isDusk = false;
|
|
dusk_hour = 0;
|
|
dusk_minute = 0;
|
|
dawn_hour = 0;
|
|
dawn_minute = 0;
|
|
reloading_spellprocess = false;
|
|
expansion_flag = 0;
|
|
holiday_flag = 0;
|
|
can_bind = 1;
|
|
can_gate = 1;
|
|
MMasterZoneLock = new CriticalSection(MUTEX_ATTRIBUTE_RECURSIVE);
|
|
|
|
pathing = nullptr;
|
|
strcpy(zonesky_file,"");
|
|
|
|
reloading = true;
|
|
spawnthread_active = false;
|
|
movementMgr = nullptr;
|
|
spellProcess = nullptr;
|
|
tradeskillMgr = nullptr;
|
|
watchdogTimestamp = Timer::GetCurrentTime2();
|
|
|
|
MPendingSpawnRemoval.SetName("ZoneServer::MPendingSpawnRemoval");
|
|
|
|
lifetime_client_count = 0;
|
|
|
|
groupraidMinLevel = 0;
|
|
groupraidMaxLevel = 0;
|
|
groupraidAvgLevel = 0;
|
|
groupraidFirstLevel = 0;
|
|
|
|
is_initialized = false;
|
|
isInstance = false;
|
|
duplicated_zone = false;
|
|
duplicated_id = 0;
|
|
}
|
|
|
|
typedef map <int32, bool> ChangedSpawnMapType;
|
|
ZoneServer::~ZoneServer() {
|
|
zoneShuttingDown = true; //ensure other threads shut down too
|
|
//allow other threads to properly shut down
|
|
if(is_initialized) {
|
|
LogWrite(ZONE__INFO, 0, "Zone", "Initiating zone shutdown of '%s'", zone_name);
|
|
}
|
|
int32 disp_count = 0;
|
|
int32 next_disp_count = 100;
|
|
while (spawnthread_active || initial_spawn_threads_active > 0){
|
|
bool disp = false;
|
|
if ( disp_count == 0 ) {
|
|
disp = true;
|
|
}
|
|
else if(disp_count >= next_disp_count) {
|
|
disp_count = 0;
|
|
disp = true;
|
|
}
|
|
|
|
disp_count++;
|
|
if (spawnthread_active && disp)
|
|
LogWrite(ZONE__DEBUG, 7, "Zone", "Zone shutdown waiting on spawn thread");
|
|
if (initial_spawn_threads_active > 0 && disp)
|
|
LogWrite(ZONE__DEBUG, 7, "Zone", "Zone shutdown waiting on initial spawn thread");
|
|
Sleep(10);
|
|
}
|
|
|
|
MChangedSpawns.lock();
|
|
changed_spawns.clear();
|
|
MChangedSpawns.unlock();
|
|
|
|
transport_spawns.clear();
|
|
safe_delete(tradeskillMgr);
|
|
MMasterZoneLock->lock();
|
|
MMasterSpawnLock.writelock(__FUNCTION__, __LINE__);
|
|
DeleteData(true);
|
|
RemoveLocationProximities();
|
|
RemoveLocationGrids();
|
|
DeleteSpawns(true);
|
|
|
|
DeleteGlobalSpawns();
|
|
|
|
if(spellProcess)
|
|
spellProcess->RemoveAllSpells();
|
|
|
|
DeleteFlightPaths();
|
|
|
|
MMasterSpawnLock.releasewritelock(__FUNCTION__, __LINE__);
|
|
MMasterZoneLock->unlock();
|
|
world.UpdateServerStatistic(STAT_SERVER_NUM_ACTIVE_ZONES, -1);
|
|
|
|
// If lockout, public, tradeskill, or quest instance delete from db when zone shuts down
|
|
if (InstanceType == SOLO_LOCKOUT_INSTANCE || InstanceType == GROUP_LOCKOUT_INSTANCE || InstanceType == RAID_LOCKOUT_INSTANCE ||
|
|
InstanceType == PUBLIC_INSTANCE || InstanceType == TRADESKILL_INSTANCE || InstanceType == QUEST_INSTANCE) {
|
|
LogWrite(INSTANCE__DEBUG, 0, "Instance", "Non persistent instance shutting down, deleting instance");
|
|
database.DeleteInstance(instanceID);
|
|
}
|
|
|
|
if (pathing != nullptr)
|
|
delete pathing;
|
|
|
|
if (movementMgr != nullptr)
|
|
delete movementMgr;
|
|
|
|
// moved to the bottom as we want spawns deleted first, this used to be above Spawn deletion which is a big no no
|
|
safe_delete(spellProcess);
|
|
|
|
|
|
MGridMaps.lock();
|
|
std::map<int32, GridMap*>::iterator grids;
|
|
for(grids = grid_maps.begin(); grids != grid_maps.end(); grids++) {
|
|
GridMap* gm = grids->second;
|
|
safe_delete(gm);
|
|
}
|
|
grid_maps.clear();
|
|
MGridMaps.unlock();
|
|
|
|
if(is_initialized) {
|
|
LogWrite(ZONE__INFO, 0, "Zone", "Completed zone shutdown of '%s'", zone_name);
|
|
}
|
|
--numzones;
|
|
UpdateWindowTitle(0);
|
|
zone_list.Remove(this);
|
|
zone_list.RemoveClientZoneReference(this);
|
|
safe_delete(MMasterZoneLock);
|
|
}
|
|
|
|
void ZoneServer::IncrementIncomingClients() {
|
|
MIncomingClients.writelock(__FUNCTION__, __LINE__);
|
|
incoming_clients++;
|
|
LogWrite(ZONE__INFO, 0, "Zone", "Increment incoming clients of '%s' zoneid %u (instance id: %u). Current incoming client count: %u", zone_name, zoneID, instanceID, incoming_clients);
|
|
MIncomingClients.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::DecrementIncomingClients() {
|
|
MIncomingClients.writelock(__FUNCTION__, __LINE__);
|
|
bool zeroed = false;
|
|
if(incoming_clients)
|
|
incoming_clients--;
|
|
else
|
|
zeroed = true;
|
|
LogWrite(ZONE__INFO, 0, "Zone", "Decrement incoming clients of '%s' zoneid %u (instance id: %u). Current incoming client count: %u (was client count previously zero: %u)", zone_name, zoneID, instanceID, incoming_clients, zeroed);
|
|
MIncomingClients.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::Init()
|
|
{
|
|
LogWrite(ZONE__INFO, 0, "Zone", "Loading new Zone '%s'", zone_name);
|
|
zone_list.Add(this);
|
|
|
|
is_initialized = true;
|
|
|
|
spellProcess = new SpellProcess();
|
|
tradeskillMgr = new TradeskillMgr();
|
|
|
|
/* Dynamic Timers */
|
|
regenTimer.Start(rule_manager.GetGlobalRule(R_Zone, RegenTimer)->GetInt32());
|
|
client_save.Start(rule_manager.GetGlobalRule(R_Zone, ClientSaveTimer)->GetInt32());
|
|
shutdownTimer.Disable();
|
|
spawn_range.Start(rule_manager.GetGlobalRule(R_Zone, CheckAttackPlayer)->GetInt32());
|
|
aggro_timer.Start(rule_manager.GetGlobalRule(R_Zone, CheckAttackNPC)->GetInt32());
|
|
/* Weather stuff */
|
|
InitWeather();
|
|
|
|
/* Static Timers */
|
|
// JA - haven't decided yet if these should remain hard-coded. Changing them could break EQ2Emu functionality
|
|
spawn_check_add.Start(1000);
|
|
spawn_check_remove.Start(30000);
|
|
spawn_expire_timer.Start(10000);
|
|
respawn_timer.Start(10000);
|
|
// there was never a starter for these?
|
|
widget_timer.Start(5000);
|
|
|
|
tracking_timer.Start(5000);
|
|
|
|
movement_timer.Start(100);
|
|
location_prox_timer.Start(1000);
|
|
location_grid_timer.Start(1000);
|
|
|
|
charsheet_changes.Start(500);
|
|
|
|
// Send game time packet every in game hour (180 sec)
|
|
sync_game_time_timer.Start(180000);
|
|
|
|
// Get the dusk and dawn time from the rule manager and store it in the correct variables
|
|
sscanf (rule_manager.GetGlobalRule(R_World, DuskTime)->GetString(), "%d:%d", &dusk_hour, &dusk_minute);
|
|
sscanf (rule_manager.GetGlobalRule(R_World, DawnTime)->GetString(), "%d:%d", &dawn_hour, &dawn_minute);
|
|
|
|
spawn_update.Start(rule_manager.GetGlobalRule(R_Zone, SpawnUpdateTimer)->GetInt16());
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "SpawnUpdateTimer: %ims", rule_manager.GetGlobalRule(R_Zone, SpawnUpdateTimer)->GetInt16());
|
|
|
|
queue_updates.Start(rule_manager.GetGlobalRule(R_Zone, SpawnUpdateTimer)->GetInt16());
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "QueueUpdateTimer(inherits SpawnUpdateTimer): %ims", rule_manager.GetGlobalRule(R_Zone, SpawnUpdateTimer)->GetInt16());
|
|
|
|
spawn_delete_timer = rule_manager.GetGlobalRule(R_Zone, SpawnDeleteTimer)->GetInt32();
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "SpawnDeleteTimer: %ums", spawn_delete_timer);
|
|
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Loading zone flight paths");
|
|
database.LoadZoneFlightPaths(this);
|
|
|
|
world.UpdateServerStatistic(STAT_SERVER_NUM_ACTIVE_ZONES, 1);
|
|
UpdateWindowTitle(0);
|
|
|
|
string zoneName(GetZoneFile());
|
|
|
|
world.LoadRegionMaps(zoneName);
|
|
|
|
world.LoadMaps(zoneName);
|
|
|
|
pathing = IPathfinder::Load(zoneName);
|
|
movementMgr = new MobMovementManager();
|
|
|
|
if(GetInstanceID()) {
|
|
PlayerHouse* ph = world.GetPlayerHouseByInstanceID(GetInstanceID());
|
|
if(ph) {
|
|
HouseZone* hz = world.GetHouseZone(ph->house_id);
|
|
if(hz) {
|
|
std::string desc = ph->player_name + "'s " + hz->name;
|
|
SetZoneDescription((char*)desc.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
MMasterSpawnLock.SetName("ZoneServer::MMasterSpawnLock");
|
|
m_npc_faction_list.SetName("ZoneServer::npc_faction_list");
|
|
m_enemy_faction_list.SetName("ZoneServer::enemy_faction_list");
|
|
m_reverse_enemy_faction_list.SetName("ZoneServer::reverse_enemy_faction_list");
|
|
MDeadSpawns.SetName("ZoneServer::dead_spawns");
|
|
MTransportSpawns.SetName("ZoneServer::transport_spawns");
|
|
MSpawnList.SetName("ZoneServer::spawn_list");
|
|
MTransporters.SetName("ZoneServer::m_transportMaps");
|
|
MSpawnGroupAssociation.SetName("ZoneServer::spawn_group_associations");
|
|
MSpawnGroupLocations.SetName("ZoneServer::spawn_group_locations");
|
|
MSpawnLocationGroups.SetName("ZoneServer::spawn_location_groups");
|
|
MSpawnGroupChances.SetName("ZoneServer::spawn_group_chances");
|
|
MTransportLocations.SetName("ZoneServer::transporter_locations");
|
|
MSpawnLocationList.SetName("ZoneServer::spawn_location_list");
|
|
MSpawnDeleteList.SetName("ZoneServer::spawn_delete_list");
|
|
MSpawnScriptTimers.SetName("ZoneServer::spawn_script_timers");
|
|
MRemoveSpawnScriptTimersList.SetName("ZoneServer::remove_spawn_script_timers_list");
|
|
MClientList.SetName("ZoneServer::clients");
|
|
MWidgetTimers.SetName("ZoneServer::widget_timers");
|
|
#ifdef WIN32
|
|
_beginthread(ZoneLoop, 0, this);
|
|
_beginthread(SpawnLoop, 0, this);
|
|
#else
|
|
pthread_create(&ZoneThread, NULL, ZoneLoop, this);
|
|
pthread_detach(ZoneThread);
|
|
pthread_create(&SpawnThread, NULL, SpawnLoop, this);
|
|
pthread_detach(SpawnThread);
|
|
#endif
|
|
}
|
|
|
|
void ZoneServer::CancelThreads() {
|
|
#ifdef WIN32
|
|
LogWrite(WORLD__ERROR, 1, "World", "Zone %s is hung, however CancelThreads is unsupported for WIN32.", GetZoneName());
|
|
#else
|
|
pthread_cancel(ZoneThread);
|
|
pthread_cancel(SpawnThread);
|
|
#endif
|
|
}
|
|
|
|
void ZoneServer::InitWeather()
|
|
{
|
|
weather_enabled = rule_manager.GetZoneRule(GetZoneID(), R_Zone, WeatherEnabled)->GetBool();
|
|
if( weather_enabled && isWeatherAllowed())
|
|
{
|
|
string tmp;
|
|
// set up weather system when zone starts up
|
|
weather_type = rule_manager.GetZoneRule(GetZoneID(), R_Zone, WeatherType)->GetInt8();
|
|
switch(weather_type)
|
|
{
|
|
case 3: tmp = "Chaotic"; break;
|
|
case 2: tmp = "Random"; break;
|
|
case 1: tmp = "Dynamic"; break;
|
|
default: tmp = "Normal"; break;
|
|
}
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "%s: Setting up '%s' weather", zone_name, tmp.c_str());
|
|
|
|
weather_frequency = rule_manager.GetZoneRule(GetZoneID(), R_Zone, WeatherChangeFrequency)->GetInt32();
|
|
LogWrite(ZONE__DEBUG, 1, "Zone", "%s: Change weather every %u seconds", zone_name, weather_frequency);
|
|
|
|
weather_change_chance = rule_manager.GetZoneRule(GetZoneID(), R_Zone, WeatherChangeChance)->GetInt8();
|
|
LogWrite(ZONE__DEBUG, 1, "Zone", "%s: Chance of weather change: %i%%", zone_name, weather_change_chance);
|
|
|
|
weather_min_severity = rule_manager.GetZoneRule(GetZoneID(), R_Zone, MinWeatherSeverity)->GetFloat();
|
|
weather_max_severity = rule_manager.GetZoneRule(GetZoneID(), R_Zone, MaxWeatherSeverity)->GetFloat();
|
|
LogWrite(ZONE__DEBUG, 1, "Zone", "%s: Weather Severity min/max is %.2f - %.2f", zone_name, weather_min_severity, weather_max_severity);
|
|
// Allow a random roll to determine if weather should start out severe or calm
|
|
if( MakeRandomInt(1, 100) > 50)
|
|
{
|
|
weather_pattern = 1; // default weather to increase in severity initially
|
|
weather_current_severity = weather_min_severity;
|
|
}
|
|
else
|
|
{
|
|
weather_pattern = 0; // default weather to decrease in severity initially
|
|
weather_current_severity = weather_max_severity;
|
|
}
|
|
LogWrite(ZONE__DEBUG, 1, "Zone", "%s: Weather Severity set to %.2f, pattern: %i", zone_name, weather_current_severity, weather_pattern);
|
|
|
|
weather_change_amount = rule_manager.GetZoneRule(GetZoneID(), R_Zone, WeatherChangePerInterval)->GetFloat();
|
|
LogWrite(ZONE__DEBUG, 1, "Zone", "%s: Weather change by %.2f each interval", zone_name, weather_change_amount);
|
|
|
|
if( weather_type > 0 )
|
|
{
|
|
weather_dynamic_offset = rule_manager.GetZoneRule(GetZoneID(), R_Zone, WeatherDynamicMaxOffset)->GetFloat();
|
|
LogWrite(ZONE__DEBUG, 1, "Zone", "%s: Weather Max Offset changes no more than %.2f each interval", zone_name, weather_dynamic_offset);
|
|
}
|
|
else
|
|
weather_dynamic_offset = 0;
|
|
|
|
SetRain(weather_current_severity);
|
|
weather_last_changed_time = Timer::GetUnixTimeStamp();
|
|
weatherTimer.Start(rule_manager.GetZoneRule(GetZoneID(), R_Zone, WeatherTimer)->GetInt32());
|
|
}
|
|
}
|
|
void ZoneServer::DeleteSpellProcess(){
|
|
//Just get a lock to make sure we aren't already looping the spawnprocess or clientprocess if this is different than the calling thread
|
|
MMasterSpawnLock.writelock(__FUNCTION__, __LINE__);
|
|
MMasterZoneLock->lock();
|
|
reloading_spellprocess = true;
|
|
// Remove spells from NPC's
|
|
Spawn* spawn = 0;
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if(spawn && spawn->IsNPC())
|
|
((NPC*)spawn)->SetSpells(0);
|
|
|
|
if(spawn->IsEntity()) {
|
|
((Entity*)spawn)->RemoveSpellBonus(nullptr, true);
|
|
((Entity*)spawn)->DeleteSpellEffects(true);
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
MMasterZoneLock->unlock();
|
|
MMasterSpawnLock.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
DismissAllPets();
|
|
spellProcess->RemoveAllSpells(true);
|
|
safe_delete(spellProcess);
|
|
}
|
|
|
|
void ZoneServer::LoadSpellProcess(){
|
|
spellProcess = new SpellProcess();
|
|
reloading_spellprocess = false;
|
|
|
|
// Reload NPC's spells
|
|
Spawn* spawn = 0;
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if(spawn && spawn->IsNPC())
|
|
((NPC*)spawn)->SetSpells(world.GetNPCSpells(((NPC*)spawn)->GetPrimarySpellList(), ((NPC*)spawn)->GetSecondarySpellList()));
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::LockAllSpells(Player* player) {
|
|
if (player && spellProcess) {
|
|
Client* client = ((Player*)player)->GetClient();
|
|
if (client)
|
|
spellProcess->LockAllSpells(client);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::UnlockAllSpells(Player* player) {
|
|
if (player && spellProcess) {
|
|
Client* client = ((Player*)player)->GetClient();
|
|
if (client)
|
|
spellProcess->UnlockAllSpells(client);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::DeleteFactionLists() {
|
|
map<int32, vector<int32> *>::iterator faction_itr;
|
|
map<int32, vector<int32> *>::iterator spawn_itr;
|
|
|
|
m_enemy_faction_list.writelock(__FUNCTION__, __LINE__);
|
|
for (faction_itr = enemy_faction_list.begin(); faction_itr != enemy_faction_list.end(); faction_itr++)
|
|
safe_delete(faction_itr->second);
|
|
enemy_faction_list.clear();
|
|
m_enemy_faction_list.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
m_reverse_enemy_faction_list.writelock(__FUNCTION__, __LINE__);
|
|
for (faction_itr = reverse_enemy_faction_list.begin(); faction_itr != reverse_enemy_faction_list.end(); faction_itr++)
|
|
safe_delete(faction_itr->second);
|
|
reverse_enemy_faction_list.clear();
|
|
m_reverse_enemy_faction_list.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
m_npc_faction_list.writelock(__FUNCTION__, __LINE__);
|
|
for (spawn_itr = npc_faction_list.begin(); spawn_itr != npc_faction_list.end(); spawn_itr++)
|
|
safe_delete(spawn_itr->second);
|
|
npc_faction_list.clear();
|
|
m_npc_faction_list.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::DeleteData(bool boot_clients){
|
|
Spawn* spawn = 0;
|
|
vector<Spawn*> tmp_player_list; // changed to a vector from a MutexList as this is a local variable and don't need mutex stuff for the list
|
|
|
|
// Clear spawn groups
|
|
spawn_group_map.clear();
|
|
|
|
// Loop through the spawn list and set the spawn for deletion
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if(spawn){
|
|
if(!boot_clients && (spawn->IsPlayer() || spawn->IsBot()))
|
|
tmp_player_list.push_back(spawn);
|
|
else if(spawn->IsPlayer()){
|
|
Client* client = ((Player*)spawn)->GetClient();
|
|
if(client)
|
|
client->Disconnect();
|
|
}
|
|
else{
|
|
RemoveSpawnSupportFunctions(spawn, boot_clients, true);
|
|
RemoveSpawnFromGrid(spawn, spawn->GetLocation());
|
|
AddPendingDelete(spawn);
|
|
}
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
// Quick hack to prevent a deadlock, RemoveSpawnSupportFunctions() will cancel spells and result in zone->GetSpawnByID()
|
|
// being called which read locks the spawn list and caused a dead lock as the above mutex's were write locked
|
|
MSpawnList.writelock(__FUNCTION__, __LINE__);
|
|
// Clear the spawn list, this was in the mutex above, moved it down so the above mutex could be a read lock
|
|
spawn_list.clear();
|
|
|
|
// Moved this up so we only read lock the list once in this list
|
|
vector<Spawn*>::iterator spawn_iter2;
|
|
for (spawn_iter2 = tmp_player_list.begin(); spawn_iter2 != tmp_player_list.end(); spawn_iter2++) {
|
|
spawn_list[(*spawn_iter2)->GetID()] = (*spawn_iter2);
|
|
}
|
|
MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
// Clear player proximities
|
|
RemovePlayerProximity(0, true);
|
|
|
|
spawn_range_map.clear(true);
|
|
if(boot_clients) {
|
|
// Refactor
|
|
vector<Client*>::iterator itr;
|
|
|
|
MClientList.writelock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++)
|
|
safe_delete(*itr);
|
|
|
|
clients.clear();
|
|
MClientList.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Clear and delete spawn locations
|
|
MSpawnLocationList.writelock(__FUNCTION__, __LINE__);
|
|
map<int32, SpawnLocation*>::iterator spawn_location_iter;
|
|
for (spawn_location_iter = spawn_location_list.begin(); spawn_location_iter != spawn_location_list.end(); spawn_location_iter++)
|
|
safe_delete(spawn_location_iter->second);
|
|
|
|
spawn_location_list.clear();
|
|
MSpawnLocationList.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
// If we allow clients to stay in the zone we need to preserve the revive_points, otherwise if the player dies they will crash
|
|
if(revive_points && boot_clients){
|
|
vector<RevivePoint*>::iterator revive_iter;
|
|
for(revive_iter=revive_points->begin(); revive_iter != revive_points->end(); revive_iter++){
|
|
safe_delete(*revive_iter);
|
|
}
|
|
safe_delete(revive_points);
|
|
}
|
|
|
|
MSpawnGroupAssociation.writelock(__FUNCTION__, __LINE__);
|
|
map<int32, set<int32>*>::iterator assoc_itr;
|
|
for (assoc_itr = spawn_group_associations.begin(); assoc_itr != spawn_group_associations.end(); assoc_itr++)
|
|
safe_delete(assoc_itr->second);
|
|
|
|
spawn_group_associations.clear();
|
|
MSpawnGroupAssociation.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
MSpawnGroupLocations.writelock(__FUNCTION__, __LINE__);
|
|
map<int32, map<int32, int32>*>::iterator loc_itr;
|
|
for (loc_itr = spawn_group_locations.begin(); loc_itr != spawn_group_locations.end(); loc_itr++)
|
|
safe_delete(loc_itr->second);
|
|
|
|
spawn_group_locations.clear();
|
|
MSpawnGroupLocations.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
MSpawnLocationGroups.writelock(__FUNCTION__, __LINE__);
|
|
map<int32, list<int32>*>::iterator group_itr;
|
|
for (group_itr = spawn_location_groups.begin(); group_itr != spawn_location_groups.end(); group_itr++)
|
|
safe_delete(group_itr->second);
|
|
|
|
spawn_location_groups.clear();
|
|
MSpawnLocationGroups.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
// Clear lists that need more then just a Clear()
|
|
DeleteFactionLists();
|
|
DeleteSpawnScriptTimers(0, true);
|
|
DeleteSpawnScriptTimers();
|
|
ClearDeadSpawns();
|
|
|
|
// Clear lists
|
|
movement_spawns.clear();
|
|
respawn_timers.clear();
|
|
transport_spawns.clear();
|
|
quick_database_id_lookup.clear();
|
|
|
|
MWidgetTimers.writelock(__FUNCTION__, __LINE__);
|
|
widget_timers.clear();
|
|
MWidgetTimers.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
map<int16, PacketStruct*>::iterator struct_itr;
|
|
for (struct_itr = versioned_info_structs.begin(); struct_itr != versioned_info_structs.end(); struct_itr++)
|
|
safe_delete(struct_itr->second);
|
|
versioned_info_structs.clear();
|
|
|
|
for (struct_itr = versioned_pos_structs.begin(); struct_itr != versioned_pos_structs.end(); struct_itr++)
|
|
safe_delete(struct_itr->second);
|
|
versioned_pos_structs.clear();
|
|
|
|
for (struct_itr = versioned_vis_structs.begin(); struct_itr != versioned_vis_structs.end(); struct_itr++)
|
|
safe_delete(struct_itr->second);
|
|
versioned_vis_structs.clear();
|
|
}
|
|
|
|
void ZoneServer::RemoveLocationProximities() {
|
|
MutexList<LocationProximity*>::iterator itr = location_proximities.begin();
|
|
while(itr.Next()){
|
|
safe_delete(itr->value);
|
|
}
|
|
location_proximities.clear();
|
|
}
|
|
|
|
RevivePoint* ZoneServer::GetRevivePoint(int32 id){
|
|
vector<RevivePoint*>::iterator revive_iter;
|
|
for(revive_iter=revive_points->begin(); revive_iter != revive_points->end(); revive_iter++){
|
|
if((*revive_iter)->id == id)
|
|
return *revive_iter;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
vector<RevivePoint*>* ZoneServer::GetRevivePoints(Client* client)
|
|
{
|
|
vector<RevivePoint*>* points = new vector<RevivePoint*>;
|
|
RevivePoint* closest_point = 0;
|
|
|
|
// we should not check for revive points if this is null
|
|
if ( revive_points != NULL )
|
|
{
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Got revive point in %s!", __FUNCTION__);
|
|
|
|
float closest = 100000;
|
|
float test_closest = 0;
|
|
RevivePoint* test_point = 0;
|
|
vector<RevivePoint*>::iterator revive_iter;
|
|
for(revive_iter=revive_points->begin(); revive_iter != revive_points->end(); revive_iter++)
|
|
{
|
|
test_point = *revive_iter;
|
|
if(test_point)
|
|
{
|
|
test_closest = client->GetPlayer()->GetDistance(test_point->x, test_point->y, test_point->z);
|
|
|
|
// should this be changed to list all revive points within max distance or just the closest
|
|
if(test_closest < closest)
|
|
{
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "test_closest: %.2f, closest: %.2f", test_closest, closest);
|
|
closest = test_closest;
|
|
closest_point = test_point;
|
|
}
|
|
if(test_point->always_included ) {
|
|
points->push_back(test_point);
|
|
if(closest_point == test_point) {
|
|
closest_point = nullptr;
|
|
closest = 100000;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(closest_point) {
|
|
points->push_back(closest_point);
|
|
}
|
|
}
|
|
|
|
if(closest_point && points->size() == 0 && closest_point->zone_id == GetZoneID())
|
|
{
|
|
LogWrite(ZONE__WARNING, 0, "Zone", "Nearest Revive Point too far away. Add another!");
|
|
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "The closest revive point is quite far away, you might want to ask the server admin for a closer one.");
|
|
points->push_back(closest_point);
|
|
}
|
|
else if(points->size() == 0)
|
|
{
|
|
LogWrite(ZONE__WARNING, 0, "Zone", "No Revive Points set for zoneID %u. Add some!", GetZoneID());
|
|
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "There are no revive points for this zone, you might want to ask the server admin for one.");
|
|
closest_point = new RevivePoint;
|
|
closest_point->heading = GetSafeHeading();
|
|
closest_point->id = 0xFFFFFFFF;
|
|
closest_point->location_name = "Zone Safe Point";
|
|
closest_point->zone_id = GetZoneID();
|
|
closest_point->x = GetSafeX();
|
|
closest_point->y = GetSafeY();
|
|
closest_point->z = GetSafeZ();
|
|
closest_point->always_included = true;
|
|
points->push_back(closest_point);
|
|
}
|
|
return points;
|
|
}
|
|
|
|
void ZoneServer::TriggerCharSheetTimer(){
|
|
charsheet_changes.Trigger();
|
|
}
|
|
|
|
void ZoneServer::RegenUpdate(){
|
|
if(damaged_spawns.size(true) == 0)
|
|
return;
|
|
|
|
Spawn* spawn = 0;
|
|
MutexList<int32>::iterator spawn_iter = damaged_spawns.begin();
|
|
while(spawn_iter.Next()){
|
|
spawn = GetSpawnByID(spawn_iter->value);
|
|
if(spawn && (((spawn->GetHP() < spawn->GetTotalHP()) && spawn->GetHP()>0) || (spawn->GetPower() < spawn->GetTotalPower()))){
|
|
if(spawn->IsEntity())
|
|
((Entity*)spawn)->DoRegenUpdate();
|
|
if(spawn->IsPlayer()){
|
|
Client* client = ((Player*)spawn)->GetClient();
|
|
if(client && client->IsReadyForUpdates())
|
|
client->QueuePacket(client->GetPlayer()->GetPlayerInfo()->serialize(client->GetVersion()));
|
|
}
|
|
}
|
|
else
|
|
RemoveDamagedSpawn(spawn);
|
|
//Spawn no longer valid, remove it from the list
|
|
if (!spawn)
|
|
damaged_spawns.Remove(spawn_iter->value);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::ClearDeadSpawns(){
|
|
MDeadSpawns.writelock(__FUNCTION__, __LINE__);
|
|
dead_spawns.clear();
|
|
MDeadSpawns.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::ProcessDepop(bool respawns_allowed, bool repop) {
|
|
vector<Client*>::iterator client_itr;
|
|
Client* client = 0;
|
|
Spawn* spawn = 0;
|
|
PacketStruct* packet = 0;
|
|
int16 packet_version = 0;
|
|
spawn_expire_timers.clear();
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client)
|
|
continue;
|
|
client->GetPlayer()->SetTarget(0);
|
|
if(repop)
|
|
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Zone Repop in progress...");
|
|
else{
|
|
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Zone Depop in progress...");
|
|
if(respawns_allowed)
|
|
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Spawns will respawn according to their respawn timers.");
|
|
}
|
|
if(!packet || packet_version != client->GetVersion()){
|
|
safe_delete(packet);
|
|
packet_version = client->GetVersion();
|
|
packet = configReader.getStruct("WS_DestroyGhostCmd", packet_version);
|
|
}
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if(spawn && !spawn->IsPlayer() && !spawn->IsBot()){
|
|
bool dispatched = false;
|
|
if(spawn->IsPet())
|
|
{
|
|
Entity* owner = ((Entity*)spawn)->GetOwner();
|
|
if(owner)
|
|
{
|
|
owner->DismissPet((Entity*)spawn);
|
|
dispatched = true;
|
|
}
|
|
}
|
|
|
|
spawn->SetDeletedSpawn(true);
|
|
|
|
if(!dispatched)
|
|
SendRemoveSpawn(client, spawn, packet);
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
DeleteTransporters();
|
|
safe_delete(packet);
|
|
if(!repop && respawns_allowed){
|
|
spawn_range_map.clear(true);
|
|
MutexList<Spawn*> tmp_player_list; // Local variable, never be on another thread so probably don't need the extra mutex code that comes with a MutexList
|
|
ClearDeadSpawns();
|
|
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.writelock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if (spawn) {
|
|
if(spawn->GetRespawnTime() > 0 && spawn->GetSpawnLocationID() > 0)
|
|
respawn_timers.Put(spawn->GetSpawnLocationID(), Timer::GetCurrentTime2() + spawn->GetRespawnTime()*1000);
|
|
if(spawn->IsPlayer() || spawn->IsBot())
|
|
tmp_player_list.Add(spawn);
|
|
else {
|
|
RemoveSpawnSupportFunctions(spawn, true);
|
|
RemoveSpawnFromGrid(spawn, spawn->GetLocation());
|
|
AddPendingDelete(spawn);
|
|
}
|
|
}
|
|
}
|
|
spawn_list.clear();
|
|
//add back just the clients
|
|
MutexList<Spawn*>::iterator spawn_iter2 = tmp_player_list.begin();
|
|
while(spawn_iter2.Next()) {
|
|
spawn_list[spawn_iter2->value->GetID()] = spawn_iter2->value;
|
|
}
|
|
MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
else {
|
|
DeleteData(false);
|
|
}
|
|
|
|
if(repop)
|
|
{
|
|
// reload spirit shards for the current zone
|
|
database.LoadSpiritShards(this);
|
|
|
|
LoadingData = true;
|
|
}
|
|
}
|
|
|
|
void ZoneServer::Depop(bool respawns, bool repop) {
|
|
respawns_allowed = respawns;
|
|
repop_zone = repop;
|
|
finished_depop = false;
|
|
depop_zone = true;
|
|
}
|
|
|
|
bool ZoneServer::AddCloseSpawnsToSpawnGroup(Spawn* spawn, float radius){
|
|
if(!spawn)
|
|
return false;
|
|
|
|
Spawn* close_spawn = 0;
|
|
bool ret = true;
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
close_spawn = itr->second;
|
|
if(close_spawn && close_spawn != spawn && !close_spawn->IsPlayer() && close_spawn->GetDistance(spawn) <= radius){
|
|
if((spawn->IsNPC() && close_spawn->IsNPC()) || (spawn->IsGroundSpawn() && close_spawn->IsGroundSpawn()) || (spawn->IsObject() && close_spawn->IsObject()) || (spawn->IsWidget() && close_spawn->IsWidget()) || (spawn->IsSign() && close_spawn->IsSign())){
|
|
if(close_spawn->GetSpawnGroupID() == 0){
|
|
spawn->AddSpawnToGroup(close_spawn);
|
|
close_spawn->AddSpawnToGroup(spawn);
|
|
}
|
|
else
|
|
ret = false;
|
|
}
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::RepopSpawns(Client* client, Spawn* in_spawn){
|
|
vector<Spawn*>* spawns = in_spawn->GetSpawnGroup();
|
|
PacketStruct* packet = configReader.getStruct("WS_DestroyGhostCmd", client->GetVersion());
|
|
if(spawns){
|
|
if(!packet)
|
|
return;
|
|
|
|
Spawn* spawn = 0;
|
|
vector<Spawn*>::iterator itr;
|
|
for(itr = spawns->begin(); itr != spawns->end(); itr++){
|
|
spawn = *itr;
|
|
spawn->SetDeletedSpawn(true);
|
|
SendRemoveSpawn(client, spawn, packet);
|
|
}
|
|
}
|
|
safe_delete(spawns);
|
|
if(in_spawn)
|
|
in_spawn->SetDeletedSpawn(true);
|
|
|
|
SendRemoveSpawn(client, in_spawn, packet);
|
|
spawn_check_add.Trigger();
|
|
safe_delete(packet);
|
|
}
|
|
|
|
bool ZoneServer::AggroVictim(NPC* npc, Spawn* victim, Client* client)
|
|
{
|
|
bool isEntity = victim->IsEntity();
|
|
if(isEntity && !npc->AttackAllowed((Entity*)victim))
|
|
return false;
|
|
|
|
victim->CheckEncounterState((Entity*)npc, true);
|
|
|
|
if (npc->HasSpawnGroup()) {
|
|
vector<Spawn*>* groupVec = npc->GetSpawnGroup();
|
|
for (int32 i = 0; i < groupVec->size(); i++) {
|
|
Spawn* group_member = groupVec->at(i);
|
|
if (group_member && !group_member->EngagedInCombat() && group_member->Alive()) {
|
|
CallSpawnScript(group_member, SPAWN_SCRIPT_AGGRO, victim);
|
|
if (isEntity)
|
|
((NPC*)group_member)->AddHate((Entity*)victim, 50);
|
|
else
|
|
((NPC*)group_member)->InCombat(true);
|
|
}
|
|
}
|
|
safe_delete(groupVec);
|
|
}
|
|
else
|
|
{
|
|
if (isEntity)
|
|
{
|
|
CallSpawnScript(victim, SPAWN_SCRIPT_AGGRO, victim);
|
|
npc->AddHate((Entity*)victim, 50);
|
|
}
|
|
else
|
|
npc->InCombat(true);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ZoneServer::CheckNPCAttacks(NPC* npc, Spawn* victim, Client* client){
|
|
if(!npc || !victim)
|
|
return true;
|
|
|
|
if (client) {
|
|
int8 arrow = 0;
|
|
if (client->IsReadyForUpdates() && npc->CanSeeInvis(client->GetPlayer()) && client->GetPlayer()->GetFactions()->ShouldAttack(npc->GetFactionID()) && npc->AttackAllowed((Entity*)victim, false)) {
|
|
if (!npc->EngagedInCombat()) {
|
|
if(client->GetPlayer()->GetArrowColor(npc->GetLevel()) != ARROW_COLOR_GRAY) {
|
|
AggroVictim(npc, victim, client);
|
|
}
|
|
else if(npc->IsScaredByStrongPlayers() &&
|
|
!client->GetPlayer()->IsSpawnInRangeList(npc->GetID())) {
|
|
SendSpawnChanges(npc, client, true, true);
|
|
client->GetPlayer()->SetSpawnInRangeList(npc->GetID(), true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
AggroVictim(npc, victim, client);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ZoneServer::CheckEnemyList(NPC* npc) {
|
|
vector<int32> *factions;
|
|
vector<int32>::iterator faction_itr;
|
|
vector<int32> *spawns;
|
|
vector<int32>::iterator spawn_itr;
|
|
map<float, Spawn*> attack_spawns;
|
|
map<float, Spawn*> reverse_attack_spawns;
|
|
map<float, Spawn*>::iterator itr;
|
|
int32 faction_id = npc->GetFactionID();
|
|
float distance;
|
|
|
|
if (faction_id == 0)
|
|
return true;
|
|
|
|
m_enemy_faction_list.readlock(__FUNCTION__, __LINE__);
|
|
if (enemy_faction_list.count(faction_id) > 0) {
|
|
factions = enemy_faction_list[faction_id];
|
|
|
|
for (faction_itr = factions->begin(); faction_itr != factions->end(); faction_itr++) {
|
|
m_npc_faction_list.readlock(__FUNCTION__, __LINE__);
|
|
if (npc_faction_list.count(*faction_itr) > 0) {
|
|
spawns = npc_faction_list[*faction_itr];
|
|
spawn_itr = spawns->begin();
|
|
|
|
for (spawn_itr = spawns->begin(); spawn_itr != spawns->end(); spawn_itr++) {
|
|
Spawn* spawn = GetSpawnByID(*spawn_itr);
|
|
if (spawn) {
|
|
if ((!npc->IsPrivateSpawn() || npc->AllowedAccess(spawn)) && (distance = spawn->GetDistance(npc)) <= npc->GetAggroRadius() && npc->CheckLoS(spawn))
|
|
attack_spawns[distance] = spawn;
|
|
}
|
|
}
|
|
}
|
|
m_npc_faction_list.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
m_enemy_faction_list.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
m_reverse_enemy_faction_list.readlock(__FUNCTION__, __LINE__);
|
|
if (reverse_enemy_faction_list.count(faction_id) > 0) {
|
|
factions = reverse_enemy_faction_list[faction_id];
|
|
|
|
for (faction_itr = factions->begin(); faction_itr != factions->end(); faction_itr++) {
|
|
m_npc_faction_list.readlock(__FUNCTION__, __LINE__);
|
|
if (npc_faction_list.count(*faction_itr) > 0) {
|
|
spawns = npc_faction_list[*faction_itr];
|
|
spawn_itr = spawns->begin();
|
|
|
|
for (spawn_itr = spawns->begin(); spawn_itr != spawns->end(); spawn_itr++) {
|
|
Spawn* spawn = GetSpawnByID(*spawn_itr);
|
|
if (spawn) {
|
|
if ((!npc->IsPrivateSpawn() || npc->AllowedAccess(spawn)) && (distance = spawn->GetDistance(npc)) <= npc->GetAggroRadius() && npc->CheckLoS(spawn))
|
|
reverse_attack_spawns[distance] = spawn;
|
|
}
|
|
}
|
|
}
|
|
m_npc_faction_list.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
m_reverse_enemy_faction_list.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
if (attack_spawns.size() > 0) {
|
|
for (itr = attack_spawns.begin(); itr != attack_spawns.end(); itr++)
|
|
CheckNPCAttacks(npc, itr->second);
|
|
}
|
|
if (reverse_attack_spawns.size() > 0) {
|
|
for (itr = reverse_attack_spawns.begin(); itr != reverse_attack_spawns.end(); itr++)
|
|
CheckNPCAttacks((NPC*)itr->second, npc);
|
|
}
|
|
|
|
return attack_spawns.size() == 0;
|
|
}
|
|
|
|
void ZoneServer::RemoveDeadEnemyList(Spawn *spawn)
|
|
{
|
|
int32 faction_id = spawn->GetFactionID();
|
|
vector<int32> *spawns;
|
|
vector<int32>::iterator itr;
|
|
|
|
m_npc_faction_list.writelock(__FUNCTION__, __LINE__);
|
|
if (npc_faction_list.count(faction_id) > 0) {
|
|
spawns = npc_faction_list[faction_id];
|
|
|
|
for (itr = spawns->begin(); itr != spawns->end(); itr++) {
|
|
if (*itr == spawn->GetID()) {
|
|
spawns->erase(itr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
m_npc_faction_list.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::AddEnemyList(NPC* npc){
|
|
int32 faction_id = npc->GetFactionID();
|
|
vector<int32> *hostile_factions;
|
|
vector<int32>::iterator itr;
|
|
|
|
if(faction_id <= 9)
|
|
return;
|
|
|
|
if(!rule_manager.GetZoneRule(GetZoneID(), R_Faction, AllowFactionBasedCombat)->GetBool()) {
|
|
LogWrite(FACTION__WARNING, 0, "Faction", "Faction Combat is DISABLED via R_Faction::AllowFactionBasedCombat rule!");
|
|
return;
|
|
}
|
|
|
|
m_npc_faction_list.readlock(__FUNCTION__, __LINE__);
|
|
if (npc_faction_list.count(faction_id) == 0) {
|
|
if(faction_id > 10) {
|
|
if ((hostile_factions = master_faction_list.GetHostileFactions(faction_id)) != NULL) {
|
|
itr = hostile_factions->begin();
|
|
|
|
for (itr = hostile_factions->begin(); itr != hostile_factions->end(); itr++) {
|
|
m_enemy_faction_list.writelock(__FUNCTION__, __LINE__);
|
|
if (enemy_faction_list.count(faction_id) == 0)
|
|
enemy_faction_list[faction_id] = new vector<int32>;
|
|
enemy_faction_list[faction_id]->push_back(*itr);
|
|
m_enemy_faction_list.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
m_reverse_enemy_faction_list.writelock(__FUNCTION__, __LINE__);
|
|
if(reverse_enemy_faction_list.count(*itr) == 0)
|
|
reverse_enemy_faction_list[*itr] = new vector<int32>;
|
|
reverse_enemy_faction_list[*itr]->push_back(faction_id);
|
|
m_reverse_enemy_faction_list.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*m_enemy_faction_list.writelock(__FUNCTION__, __LINE__);
|
|
if(enemy_faction_list.count(1) == 0)
|
|
enemy_faction_list[1] = new vector<int32>;
|
|
enemy_faction_list[1]->push_back(faction_id);
|
|
m_enemy_faction_list.releasewritelock(__FUNCTION__, __LINE__);*/
|
|
}
|
|
m_npc_faction_list.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
m_npc_faction_list.writelock(__FUNCTION__, __LINE__);
|
|
if(npc_faction_list.count(faction_id) == 0)
|
|
npc_faction_list[faction_id] = new vector<int32>;
|
|
npc_faction_list[faction_id]->push_back(npc->GetID());
|
|
m_npc_faction_list.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::CheckSpawnRange(Client* client, Spawn* spawn, bool initial_login){
|
|
if(client && spawn && (initial_login || client->IsConnected())) {
|
|
if(spawn != client->GetPlayer()) {
|
|
if(spawn_range_map.count(client) == 0)
|
|
spawn_range_map.Put(client, new MutexMap<int32, float >());
|
|
float curDist = spawn->GetDistance(client->GetPlayer());
|
|
|
|
int32 ghost_spawn_id = client->GetPlayerPOVGhostSpawnID();
|
|
Spawn* otherSpawn = GetSpawnByID(ghost_spawn_id);
|
|
|
|
if (!client->GetPlayer()->WasSentSpawn(spawn->GetID())
|
|
&& (!otherSpawn || otherSpawn->GetDistance(spawn) > SEND_SPAWN_DISTANCE) && curDist > SEND_SPAWN_DISTANCE)
|
|
{
|
|
return;
|
|
}
|
|
|
|
spawn_range_map.Get(client)->Put(spawn->GetID(), curDist);
|
|
|
|
if(!initial_login && client && spawn->IsNPC() && (!spawn->IsPrivateSpawn() || spawn->AllowedAccess(client->GetPlayer()))
|
|
&& curDist <= ((NPC*)spawn)->GetAggroRadius() && !client->GetPlayer()->GetInvulnerable())
|
|
CheckNPCAttacks((NPC*)spawn, client->GetPlayer(), client);
|
|
}
|
|
|
|
if(!initial_login)
|
|
CheckPlayerProximity(spawn, client);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::CheckSpawnRange(Spawn* spawn){
|
|
vector<Client*>::iterator client_itr;
|
|
Client* client = 0;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(client && client->IsReadyForSpawns())
|
|
CheckSpawnRange(client, spawn);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
bool ZoneServer::PrepareSpawnID(Player* player, Spawn* spawn){
|
|
return player->SetSpawnMap(spawn);
|
|
}
|
|
|
|
void ZoneServer::CheckSendSpawnToClient(Client* client, bool initial_login) {
|
|
if (!client) {
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "CheckSendSpawnToClient called with an invalid client");
|
|
return;
|
|
}
|
|
|
|
if (!initial_login && !client->GetInitialSpawnsSent() || (!initial_login && !client->IsReadyForSpawns()))
|
|
return;
|
|
|
|
Spawn* spawn = 0;
|
|
map<float, vector<Spawn*>* > closest_spawns;
|
|
if (spawn_range_map.count(client) > 0) {
|
|
if (initial_login || client->IsConnected()) {
|
|
MutexMap<int32, float >::iterator spawn_iter = spawn_range_map.Get(client)->begin();
|
|
while (spawn_iter.Next()) {
|
|
spawn = GetSpawnByID(spawn_iter->first, true);
|
|
if (spawn && spawn->GetPrivateQuestSpawn()) {
|
|
if (!spawn->IsPrivateSpawn())
|
|
spawn->AddAllowAccessSpawn(spawn);
|
|
if (spawn->MeetsSpawnAccessRequirements(client->GetPlayer())) {
|
|
if (spawn->IsPrivateSpawn() && !spawn->AllowedAccess(client->GetPlayer()))
|
|
spawn->AddAllowAccessSpawn(client->GetPlayer());
|
|
}
|
|
else if (spawn->AllowedAccess(client->GetPlayer()))
|
|
spawn->RemoveSpawnAccess(client->GetPlayer());
|
|
}
|
|
if (spawn && spawn != client->GetPlayer() && client->GetPlayer()->ShouldSendSpawn(spawn)) {
|
|
if ((!initial_login && spawn_iter->second <= SEND_SPAWN_DISTANCE) || (initial_login && (spawn_iter->second <= (SEND_SPAWN_DISTANCE / 2) || spawn->IsWidget()))) {
|
|
if(PrepareSpawnID(client->GetPlayer(), spawn)) {
|
|
if (closest_spawns.count(spawn_iter->second) == 0)
|
|
closest_spawns[spawn_iter->second] = new vector<Spawn*>();
|
|
closest_spawns[spawn_iter->second]->push_back(spawn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
vector<Spawn*>::iterator spawn_iter2;
|
|
map<float, vector<Spawn*>* >::iterator itr;
|
|
for (itr = closest_spawns.begin(); itr != closest_spawns.end(); ) {
|
|
for (spawn_iter2 = itr->second->begin(); spawn_iter2 != itr->second->end(); spawn_iter2++) {
|
|
spawn = *spawn_iter2;
|
|
|
|
if(!client->IsReloadingZone() || (client->IsReloadingZone() && spawn != client->GetPlayer()))
|
|
SendSpawn(spawn, client);
|
|
|
|
if (client->ShouldTarget() && client->GetCombineSpawn() == spawn)
|
|
client->TargetSpawn(spawn);
|
|
}
|
|
vector<Spawn*>* vect = itr->second;
|
|
map<float, vector<Spawn*>* >::iterator tmpitr = itr;
|
|
itr++;
|
|
closest_spawns.erase(tmpitr);
|
|
safe_delete(vect);
|
|
}
|
|
}
|
|
|
|
if (initial_login)
|
|
client->SetInitialSpawnsSent(true);
|
|
}
|
|
|
|
void ZoneServer::CheckSendSpawnToClient(){
|
|
vector<Client*>::iterator itr;
|
|
Client* client = 0;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
client = *itr;
|
|
if(client->IsReadyForSpawns())
|
|
CheckSendSpawnToClient(client);
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::CheckRemoveSpawnFromClient(Spawn* spawn) {
|
|
vector<Client*>::iterator itr;
|
|
Client* client = 0;
|
|
PacketStruct* packet = 0;
|
|
int16 packet_version = 0;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
client = *itr;
|
|
if(client){
|
|
int32 ghost_spawn_id = client->GetPlayerPOVGhostSpawnID();
|
|
Spawn* otherSpawn = GetSpawnByID(ghost_spawn_id);
|
|
if(!packet || packet_version != client->GetVersion()){
|
|
safe_delete(packet);
|
|
packet_version = client->GetVersion();
|
|
packet = configReader.getStruct("WS_DestroyGhostCmd", packet_version);
|
|
}
|
|
|
|
if(spawn && spawn != client->GetPlayer() &&
|
|
client->GetPlayer()->WasSentSpawn(spawn->GetID()) &&
|
|
!client->GetPlayer()->IsRemovingSpawn(spawn->GetID()) &&
|
|
client->GetPlayer()->WasSpawnRemoved(spawn) == false &&
|
|
(ghost_spawn_id == 0 || (ghost_spawn_id != spawn->GetID() && otherSpawn && otherSpawn->GetDistance(spawn) > REMOVE_SPAWN_DISTANCE)) &&
|
|
(spawn_range_map.Get(client)->Get(spawn->GetID()) > REMOVE_SPAWN_DISTANCE &&
|
|
!spawn->IsSign() && !spawn->IsObject() && !spawn->IsWidget() && !spawn->IsTransportSpawn())){
|
|
SendRemoveSpawn(client, spawn, packet);
|
|
spawn_range_map.Get(client)->erase(spawn->GetID());
|
|
}
|
|
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
safe_delete(packet);
|
|
}
|
|
|
|
bool ZoneServer::CombatProcess(Spawn* spawn) {
|
|
bool ret = true;
|
|
|
|
if (spawn && spawn->IsEntity())
|
|
((Entity*)spawn)->ProcessCombat();
|
|
if (spawn && !spawn->Alive() && !spawn->IsLootDispensed()) {
|
|
LootProcess(spawn);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::LootProcess(Spawn* spawn) {
|
|
if (spawn->GetLootMethod() == GroupLootMethod::METHOD_ROUND_ROBIN) {
|
|
spawn->LockLoot();
|
|
if (spawn->CheckLootTimer() || spawn->IsLootWindowComplete()) {
|
|
LogWrite(LOOT__INFO, 0, "Loot", "%s: Dispensing loot, loot window was completed? %s.", spawn->GetName(), spawn->IsLootWindowComplete() ? "YES" : "NO");
|
|
spawn->DisableLootTimer();
|
|
spawn->SetLootDispensed();
|
|
Spawn* looter = nullptr;
|
|
if (spawn->GetLootGroupID() < 1 && spawn->GetLootWindowList()->size() > 0) {
|
|
std::map<int32, bool>::iterator itr;
|
|
|
|
for (itr = spawn->GetLootWindowList()->begin(); itr != spawn->GetLootWindowList()->end(); itr++) {
|
|
Spawn* entry = GetSpawnByID(itr->first, true);
|
|
if (entry->IsPlayer()) {
|
|
looter = entry;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int32 item_id = 0;
|
|
std::vector<int32> item_list;
|
|
spawn->GetLootItemsList(&item_list);
|
|
spawn->UnlockLoot();
|
|
|
|
std::vector<int32>::iterator item_itr;
|
|
|
|
for (item_itr = item_list.begin(); item_itr != item_list.end(); item_itr++) {
|
|
int32 item_id = *item_itr;
|
|
Item* tmpItem = master_item_list.GetItem(item_id);
|
|
|
|
bool skipItem = spawn->IsItemInLootTier(tmpItem);
|
|
|
|
if (skipItem)
|
|
continue;
|
|
|
|
if (looter) {
|
|
if (looter->IsPlayer()) {
|
|
|
|
Item* item = spawn->LootItem(item_id);
|
|
bool success = false;
|
|
success = ((Player*)looter)->GetClient()->HandleLootItem(spawn, item, ((Player*)looter));
|
|
|
|
if (!success)
|
|
spawn->AddLootItem(item);
|
|
}
|
|
else {
|
|
Item* item = spawn->LootItem(item_id);
|
|
safe_delete(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (spawn->GetLootGroupID() > 0) {
|
|
int32 item_id = 0;
|
|
std::vector<int32> item_list;
|
|
spawn->GetLootItemsList(&item_list);
|
|
spawn->UnlockLoot();
|
|
spawn->DistributeGroupLoot_RoundRobin(&item_list);
|
|
}
|
|
|
|
if (!spawn->HasLoot()) {
|
|
if (spawn->IsNPC())
|
|
RemoveDeadSpawn(spawn);
|
|
}
|
|
else {
|
|
spawn->LockLoot();
|
|
spawn->SetLootMethod(GroupLootMethod::METHOD_FFA, 0, 0);
|
|
spawn->SetLooterSpawnID(0);
|
|
spawn->UnlockLoot();
|
|
}
|
|
}
|
|
else {
|
|
spawn->UnlockLoot();
|
|
}
|
|
}
|
|
else if ((spawn->GetLootMethod() == GroupLootMethod::METHOD_LOTTO || spawn->GetLootMethod() == GroupLootMethod::METHOD_NEED_BEFORE_GREED) && spawn->IsLootTimerRunning()) {
|
|
spawn->LockLoot();
|
|
if (spawn->CheckLootTimer() || spawn->IsLootWindowComplete()) {
|
|
LogWrite(LOOT__INFO, 0, "Loot", "%s: Dispensing loot, loot window was completed? %s.", spawn->GetName(), spawn->IsLootWindowComplete() ? "YES" : "NO");
|
|
spawn->DisableLootTimer();
|
|
spawn->SetLootDispensed();
|
|
|
|
// identify any clients that still have the loot window open, close it out
|
|
CloseSpawnLootWindow(spawn);
|
|
|
|
// lotto items while we have loot items in the list
|
|
int32 item_id = 0;
|
|
std::vector<int32> item_list;
|
|
spawn->GetLootItemsList(&item_list);
|
|
spawn->UnlockLoot();
|
|
|
|
std::vector<int32>::iterator item_itr;
|
|
|
|
for (item_itr = item_list.begin(); item_itr != item_list.end(); item_itr++) {
|
|
int32 item_id = *item_itr;
|
|
Item* tmpItem = master_item_list.GetItem(item_id);
|
|
|
|
bool skipItem = spawn->IsItemInLootTier(tmpItem);
|
|
|
|
if (skipItem)
|
|
continue;
|
|
|
|
std::map<int32, int32> out_entries;
|
|
std::map<int32, int32>::iterator out_itr;
|
|
bool itemNeed = true;
|
|
switch (spawn->GetLootMethod()) {
|
|
case GroupLootMethod::METHOD_LOTTO: {
|
|
spawn->GetSpawnLottoEntries(item_id, &out_entries);
|
|
break;
|
|
}
|
|
case GroupLootMethod::METHOD_NEED_BEFORE_GREED: {
|
|
spawn->GetSpawnNeedGreedEntries(item_id, true, &out_entries);
|
|
if (out_entries.size() < 1) {
|
|
spawn->GetSpawnNeedGreedEntries(item_id, false, &out_entries);
|
|
itemNeed = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (out_entries.size() < 1) {
|
|
LogWrite(LOOT__INFO, 0, "Loot", "%s: No spawns matched for loot attempt of %s (%u), skip item.", spawn->GetName(), tmpItem ? tmpItem->name.c_str() : "Unknown", item_id);
|
|
continue;
|
|
}
|
|
Spawn* looter = nullptr;
|
|
int32 curWinValue = 0;
|
|
for (out_itr = out_entries.begin(); out_itr != out_entries.end(); out_itr++) {
|
|
Spawn* entry = GetSpawnByID(out_itr->first, true);
|
|
if ((out_itr->second > curWinValue) || looter == nullptr) {
|
|
curWinValue = out_itr->second;
|
|
looter = entry;
|
|
}
|
|
if (spawn->GetLootMethod() == GroupLootMethod::METHOD_LOTTO) {
|
|
world.GetGroupManager()->SendGroupChatMessage(spawn->GetLootGroupID(), CHANNEL_LOOT_ROLLS, "%s rolled %u on %s.", entry->GetName(), out_itr->second, tmpItem ? tmpItem->name.c_str() : "Unknown");
|
|
}
|
|
else {
|
|
world.GetGroupManager()->SendGroupChatMessage(spawn->GetLootGroupID(), CHANNEL_LOOT_ROLLS, "%s rolled %s (%u) on %s.", entry->GetName(), itemNeed ? "NEED" : "GREED", out_itr->second, tmpItem ? tmpItem->name.c_str() : "Unknown");
|
|
}
|
|
}
|
|
|
|
if (looter) {
|
|
if (looter->IsPlayer()) {
|
|
Item* item = spawn->LootItem(item_id);
|
|
bool success = false;
|
|
success = ((Player*)looter)->GetClient()->HandleLootItem(spawn, item, ((Player*)looter));
|
|
|
|
if (!success)
|
|
spawn->AddLootItem(item);
|
|
}
|
|
else {
|
|
Item* item = spawn->LootItem(item_id);
|
|
safe_delete(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!spawn->HasLoot()) {
|
|
if (spawn->IsNPC())
|
|
RemoveDeadSpawn(spawn);
|
|
}
|
|
else {
|
|
spawn->LockLoot();
|
|
spawn->SetLootMethod(GroupLootMethod::METHOD_FFA, 0, 0);
|
|
spawn->SetLooterSpawnID(0);
|
|
spawn->UnlockLoot();
|
|
}
|
|
}
|
|
else {
|
|
spawn->UnlockLoot();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::CloseSpawnLootWindow(Spawn* spawn) {
|
|
if (spawn->GetLootWindowList()->size() > 0) {
|
|
std::map<int32, bool>::iterator itr;
|
|
for (itr = spawn->GetLootWindowList()->begin(); itr != spawn->GetLootWindowList()->end(); itr++) {
|
|
if (itr->second)
|
|
continue;
|
|
|
|
itr->second = true;
|
|
Spawn* looter = GetSpawnByID(itr->first, true);
|
|
if (looter && looter->IsPlayer() && ((Player*)looter)->GetClient()) {
|
|
LogWrite(LOOT__DEBUG, 0, "Loot", "%s: Close loot for player %s.", spawn->GetName(), looter->GetName());
|
|
((Player*)looter)->GetClient()->CloseLoot(spawn->GetID());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void ZoneServer::AddPendingDelete(Spawn* spawn) {
|
|
MSpawnDeleteList.writelock(__FUNCTION__, __LINE__);
|
|
spawn->SetDeletedSpawn(true);
|
|
if (spawn_delete_list.count(spawn) == 0)
|
|
spawn_delete_list.insert(make_pair(spawn, Timer::GetCurrentTime2() + spawn_delete_timer)); //give other threads up to 30 seconds to stop using this spawn reference
|
|
MSpawnDeleteList.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::DeleteSpawns(bool delete_all) {
|
|
MSpawnDeleteList.writelock(__FUNCTION__, __LINE__);
|
|
MPendingSpawnRemoval.readlock(__FUNCTION__, __LINE__);
|
|
if(spawn_delete_list.size() > 0){
|
|
map<Spawn*, int32>::iterator itr;
|
|
map<Spawn*, int32>::iterator erase_itr;
|
|
int32 current_time = Timer::GetCurrentTime2();
|
|
Spawn* spawn = 0;
|
|
for (itr = spawn_delete_list.begin(); itr != spawn_delete_list.end(); ) {
|
|
if (delete_all || current_time >= itr->second){
|
|
// we haven't removed it from the spawn list yet..
|
|
if(!delete_all && m_pendingSpawnRemove.count(itr->first->GetID()))
|
|
continue;
|
|
|
|
spawn = itr->first;
|
|
|
|
lua_interface->SetLuaUserDataStale(spawn);
|
|
|
|
if (spellProcess) {
|
|
spellProcess->RemoveCaster(spawn, true);
|
|
}
|
|
|
|
if(movementMgr != nullptr) {
|
|
movementMgr->RemoveMob((Entity*)spawn);
|
|
}
|
|
|
|
// delete brain if it has one
|
|
if(spawn->IsNPC()) {
|
|
NPC* tmpNPC = (NPC*)spawn;
|
|
if(tmpNPC->Brain())
|
|
tmpNPC->SetBrain(nullptr);
|
|
}
|
|
|
|
erase_itr = itr++;
|
|
spawn_delete_list.erase(erase_itr);
|
|
|
|
MSpawnList.writelock(__FUNCTION__, __LINE__);
|
|
std::map<int32, Spawn*>::iterator sitr = spawn_list.find(spawn->GetID());
|
|
if(sitr != spawn_list.end()) {
|
|
spawn_list.erase(sitr);
|
|
}
|
|
|
|
if(spawn->IsCollector()) {
|
|
std::map<int32, Spawn*>::iterator subitr = subspawn_list[SUBSPAWN_TYPES::COLLECTOR].find(spawn->GetID());
|
|
if(subitr != subspawn_list[SUBSPAWN_TYPES::COLLECTOR].end()) {
|
|
subspawn_list[SUBSPAWN_TYPES::COLLECTOR].erase(subitr);
|
|
}
|
|
}
|
|
|
|
if(spawn->GetPickupItemID()) {
|
|
std::map<int32, Spawn*>::iterator subitr = subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].find(spawn->GetPickupItemID());
|
|
if(subitr != subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].end() && subitr->second == spawn) {
|
|
subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].erase(subitr);
|
|
}
|
|
housing_spawn_map.erase(spawn->GetID());
|
|
}
|
|
MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
|
|
safe_delete(spawn);
|
|
}
|
|
else
|
|
itr++;
|
|
}
|
|
}
|
|
MPendingSpawnRemoval.releasereadlock(__FUNCTION__, __LINE__);
|
|
MSpawnDeleteList.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::AddDamagedSpawn(Spawn* spawn){
|
|
if (spawn)
|
|
damaged_spawns.Add(spawn->GetID());
|
|
}
|
|
|
|
void ZoneServer::RemoveDamagedSpawn(Spawn* spawn){
|
|
if (spawn)
|
|
damaged_spawns.Remove(spawn->GetID());
|
|
}
|
|
|
|
bool ZoneServer::Process()
|
|
{
|
|
MMasterZoneLock->lock(); //Changing this back to a recursive lock to fix a possible /reload spells crash with multiple zones running - Foof
|
|
SetWatchdogTime(Timer::GetCurrentTime2());
|
|
#ifndef NO_CATCH
|
|
try
|
|
{
|
|
#endif
|
|
while (zoneID == 0) { //this is loaded by world
|
|
SetWatchdogTime(Timer::GetCurrentTime2());
|
|
Sleep(10);
|
|
}
|
|
|
|
if (LoadingData) {
|
|
if (lua_interface) {
|
|
string tmpScript("ZoneScripts/");
|
|
tmpScript.append(GetZoneName());
|
|
tmpScript.append(".lua");
|
|
struct stat buffer;
|
|
bool fileExists = (stat(tmpScript.c_str(), &buffer) == 0);
|
|
if (fileExists)
|
|
lua_interface->RunZoneScript(tmpScript.c_str(), "preinit_zone_script", this);
|
|
}
|
|
|
|
if (reloading) {
|
|
LogWrite(COMMAND__DEBUG, 0, "Command", "-Loading Entity Commands...");
|
|
database.LoadEntityCommands(this);
|
|
LogWrite(NPC__INFO, 0, "NPC", "-Loading Spirit Shard data...");
|
|
database.LoadSpiritShards(this);
|
|
LogWrite(NPC__INFO, 0, "NPC", "-Load Spirit Shard data complete!");
|
|
|
|
LogWrite(NPC__INFO, 0, "NPC", "-Loading NPC data...");
|
|
database.LoadNPCs(this);
|
|
LogWrite(NPC__INFO, 0, "NPC", "-Load NPC data complete!");
|
|
|
|
LogWrite(OBJECT__INFO, 0, "Object", "-Loading Object data...");
|
|
database.LoadObjects(this);
|
|
LogWrite(OBJECT__INFO, 0, "Object", "-Load Object data complete!");
|
|
|
|
LogWrite(SIGN__INFO, 0, "Sign", "-Loading Sign data...");
|
|
database.LoadSigns(this);
|
|
LogWrite(SIGN__INFO, 0, "Sign", "-Load Sign data complete!");
|
|
|
|
LogWrite(WIDGET__INFO, 0, "Widget", "-Loading Widget data...");
|
|
database.LoadWidgets(this);
|
|
LogWrite(WIDGET__INFO, 0, "Widget", "-Load Widget data complete!");
|
|
|
|
LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Loading Groundspawn data...");
|
|
database.LoadGroundSpawns(this);
|
|
database.LoadGroundSpawnEntries(this);
|
|
LogWrite(GROUNDSPAWN__INFO, 0, "GSpawn", "-Load Groundspawn data complete!");
|
|
|
|
LogWrite(PET__INFO, 0, "Pet", "-Loading Pet data...");
|
|
database.GetPetNames(this);
|
|
LogWrite(PET__INFO, 0, "Pet", "-Load Pet data complete!");
|
|
|
|
LogWrite(LOOT__INFO, 0, "Loot", "-Loading Spawn loot data...");
|
|
database.LoadLoot(this);
|
|
LogWrite(LOOT__INFO, 0, "Loot", "-Loading Spawn loot data complete!");
|
|
|
|
LogWrite(TRANSPORT__INFO, 0, "Transport", "-Loading Transporters...");
|
|
database.LoadTransporters(this);
|
|
LogWrite(TRANSPORT__INFO, 0, "Transport", "-Loading Transporters complete!");
|
|
reloading = false;
|
|
world.RemoveReloadingSubSystem("Spawns");
|
|
}
|
|
|
|
MSpawnGroupAssociation.writelock(__FUNCTION__, __LINE__);
|
|
spawn_group_associations.clear();
|
|
MSpawnGroupAssociation.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
MSpawnGroupLocations.writelock(__FUNCTION__, __LINE__);
|
|
spawn_group_locations.clear();
|
|
MSpawnGroupLocations.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
MSpawnLocationGroups.writelock(__FUNCTION__, __LINE__);
|
|
spawn_location_groups.clear();
|
|
MSpawnLocationGroups.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
MSpawnGroupChances.writelock(__FUNCTION__, __LINE__);
|
|
spawn_group_chances.clear();
|
|
MSpawnGroupChances.releasewritelock(__FUNCTION__, __LINE__);
|
|
Map* zonemap = world.GetMap(std::string(GetZoneFile()),0);
|
|
while (zonemap != nullptr && zonemap->IsMapLoading())
|
|
{
|
|
SetWatchdogTime(Timer::GetCurrentTime2());
|
|
// Client loop
|
|
ClientProcess(true);
|
|
Sleep(10);
|
|
}
|
|
|
|
default_zone_map = world.GetMap(std::string(GetZoneFile()),0);
|
|
|
|
DeleteTransporters();
|
|
ReloadTransporters();
|
|
|
|
database.LoadSpawns(this);
|
|
ProcessSpawnLocations();
|
|
|
|
if (!revive_points)
|
|
revive_points = new vector<RevivePoint*>;
|
|
else {
|
|
while (!revive_points->empty()) {
|
|
safe_delete(revive_points->back());
|
|
revive_points->pop_back();
|
|
}
|
|
}
|
|
database.LoadRevivePoints(revive_points, GetZoneID());
|
|
|
|
RemoveLocationGrids();
|
|
database.LoadLocationGrids(this);
|
|
|
|
|
|
MMasterZoneLock->unlock();
|
|
|
|
while(true) {
|
|
ProcessPendingSpawns();
|
|
Sleep(20);
|
|
MPendingSpawnListAdd.readlock(__FUNCTION__, __LINE__);
|
|
int32 count = pending_spawn_list_add.size();
|
|
MPendingSpawnListAdd.releasereadlock(__FUNCTION__, __LINE__);
|
|
if(count < 1)
|
|
break;
|
|
}
|
|
|
|
startupDelayTimer.Start(60000); // this is hard coded for 60 seconds after the zone is loaded to allow a client to at least add itself to the list before we start zone shutdown timer
|
|
|
|
MMasterZoneLock->lock();
|
|
|
|
LoadingData = false;
|
|
|
|
const char* zone_script = world.GetZoneScript(this->GetZoneID());
|
|
if (lua_interface && zone_script) {
|
|
RemoveLocationProximities();
|
|
lua_interface->RunZoneScript(zone_script, "init_zone_script", this);
|
|
}
|
|
|
|
spawn_range.Trigger();
|
|
spawn_check_add.Trigger();
|
|
}
|
|
|
|
if (reloading_spellprocess){
|
|
MMasterZoneLock->unlock();
|
|
return !zoneShuttingDown;
|
|
}
|
|
|
|
if(shutdownTimer.Enabled() && shutdownTimer.Check() && connected_clients.size(true) == 0) {
|
|
//if(lifetime_client_count)
|
|
zoneShuttingDown = true;
|
|
/*else { // allow up to 120 seconds then timeout
|
|
LogWrite(ZONE__WARNING, 0, "Zone", "No clients have connected to zone '%s' and the shutdown timer has counted down -- will delay shutdown for 120 seconds.", GetZoneName());
|
|
shutdownTimer.Start(120000, true);
|
|
lifetime_client_count = 1;
|
|
}*/
|
|
MMasterZoneLock->unlock();
|
|
return false;
|
|
}
|
|
|
|
// client loop
|
|
if(charsheet_changes.Check()) {
|
|
SendCharSheetChanges();
|
|
}
|
|
else {
|
|
SendRaidSheetChanges();
|
|
}
|
|
|
|
// Client loop
|
|
ClientProcess(startupDelayTimer.Enabled());
|
|
if(startupDelayTimer.Check())
|
|
startupDelayTimer.Disable();
|
|
|
|
if(!reloading && spellProcess)
|
|
spellProcess->Process();
|
|
if (tradeskillMgr)
|
|
tradeskillMgr->Process();
|
|
|
|
// Client loop
|
|
if(client_save.Check())
|
|
SaveClients();
|
|
|
|
// Possibility to do a client loop
|
|
if(weather_enabled && weatherTimer.Check())
|
|
ProcessWeather();
|
|
|
|
// client related loop, move to main thread?
|
|
if(!zoneShuttingDown)
|
|
ProcessDrowning();
|
|
|
|
// client than location_proximities loop, move to main thread
|
|
if (location_prox_timer.Check() && !zoneShuttingDown)
|
|
CheckLocationProximity();
|
|
|
|
// client than location_grid loop, move to main thread
|
|
if (location_grid_timer.Check() && !zoneShuttingDown)
|
|
CheckLocationGrids();
|
|
|
|
if (sync_game_time_timer.Check() && !zoneShuttingDown)
|
|
SendTimeUpdateToAllClients();
|
|
|
|
world.MWorldTime.readlock(__FUNCTION__, __LINE__);
|
|
int hour = world.GetWorldTimeStruct()->hour;
|
|
int minute = world.GetWorldTimeStruct()->minute;
|
|
world.MWorldTime.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
if (!isDusk && (hour >= 19 || hour < 8)) {//((hour > dusk_hour || hour < dawn_hour) || ((dusk_hour == hour && minute >= dusk_minute) || (hour == dawn_hour && minute < dawn_minute)))) {
|
|
isDusk = true;
|
|
const char* zone_script = world.GetZoneScript(GetZoneID());
|
|
if (lua_interface && zone_script)
|
|
lua_interface->RunZoneScript(zone_script, "dusk", this);
|
|
|
|
ProcessSpawnConditional(SPAWN_CONDITIONAL_NIGHT);
|
|
}
|
|
else if (isDusk && hour >= 8 && hour < 19) {//((hour > dawn_hour && hour < dusk_hour) || ((hour == dawn_hour && minute >= dawn_minute) || (hour == dusk_hour && minute < dusk_minute)))) {
|
|
isDusk = false;
|
|
const char* zone_script = world.GetZoneScript(GetZoneID());
|
|
if (lua_interface && zone_script)
|
|
lua_interface->RunZoneScript(zone_script, "dawn", this);
|
|
|
|
ProcessSpawnConditional(SPAWN_CONDITIONAL_DAY);
|
|
}
|
|
|
|
// damaged spawns loop, spawn related, move to spawn thread?
|
|
if(regenTimer.Check())
|
|
RegenUpdate();
|
|
|
|
// respawn_timers loop
|
|
if(respawn_timer.Check() && !zoneShuttingDown)
|
|
CheckRespawns();
|
|
|
|
// spawn_expire_timers loop
|
|
if (spawn_expire_timer.Check() && !zoneShuttingDown)
|
|
CheckSpawnExpireTimers();
|
|
|
|
// widget_timers loop
|
|
if(widget_timer.Check() && !zoneShuttingDown)
|
|
CheckWidgetTimers();
|
|
|
|
// spawn_script_timers loop
|
|
if(!reloading && !zoneShuttingDown)
|
|
CheckSpawnScriptTimers();
|
|
|
|
// Check to see if a dead spawn needs to be removed
|
|
CheckDeadSpawnRemoval();
|
|
#ifndef NO_CATCH
|
|
}
|
|
catch(...)
|
|
{
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "Exception while running '%s'", GetZoneName());
|
|
zoneShuttingDown = true;
|
|
MMasterZoneLock->unlock();
|
|
return false;
|
|
}
|
|
#endif
|
|
MMasterZoneLock->unlock();
|
|
return (zoneShuttingDown == false);
|
|
}
|
|
|
|
bool ZoneServer::SpawnProcess(){
|
|
if(depop_zone) {
|
|
depop_zone = false;
|
|
ProcessDepop(respawns_allowed, repop_zone);
|
|
finished_depop = true;
|
|
}
|
|
|
|
MMasterSpawnLock.writelock(__FUNCTION__, __LINE__);
|
|
// If the zone is loading data or shutting down don't do anything
|
|
if(!LoadingData && !zoneShuttingDown && !reloading_spellprocess) {
|
|
// Set some bool's for timers
|
|
bool movement = movement_timer.Check();
|
|
bool spawnRange = spawn_range.Check();
|
|
bool checkRemove = spawn_check_remove.Check();
|
|
bool aggroCheck = aggro_timer.Check();
|
|
vector<int32> pending_spawn_list_remove;
|
|
|
|
// Check to see if there are any spawn id's that need to be removed from the spawn list, if so remove them all
|
|
ProcessSpawnRemovals();
|
|
|
|
map<int32, Spawn*>::iterator itr;
|
|
if (spawnRange || checkRemove)
|
|
{
|
|
// Loop through the spawn list
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
// Loop throught the list to set up spawns to be sent to clients
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
// if zone is shutting down kill the loop
|
|
if (zoneShuttingDown)
|
|
break;
|
|
|
|
Spawn* spawn = itr->second;
|
|
if (spawn) {
|
|
// Checks the range to all clients in the zone
|
|
if (spawnRange)
|
|
CheckSpawnRange(spawn);
|
|
|
|
// Checks to see if the spawn needs to be removed from a client
|
|
if (checkRemove)
|
|
CheckRemoveSpawnFromClient(spawn);
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Broke the spawn loop into 2 so spawns are sent to the client faster, send the spawns to clients now then resume the spawn loop
|
|
|
|
// client loop, move to main thread?
|
|
// moved this back to the spawn thread as on the main thread during a depop, done on the spawn thread, spawns would start to pop again
|
|
// might be an issue with other functions moved from the spawn thread to the main thread?
|
|
if(spawn_check_add.Check() && !zoneShuttingDown)
|
|
CheckSendSpawnToClient();
|
|
|
|
|
|
// send spawn changes, changed_spawns loop
|
|
if (spawn_update.Check() && !zoneShuttingDown) { //check for changed spawns every {Rule:SpawnUpdateTimer} milliseconds (default: 200ms)
|
|
SendSpawnChanges();
|
|
}
|
|
|
|
if (movement || aggroCheck)
|
|
{
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
// Break the loop if the zone is shutting down
|
|
if (zoneShuttingDown)
|
|
break;
|
|
|
|
Spawn* spawn = itr->second;
|
|
if (spawn) {
|
|
// Process spawn movement
|
|
if (movement) {
|
|
spawn->ProcessMovement(true);
|
|
// update last_movement_update for all spawns (used for time_step)
|
|
spawn->last_movement_update = Timer::GetCurrentTime2();
|
|
if (!aggroCheck)
|
|
CombatProcess(spawn);
|
|
}
|
|
|
|
// Makes NPC's KOS to other NPC's or players
|
|
if (aggroCheck)
|
|
{
|
|
ProcessAggroChecks(spawn);
|
|
CombatProcess(spawn);
|
|
}
|
|
}
|
|
else {
|
|
// unable to get a valid spawn, lets add the id to another list to remove from the spawn list after this loop is finished
|
|
pending_spawn_list_remove.push_back(itr->first);
|
|
}
|
|
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Check to see if there are any spawn id's that need to be removed from the spawn list, if so remove them all
|
|
if (pending_spawn_list_remove.size() > 0) {
|
|
MSpawnList.writelock(__FUNCTION__, __LINE__);
|
|
vector<int32>::iterator itr2;
|
|
for (itr2 = pending_spawn_list_remove.begin(); itr2 != pending_spawn_list_remove.end(); itr2++) {
|
|
spawn_list.erase(*itr2);
|
|
subspawn_list[SUBSPAWN_TYPES::COLLECTOR].erase(*itr2);
|
|
|
|
std::map<int32,int32>::iterator hsmitr = housing_spawn_map.find(*itr2);
|
|
if(hsmitr != housing_spawn_map.end()) {
|
|
subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].erase(hsmitr->second);
|
|
housing_spawn_map.erase(hsmitr);
|
|
}
|
|
}
|
|
|
|
pending_spawn_list_remove.clear();
|
|
MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// Double Check to see if there are any spawn id's that need to be removed from the spawn list, if so remove them before we replace with pending spawns
|
|
// and also potentially further down when we delete the Spawn* in DeleteSpawns(false)
|
|
ProcessSpawnRemovals();
|
|
|
|
// Check to see if there are spawns waiting to be added to the spawn list, if so add them all
|
|
if (pending_spawn_list_add.size() > 0) {
|
|
ProcessPendingSpawns();
|
|
}
|
|
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
if (movementMgr != nullptr)
|
|
movementMgr->Process();
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
if(queue_updates.Check())
|
|
ProcessQueuedStateCommands();
|
|
// Do other loops for spawns
|
|
// tracking, client loop with spawn loop for each client that is tracking, change to a spawn_range_map loop instead of using the main spawn list?
|
|
//if (tracking_timer.Check())
|
|
//ProcessTracking(); // probably doesn't work as spawn loop iterator is never set
|
|
|
|
|
|
// Delete unused spawns, do this last
|
|
if(!zoneShuttingDown)
|
|
DeleteSpawns(false);
|
|
|
|
// Nothing should come after this
|
|
|
|
|
|
//LogWrite(PLAYER__ERROR, 0, "Debug", "Spawn loop time %u", Timer::GetCurrentTime2() - time);
|
|
}
|
|
|
|
MMasterSpawnLock.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
return (zoneShuttingDown == false);
|
|
}
|
|
|
|
void ZoneServer::CheckDeadSpawnRemoval() {
|
|
MDeadSpawns.writelock(__FUNCTION__, __LINE__);
|
|
if(dead_spawns.size() > 0){
|
|
vector<Spawn*> tmp_dead_list;
|
|
int32 current_time = Timer::GetCurrentTime2();
|
|
Spawn* spawn = 0;
|
|
map<int32, int32>::iterator itr = dead_spawns.begin();
|
|
map<int32, int32>::iterator itr_delete;
|
|
while (itr != dead_spawns.end()) {
|
|
spawn = GetSpawnByID(itr->first);
|
|
if (spawn) {
|
|
if(current_time >= itr->second)
|
|
tmp_dead_list.push_back(spawn);
|
|
itr++;
|
|
}
|
|
else {
|
|
itr_delete = itr++;
|
|
dead_spawns.erase(itr_delete);
|
|
}
|
|
}
|
|
for(int i=tmp_dead_list.size()-1;i>=0;i--){
|
|
spawn = tmp_dead_list[i];
|
|
if (!spawn->IsPlayer())
|
|
{
|
|
dead_spawns.erase(spawn->GetID());
|
|
MDeadSpawns.releasewritelock(__FUNCTION__, __LINE__);
|
|
RemoveSpawn(spawn, true, true, true, true, true);
|
|
MDeadSpawns.writelock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
}
|
|
MDeadSpawns.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::CheckRespawns(){
|
|
vector<int32> tmp_respawn_list;
|
|
MutexMap<int32, int32>::iterator itr = respawn_timers.begin();
|
|
while(itr.Next()){
|
|
if(Timer::GetCurrentTime2() >= itr->second)
|
|
tmp_respawn_list.push_back(itr->first);
|
|
}
|
|
for(int i=tmp_respawn_list.size()-1;i>=0;i--){
|
|
if ( IsInstanceZone() )
|
|
{
|
|
database.DeleteInstanceSpawnRemoved(GetInstanceID(),tmp_respawn_list[i]);
|
|
}
|
|
else {
|
|
database.DeletePersistedRespawn(GetZoneID(),tmp_respawn_list[i]);
|
|
}
|
|
|
|
ProcessSpawnLocation(tmp_respawn_list[i], nullptr, nullptr, nullptr, nullptr, nullptr, true);
|
|
respawn_timers.erase(tmp_respawn_list[i]);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SendRespawnTimerList(Client* client){
|
|
MutexMap<int32, int32>::iterator itr = respawn_timers.begin();
|
|
client->Message(CHANNEL_FACTION, "Respawn Timers:");
|
|
client->Message(CHANNEL_FACTION, "Location ID : Time Remaining");
|
|
while(itr.Next()){
|
|
client->Message(CHANNEL_FACTION, "%u: %i seconds.", itr->first, (itr->second - Timer::GetCurrentTime2())/1000);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::CheckSpawnExpireTimers() {
|
|
MutexMap<int32, int32>::iterator itr = spawn_expire_timers.begin();
|
|
while (itr.Next()) {
|
|
Spawn* spawn = GetSpawnByID(itr->first);
|
|
if (spawn) {
|
|
if (Timer::GetCurrentTime2() >= itr.second) {
|
|
spawn_expire_timers.erase(itr.first);
|
|
Despawn(spawn, spawn->GetRespawnTime());
|
|
}
|
|
}
|
|
else
|
|
spawn_expire_timers.erase(itr->first);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::AddSpawnExpireTimer(Spawn* spawn, int32 expire_time, int32 expire_offset) {
|
|
if (spawn) {
|
|
int32 actual_expire_time = expire_time;
|
|
if (expire_offset > 0) {
|
|
int32 low = expire_time;
|
|
int32 high = expire_time + expire_offset;
|
|
if (expire_offset < expire_time)
|
|
low = expire_time - expire_offset;
|
|
int32 range = (high - low) + 1;
|
|
actual_expire_time = (low + (int32)((range * rand()) / (RAND_MAX + 1.0)));
|
|
}
|
|
actual_expire_time *= 1000;
|
|
spawn_expire_timers.Put(spawn->GetID(), Timer::GetCurrentTime2() + actual_expire_time);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SaveClient(Client* client){
|
|
client->Save();
|
|
}
|
|
|
|
void ZoneServer::SaveClients(){
|
|
vector<Client*>::iterator itr;
|
|
Client* client = 0;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
client = *itr;
|
|
if(client->IsConnected() && client->IsReadyForUpdates()){
|
|
SaveClient(client);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendSpawnVisualState(Spawn* spawn, int16 type){
|
|
if(!spawn)
|
|
return;
|
|
|
|
vector<Client*>::iterator itr;
|
|
spawn->SetTempVisualState(type);
|
|
Client* client = 0;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
client = *itr;
|
|
if(client && client->GetPlayer() != spawn)
|
|
AddChangedSpawn(spawn);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendSpawnChangesByDBID(int32 db_id, Client* client, bool override_changes, bool override_vis_changes){
|
|
Spawn* spawn = GetSpawnByDatabaseID(db_id);
|
|
if(spawn && (spawn->changed || override_changes || override_vis_changes))
|
|
SendSpawnChanges(spawn, client, override_changes, override_vis_changes);
|
|
}
|
|
|
|
void ZoneServer::SendSpawnChanges(Spawn* spawn, Client* client, bool override_changes, bool override_vis_changes){
|
|
if(client && client->IsConnected() && client->IsReadyForUpdates() && client->GetPlayer()->WasSentSpawn(spawn->GetID()) && (spawn->IsTransportSpawn() || client->GetPlayer()->GetDistance(spawn) < SEND_SPAWN_DISTANCE)){
|
|
EQ2Packet* outapp = spawn->spawn_update_packet(client->GetPlayer(), client->GetVersion(), override_changes, override_vis_changes);
|
|
if(outapp)
|
|
client->QueuePacket(outapp);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SendSpawnChanges(Spawn* spawn){
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
MSpawnList.readlock();
|
|
if(spawn && spawn->changed){
|
|
if(!spawn->IsPlayer() || (spawn->IsPlayer() && (spawn->info_changed || spawn->vis_changed))){
|
|
vector<Client*>::iterator itr;
|
|
Client* client = 0;
|
|
|
|
// MClientList locked at a higher level
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
client = *itr;
|
|
SendSpawnChanges(spawn, client);
|
|
}
|
|
}
|
|
spawn->changed = false;
|
|
spawn->info_changed = false;
|
|
if(spawn->IsPlayer() == false)
|
|
spawn->position_changed = false;
|
|
spawn->vis_changed = false;
|
|
}
|
|
MSpawnList.releasereadlock();
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
Spawn* ZoneServer::FindSpawn(Player* searcher, const char* name){
|
|
if(!searcher || !name)
|
|
return 0;
|
|
|
|
Spawn* spawn = 0;
|
|
vector<Spawn*> find_spawn_list;
|
|
vector<Spawn*>::iterator fspawn_iter;
|
|
int8 name_size = strlen(name);
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if(spawn && !strncasecmp(spawn->GetName(), name, name_size))
|
|
find_spawn_list.push_back(spawn);
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
Spawn* closest = 0;
|
|
float distance = 0;
|
|
float test_distance = 0;
|
|
for(fspawn_iter=find_spawn_list.begin(); fspawn_iter!=find_spawn_list.end(); fspawn_iter++){
|
|
spawn = *fspawn_iter;
|
|
if(spawn && ((((test_distance = searcher->GetDistance(spawn)) < distance)) || !closest)){
|
|
distance = test_distance;
|
|
closest = spawn;
|
|
}
|
|
}
|
|
return closest;
|
|
}
|
|
|
|
void ZoneServer::AddChangedSpawn(Spawn* spawn) {
|
|
if (!spawn || (spawn->IsPlayer() && !spawn->info_changed && !spawn->vis_changed) || (spawn->IsPlayer() && ((Player*)spawn)->GetClient() && ((Player*)spawn)->GetClient()->IsReadyForUpdates() == false))
|
|
return;
|
|
|
|
MChangedSpawns.lock_shared();
|
|
ChangedSpawnMapType::iterator it = changed_spawns.find(spawn->GetID());
|
|
if (it != changed_spawns.end()) {
|
|
it->second = true;
|
|
MChangedSpawns.unlock_shared();
|
|
}
|
|
else {
|
|
MChangedSpawns.unlock_shared();
|
|
MChangedSpawns.lock();
|
|
changed_spawns.insert(make_pair(spawn->GetID(),true));
|
|
MChangedSpawns.unlock();
|
|
}
|
|
}
|
|
|
|
void ZoneServer::RemoveChangedSpawn(Spawn* spawn){
|
|
if(!spawn)
|
|
return;
|
|
|
|
MChangedSpawns.lock();
|
|
ChangedSpawnMapType::iterator it = changed_spawns.find(spawn->GetID());
|
|
if (it != changed_spawns.end()) {
|
|
it->second = false;
|
|
}
|
|
MChangedSpawns.unlock();
|
|
}
|
|
|
|
void ZoneServer::AddDrowningVictim(Player* player){
|
|
Client* client = ((Player*)player)->GetClient();
|
|
if(client && drowning_victims.count(client) == 0)
|
|
drowning_victims.Put(client, Timer::GetCurrentTime2());
|
|
}
|
|
|
|
void ZoneServer::RemoveDrowningVictim(Player* player){
|
|
Client* client = ((Player*)player)->GetClient();
|
|
if(client)
|
|
drowning_victims.erase(client);
|
|
}
|
|
|
|
Client* ZoneServer::GetDrowningVictim(Player* player){
|
|
Client* client = ((Player*)player)->GetClient();
|
|
if(client && drowning_victims.count(client) > 0)
|
|
return(client);
|
|
return 0;
|
|
}
|
|
|
|
void ZoneServer::ProcessDrowning(){
|
|
vector<Client*> dead_list;
|
|
if(drowning_victims.size(true) > 0){
|
|
sint32 damage = 0;
|
|
int32 current_time = Timer::GetCurrentTime2();
|
|
MutexMap<Client*, int32>::iterator itr = drowning_victims.begin();
|
|
while(itr.Next()){
|
|
if(current_time >= itr->second) {
|
|
Client* client = itr->first;
|
|
Player* player = client->GetPlayer();
|
|
drowning_victims.Get(client) = Timer::GetCurrentTime2() + 2000;
|
|
damage = player->GetTotalHP()/20 + player->GetInfoStruct()->get_hp_regen();
|
|
player->TakeDamage(damage);
|
|
if(!player->Alive())
|
|
dead_list.push_back(client);
|
|
player->SetCharSheetChanged(true);
|
|
SendCharSheetChanges(client);
|
|
SendDamagePacket(0, player, DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE, DAMAGE_PACKET_RESULT_SUCCESSFUL, DAMAGE_PACKET_DAMAGE_TYPE_DROWN, damage, 0);
|
|
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are drowning!");
|
|
}
|
|
}
|
|
}
|
|
if(dead_list.size() > 0){
|
|
vector<Client*>::iterator itr;
|
|
for(itr = dead_list.begin(); itr != dead_list.end(); itr++){
|
|
RemoveDrowningVictim((*itr)->GetPlayer());
|
|
KillSpawn(false, (*itr)->GetPlayer(), nullptr, true, 0, 0, 10); // kill blow type 10 means death by WATER! (glug glug!)
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SendSpawnChanges(){
|
|
std::shared_lock lock(MChangedSpawns);
|
|
if (changed_spawns.size() < 1)
|
|
return;
|
|
|
|
set<Spawn*> spawns_to_send;
|
|
Spawn* spawn = 0;
|
|
|
|
int count = 0;
|
|
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
|
|
for( ChangedSpawnMapType::iterator it = changed_spawns.begin(); it != changed_spawns.end(); ++it ) {
|
|
if(!it->second)
|
|
continue;
|
|
|
|
spawn = GetSpawnByID(it->first);
|
|
if(spawn){
|
|
spawns_to_send.insert(spawn);
|
|
count++;
|
|
}
|
|
}
|
|
|
|
vector<Client*>::iterator client_itr;
|
|
Client* client = 0;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
if(clients.size())
|
|
{
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(client)
|
|
client->SendSpawnChanges(spawns_to_send);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
for (const auto& spawn : spawns_to_send) {
|
|
spawn->changed = false;
|
|
spawn->position_changed = false;
|
|
spawn->vis_changed = false;
|
|
spawn->info_changed = false;
|
|
spawn->size_changed = false;
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendPlayerPositionChanges(Player* player){
|
|
if(player){
|
|
player->position_changed = false;
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(player != client->GetPlayer() && client->GetPlayer()->WasSentSpawn(player->GetID())){
|
|
if(client->GetVersion() > 373) {
|
|
EQ2Packet* outapp = player->player_position_update_packet(client->GetPlayer(), client->GetVersion(), true);
|
|
if(outapp)
|
|
client->QueuePacket(outapp);
|
|
}
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SendRaidSheetChanges(){
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
Client* client = (Client*)(*client_itr);
|
|
if(client && client->IsConnected() && client->GetPlayer()->GetRaidSheetChanged()) {
|
|
client->GetPlayer()->SetRaidSheetChanged(false);
|
|
EQ2Packet* packet = client->GetPlayer()->GetRaidUpdatePacket(client->GetVersion());
|
|
if(packet) {
|
|
client->QueuePacket(packet);
|
|
}
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendCharSheetChanges(){
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++)
|
|
SendCharSheetChanges(*client_itr);
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendCharSheetChanges(Client* client){
|
|
if(client && client->IsConnected()){
|
|
if(client->GetPlayer()->GetCharSheetChanged()) {
|
|
client->GetPlayer()->SetCharSheetChanged(false);
|
|
ClientPacketFunctions::SendCharacterSheet(client);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 ZoneServer::CalculateSpawnGroup(SpawnLocation* spawnlocation, bool respawn)
|
|
{
|
|
int32 group = 0;
|
|
list<int32>* groups_at_location = GetSpawnGroupsByLocation(spawnlocation->placement_id);
|
|
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
|
|
|
if(groups_at_location){
|
|
list<int32>::iterator group_location_itr;
|
|
float chance = 0;
|
|
float total_chance = 0;
|
|
map<int32, float> tmp_chances;
|
|
set<int32>* associated_groups = 0;
|
|
for (group_location_itr = groups_at_location->begin(); group_location_itr != groups_at_location->end(); group_location_itr++) {
|
|
if(tmp_chances.count(*group_location_itr) > 0)
|
|
continue;
|
|
associated_groups = GetAssociatedGroups(*group_location_itr);
|
|
if(associated_groups){
|
|
set<int32>::iterator group_itr;
|
|
for (group_itr = associated_groups->begin(); group_itr != associated_groups->end(); group_itr++) {
|
|
chance = GetSpawnGroupChance(*group_itr);
|
|
if(chance > 0){
|
|
total_chance += chance;
|
|
tmp_chances[*group_itr] = chance;
|
|
}
|
|
else
|
|
tmp_chances[*group_itr] = 0;
|
|
}
|
|
}
|
|
else{ //single group, no associations
|
|
chance = GetSpawnGroupChance(*group_location_itr);
|
|
total_chance += chance;
|
|
tmp_chances[*group_location_itr] = chance;
|
|
}
|
|
}
|
|
if(tmp_chances.size() > 1){
|
|
//set the default for any chances not set
|
|
map<int32, float>::iterator itr2;
|
|
for(itr2 = tmp_chances.begin(); itr2 != tmp_chances.end(); itr2++){
|
|
if(itr2->second == 0){
|
|
total_chance += 100/tmp_chances.size();
|
|
tmp_chances[itr2->first] = (float)(100 / tmp_chances.size());
|
|
}
|
|
}
|
|
}
|
|
if(tmp_chances.size() > 1){
|
|
float roll = (float)(rand()%((int32)total_chance));
|
|
map<int32, float>::iterator itr3;
|
|
for (itr3 = tmp_chances.begin(); itr3 != tmp_chances.end(); itr3++){
|
|
if(itr3->second >= roll){
|
|
group = itr3->first;
|
|
break;
|
|
}
|
|
else
|
|
roll -= itr3->second;
|
|
}
|
|
}
|
|
else if(tmp_chances.size() == 1)
|
|
group = tmp_chances.begin()->first;
|
|
}
|
|
if(group > 0){
|
|
map<int32, int32>* locations = GetSpawnLocationsByGroup(group);
|
|
if(locations){
|
|
map<int32, int32>::iterator itr;
|
|
Spawn* spawn = 0;
|
|
Spawn* leader = 0;
|
|
|
|
MSpawnLocationList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = locations->begin(); itr != locations->end(); itr++) {
|
|
if(spawn_location_list.count(itr->second) > 0){
|
|
spawn = ProcessSpawnLocation(spawn_location_list[itr->second], nullptr, nullptr, nullptr, nullptr, nullptr, respawn);
|
|
if(!leader && spawn)
|
|
leader = spawn;
|
|
if(leader)
|
|
leader->AddSpawnToGroup(spawn);
|
|
if(spawn){
|
|
//if(spawn_group_map.count(group) == 0)
|
|
// spawn_group_map.Put(group, new MutexList<Spawn*>());
|
|
MutexList<int32>* groupList = &spawn_group_map.Get(group);
|
|
groupList->Add(spawn->GetID());
|
|
spawn->SetSpawnGroupID(group);
|
|
}
|
|
}
|
|
}
|
|
MSpawnLocationList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
|
|
return group;
|
|
}
|
|
|
|
void ZoneServer::ProcessSpawnLocation(int32 location_id, map<int32,int32>* instNPCs, map<int32,int32>* instGroundSpawns, map<int32,int32>* instObjSpawns, map<int32,int32>* instWidgetSpawns, map<int32,int32>* instSignSpawns, bool respawn)
|
|
{
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
|
|
|
MSpawnLocationList.readlock(__FUNCTION__, __LINE__);
|
|
|
|
if(spawn_location_list.count(location_id) > 0)
|
|
{
|
|
if(respawn) //see if there are any spawns still in game associated with this spawn's group, if so, dont spawn this
|
|
{
|
|
list<int32>* groups = GetSpawnGroupsByLocation(spawn_location_list[location_id]->placement_id);
|
|
|
|
if(groups)
|
|
{
|
|
set<int32>* associated_groups = 0;
|
|
bool should_spawn = true;
|
|
list<int32>::iterator itr;
|
|
for (itr = groups->begin(); itr != groups->end(); itr++) {
|
|
associated_groups = GetAssociatedGroups(*itr);
|
|
|
|
if(associated_groups)
|
|
{
|
|
set<int32>::iterator assoc_itr;
|
|
for (assoc_itr = associated_groups->begin(); assoc_itr != associated_groups->end(); assoc_itr++) {
|
|
if(spawn_group_map.count(*assoc_itr) > 0 && spawn_group_map.Get(*assoc_itr).size() > 0)
|
|
should_spawn = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(should_spawn)
|
|
CalculateSpawnGroup(spawn_location_list[location_id]);
|
|
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
// need to unlock the list before we exit the function
|
|
MSpawnLocationList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return;
|
|
}
|
|
}
|
|
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
|
|
ProcessSpawnLocation(spawn_location_list[location_id], instNPCs, instGroundSpawns, instObjSpawns, instWidgetSpawns, instSignSpawns, respawn);
|
|
}
|
|
MSpawnLocationList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
Spawn* ZoneServer::ProcessSpawnLocation(SpawnLocation* spawnlocation, map<int32,int32>* instNPCs, map<int32,int32>* instGroundSpawns, map<int32,int32>* instObjSpawns, map<int32,int32>* instWidgetSpawns, map<int32,int32>* instSignSpawns, bool respawn)
|
|
{
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
|
|
|
if(!spawnlocation)
|
|
return 0;
|
|
|
|
Spawn* spawn = 0;
|
|
float rand_number = MakeRandomFloat(0, spawnlocation->total_percentage);
|
|
|
|
for(int32 i=0;i<spawnlocation->entities.size();i++)
|
|
{
|
|
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;
|
|
|
|
if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC)
|
|
spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id);
|
|
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT)
|
|
spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id);
|
|
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET)
|
|
spawnTime = database.CheckSpawnRemoveInfo(instWidgetSpawns,spawnlocation->entities[i]->spawn_location_id);
|
|
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN)
|
|
spawnTime = database.CheckSpawnRemoveInfo(instSignSpawns,spawnlocation->entities[i]->spawn_location_id);
|
|
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN)
|
|
spawnTime = database.CheckSpawnRemoveInfo(instGroundSpawns,spawnlocation->entities[i]->spawn_location_id);
|
|
|
|
if(spawnTime == 0) { // don't respawn
|
|
return nullptr;
|
|
}
|
|
else if(spawnTime > 1) { // if not 1, respawn after time
|
|
AddRespawn(spawnlocation->entities[i]->spawn_location_id, spawnTime);
|
|
return nullptr;
|
|
}
|
|
|
|
if (spawnlocation->conditional > 0) {
|
|
if ((spawnlocation->conditional & SPAWN_CONDITIONAL_DAY) == SPAWN_CONDITIONAL_DAY && isDusk)
|
|
continue;
|
|
|
|
if ((spawnlocation->conditional & SPAWN_CONDITIONAL_NIGHT) == SPAWN_CONDITIONAL_NIGHT && !isDusk)
|
|
continue;
|
|
|
|
if ((spawnlocation->conditional & SPAWN_CONDITIONAL_DAY) == SPAWN_CONDITIONAL_NOT_RAINING && rain >= 0.75f)
|
|
continue;
|
|
|
|
if ((spawnlocation->conditional & SPAWN_CONDITIONAL_DAY) == SPAWN_CONDITIONAL_RAINING && rain < 0.75f)
|
|
continue;
|
|
}
|
|
|
|
if (spawnlocation->entities[i]->spawn_percentage >= rand_number) {
|
|
if (spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC)
|
|
spawn = AddNPCSpawn(spawnlocation, spawnlocation->entities[i]);
|
|
else if (spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN)
|
|
spawn = AddGroundSpawn(spawnlocation, spawnlocation->entities[i]);
|
|
else if (spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT)
|
|
spawn = AddObjectSpawn(spawnlocation, spawnlocation->entities[i]);
|
|
else if (spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET)
|
|
spawn = AddWidgetSpawn(spawnlocation, spawnlocation->entities[i]);
|
|
else if (spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN)
|
|
spawn = AddSignSpawn(spawnlocation, spawnlocation->entities[i]);
|
|
|
|
if (GetInstanceType() == PERSONAL_HOUSE_INSTANCE)
|
|
database.GetHouseSpawnInstanceData(this, spawn);
|
|
|
|
if(spawn && spawn->IsOmittedByDBFlag())
|
|
{
|
|
LogWrite(SPAWN__WARNING, 0, "Spawn", "Spawn (%u) in spawn location id %u was skipped due to a missing expansion / holiday flag being met (ZoneServer::ProcessSpawnLocation)", spawnlocation->entities[i]->spawn_id, spawnlocation->entities[i]->spawn_location_id);
|
|
safe_delete(spawn);
|
|
spawn = 0;
|
|
continue;
|
|
}
|
|
else if (!spawn)
|
|
{
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "Error adding spawn by spawn location to zone %s with location id %u, spawn id %u, spawn type %u.", GetZoneName(), spawnlocation->entities[i]->spawn_location_id, spawnlocation->entities[i]->spawn_id, spawnlocation->entities[i]->spawn_type);
|
|
continue;
|
|
}
|
|
|
|
if (spawn)
|
|
{
|
|
if(respawn)
|
|
CallSpawnScript(spawn, SPAWN_SCRIPT_RESPAWN);
|
|
else
|
|
CallSpawnScript(spawn, SPAWN_SCRIPT_SPAWN);
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
rand_number -= spawnlocation->entities[i]->spawn_percentage;
|
|
}
|
|
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
|
|
return spawn;
|
|
}
|
|
|
|
|
|
Spawn* ZoneServer::ProcessInstanceSpawnLocation(SpawnLocation* spawnlocation, map<int32,int32>* instNPCs, map<int32,int32>* instGroundSpawns, map<int32,int32>* instObjSpawns, map<int32,int32>* instWidgetSpawns, map<int32,int32>* instSignSpawns, bool respawn)
|
|
{
|
|
if(!spawnlocation)
|
|
return 0;
|
|
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
|
|
|
Spawn* spawn = 0;
|
|
float rand_number = MakeRandomFloat(0, spawnlocation->total_percentage);
|
|
|
|
for(int32 i=0;i<spawnlocation->entities.size();i++)
|
|
{
|
|
if(spawnlocation->entities[i]->spawn_percentage == 0)
|
|
continue;
|
|
|
|
int32 spawnTime = 0;
|
|
|
|
if(spawnlocation->entities[i]->spawn_percentage >= rand_number)
|
|
{
|
|
if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_NPC &&
|
|
(spawnTime = database.CheckSpawnRemoveInfo(instNPCs,spawnlocation->entities[i]->spawn_location_id)) > 0)
|
|
spawn = AddNPCSpawn(spawnlocation, spawnlocation->entities[i]);
|
|
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_GROUNDSPAWN &&
|
|
(spawnTime = database.CheckSpawnRemoveInfo(instGroundSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)
|
|
spawn = AddGroundSpawn(spawnlocation, spawnlocation->entities[i]);
|
|
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_OBJECT &&
|
|
(spawnTime = database.CheckSpawnRemoveInfo(instObjSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)
|
|
spawn = AddObjectSpawn(spawnlocation, spawnlocation->entities[i]);
|
|
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_WIDGET &&
|
|
(spawnTime = database.CheckSpawnRemoveInfo(instWidgetSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)
|
|
spawn = AddWidgetSpawn(spawnlocation, spawnlocation->entities[i]);
|
|
else if(spawnlocation->entities[i]->spawn_type == SPAWN_ENTRY_TYPE_SIGN &&
|
|
(spawnTime = database.CheckSpawnRemoveInfo(instSignSpawns,spawnlocation->entities[i]->spawn_location_id)) > 0)
|
|
spawn = AddSignSpawn(spawnlocation, spawnlocation->entities[i]);
|
|
|
|
if(spawn && spawn->IsOmittedByDBFlag())
|
|
{
|
|
LogWrite(SPAWN__WARNING, 0, "Spawn", "Spawn (%u) in spawn location id %u was skipped due to a missing expansion / holiday flag being met (ZoneServer::ProcessInstanceSpawnLocation)", spawnlocation->entities[i]->spawn_id, spawnlocation->entities[i]->spawn_location_id);
|
|
safe_delete(spawn);
|
|
spawn = 0;
|
|
continue;
|
|
}
|
|
|
|
if (GetInstanceType() == PERSONAL_HOUSE_INSTANCE)
|
|
database.GetHouseSpawnInstanceData(this, spawn);
|
|
|
|
const char* script = 0;
|
|
|
|
for(int x=0;x<3;x++)
|
|
{
|
|
switch(x)
|
|
{
|
|
case 0:
|
|
script = world.GetSpawnEntryScript(spawnlocation->entities[i]->spawn_entry_id);
|
|
break;
|
|
case 1:
|
|
script = world.GetSpawnLocationScript(spawnlocation->entities[i]->spawn_location_id);
|
|
break;
|
|
case 2:
|
|
script = world.GetSpawnScript(spawnlocation->entities[i]->spawn_id);
|
|
break;
|
|
}
|
|
|
|
if(spawn && script && lua_interface->GetSpawnScript(script) != 0)
|
|
{
|
|
spawn->SetSpawnScript(string(script));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(spawn)
|
|
{
|
|
if (respawn)
|
|
CallSpawnScript(spawn, SPAWN_SCRIPT_RESPAWN);
|
|
else
|
|
CallSpawnScript(spawn, SPAWN_SCRIPT_SPAWN);
|
|
|
|
if ( spawnTime > 1 )
|
|
{
|
|
spawn->SetRespawnTime(spawnTime);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
rand_number -= spawnlocation->entities[i]->spawn_percentage;
|
|
}
|
|
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
|
|
return spawn;
|
|
}
|
|
|
|
void ZoneServer::ProcessSpawnLocations()
|
|
{
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
|
|
|
map<int32,int32>* instNPCs = NULL;
|
|
map<int32,int32>* instGroundSpawns = NULL;
|
|
map<int32,int32>* instObjSpawns = NULL;
|
|
map<int32,int32>* instWidgetSpawns = NULL;
|
|
map<int32,int32>* instSignSpawns = NULL;
|
|
|
|
if ( this->IsInstanceZone() )
|
|
{
|
|
LogWrite(SPAWN__DEBUG, 0, "Spawn", "Processing Instance Removed Spawns...");
|
|
instNPCs = database.GetInstanceRemovedSpawns(this->GetInstanceID() , SPAWN_ENTRY_TYPE_NPC );
|
|
instGroundSpawns = database.GetInstanceRemovedSpawns(this->GetInstanceID() , SPAWN_ENTRY_TYPE_GROUNDSPAWN );
|
|
instObjSpawns = database.GetInstanceRemovedSpawns(this->GetInstanceID() , SPAWN_ENTRY_TYPE_OBJECT );
|
|
instWidgetSpawns = database.GetInstanceRemovedSpawns(this->GetInstanceID() , SPAWN_ENTRY_TYPE_WIDGET );
|
|
instSignSpawns = database.GetInstanceRemovedSpawns(this->GetInstanceID() , SPAWN_ENTRY_TYPE_SIGN );
|
|
}
|
|
else {
|
|
instNPCs = database.GetPersistedSpawns(this->GetZoneID() , SPAWN_ENTRY_TYPE_NPC );
|
|
instGroundSpawns = database.GetPersistedSpawns(this->GetZoneID() , SPAWN_ENTRY_TYPE_GROUNDSPAWN );
|
|
instObjSpawns = database.GetPersistedSpawns(this->GetZoneID() , SPAWN_ENTRY_TYPE_OBJECT );
|
|
instWidgetSpawns = database.GetPersistedSpawns(this->GetZoneID() , SPAWN_ENTRY_TYPE_WIDGET );
|
|
instSignSpawns = database.GetPersistedSpawns(this->GetZoneID() , SPAWN_ENTRY_TYPE_SIGN );
|
|
}
|
|
|
|
map<int32, bool> processed_spawn_locations;
|
|
map<int32, SpawnLocation*>::iterator itr;
|
|
MSpawnLocationList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_location_list.begin(); itr != spawn_location_list.end(); itr++) {
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "while spawn_location_list itr (#%u)", spawn_location_list.size());
|
|
|
|
if(itr->second && processed_spawn_locations.count(itr->second->placement_id) > 0) //if we processed one spawn in a spawn group, we processed them all for that group
|
|
continue;
|
|
|
|
if(itr->second && spawn_location_groups.count(itr->second->placement_id) > 0)
|
|
{
|
|
int32 group_id = CalculateSpawnGroup(itr->second);
|
|
|
|
if(group_id)
|
|
{
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "is group_id");
|
|
set<int32>* associated_groups = GetAssociatedGroups(group_id);
|
|
|
|
if(associated_groups)
|
|
{
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "is associated_groups");
|
|
vector<int32>* associated_locations = GetAssociatedLocations(associated_groups);
|
|
|
|
if(associated_locations)
|
|
{
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "is associated_locations");
|
|
for(int32 i=0;i<associated_locations->size();i++)
|
|
{
|
|
LogWrite(SPAWN__DEBUG, 5, "Spawn", "Loading processed_spawn_locations...");
|
|
processed_spawn_locations[associated_locations->at(i)] = true;
|
|
}
|
|
|
|
safe_delete(associated_locations);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( this->IsInstanceZone() )
|
|
{
|
|
//LogWrite(SPAWN__DEBUG, 5, "Spawn", "ProcessInstanceSpawnLocation (%u)...", itr->second->placement_id);
|
|
ProcessInstanceSpawnLocation(itr->second,instNPCs,instGroundSpawns,instObjSpawns,instWidgetSpawns,instSignSpawns);
|
|
}
|
|
else
|
|
{
|
|
//LogWrite(SPAWN__DEBUG, 5, "Spawn", "ProcessSpawnLocation (%u)...", itr->second->placement_id);
|
|
ProcessSpawnLocation(itr->second,instNPCs,instGroundSpawns,instObjSpawns,instWidgetSpawns,instSignSpawns);
|
|
}
|
|
}
|
|
}
|
|
MSpawnLocationList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
safe_delete(instNPCs);
|
|
safe_delete(instGroundSpawns);
|
|
safe_delete(instObjSpawns);
|
|
safe_delete(instWidgetSpawns);
|
|
safe_delete(instSignSpawns);
|
|
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
}
|
|
|
|
void ZoneServer::AddLoot(NPC* npc, Spawn* killer, GroupLootMethod loot_method, int8 item_rarity, int32 group_id){
|
|
// this function is ran twice, first on spawn of mob, then at death of mob (gray mob check and no_drop_quest_completed_id check)
|
|
|
|
// first we see if the skipping of gray mobs loot is enabled, then we move all non body drops
|
|
if(killer)
|
|
{
|
|
npc->SetLootMethod(loot_method, item_rarity, group_id);
|
|
int8 skip_loot_gray_mob_flag = rule_manager.GetZoneRule(GetZoneID(), R_Loot, SkipLootGrayMob)->GetInt8();
|
|
if(skip_loot_gray_mob_flag) {
|
|
Player* player = 0;
|
|
if(killer->IsPlayer())
|
|
player = (Player*)killer;
|
|
else if(killer->IsPet()) {
|
|
Spawn* owner = ((Entity*)killer)->GetOwner();
|
|
if(owner->IsPlayer())
|
|
player = (Player*)owner;
|
|
}
|
|
if(player) {
|
|
int8 difficulty = player->GetArrowColor(npc->GetLevel());
|
|
if(difficulty == ARROW_COLOR_GRAY) {
|
|
npc->ClearNonBodyLoot();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for starting loot of Spawn and death of Spawn loot (no_drop_quest_completed_id)
|
|
vector<int32> loot_tables = GetSpawnLootList(npc->GetDatabaseID(), GetZoneID(), npc->GetLevel(), race_types_list.GetRaceType(npc->GetModelType()), npc);
|
|
if(loot_tables.size() > 0){
|
|
vector<LootDrop*>* loot_drops = 0;
|
|
vector<LootDrop*>::iterator loot_drop_itr;
|
|
LootTable* table = 0;
|
|
vector<int32>::iterator loot_list_itr;
|
|
float chancecoin = 0;
|
|
float chancetable = 0;
|
|
float chancedrop = 0;
|
|
float chancetally = 0;
|
|
float droptally = 0;
|
|
// the following loop,loops through each table
|
|
for(loot_list_itr = loot_tables.begin(); loot_list_itr != loot_tables.end(); loot_list_itr++){
|
|
table = GetLootTable(*loot_list_itr);
|
|
// if killer is assigned this is on-death, we already assigned coin
|
|
if(!killer && table && table->maxcoin > 0){
|
|
chancecoin = rand()%100;
|
|
if(table->coin_probability >= chancecoin){
|
|
if(table->maxcoin > table->mincoin)
|
|
npc->AddLootCoins(table->mincoin + rand()%(table->maxcoin - table->mincoin));
|
|
}
|
|
}
|
|
int numberchances = 1;
|
|
|
|
//if (table->lootdrop_probability == 100){ }
|
|
//else
|
|
//chancetally += table->lootdrop_probability;
|
|
int maxchance = 0;
|
|
if (table) {
|
|
maxchance = table->maxlootitems;
|
|
for (numberchances; numberchances <= maxchance; numberchances++) {
|
|
chancetable = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / 100));
|
|
//LogWrite(PLAYER__DEBUG, 0, "Player", "Table Chance: '%f'", chancetable);
|
|
float droppercenttotal = 0;
|
|
//--------------------------------------------------------------------------------------------------------------------------------------------------------------
|
|
if (table->lootdrop_probability == 100 || table->lootdrop_probability >= chancetable) {
|
|
|
|
//LogWrite(PLAYER__DEBUG, 0, "Player", "Probability:%f Table Chance: '%f'", table->lootdrop_probability, chancetable);
|
|
loot_drops = GetLootDrops(*loot_list_itr);
|
|
if (loot_drops && loot_drops->size() > 0) {
|
|
LootDrop* drop = 0;
|
|
int16 count = 0;
|
|
|
|
std::shuffle(loot_drops->begin(), loot_drops->end(), std::default_random_engine(Timer::GetCurrentTime2()));
|
|
|
|
int16 IC = 0;
|
|
for (loot_drop_itr = loot_drops->begin(); loot_drop_itr != loot_drops->end(); loot_drop_itr++) {
|
|
drop = *loot_drop_itr;
|
|
droppercenttotal += drop->probability;
|
|
}
|
|
|
|
|
|
int droplistsize = loot_drops->size();
|
|
float chancedroptally = 0;
|
|
bool breakIterMaxLoot = false;
|
|
chancedrop = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / 100));
|
|
for (loot_drop_itr = loot_drops->begin(); loot_drop_itr != loot_drops->end(); loot_drop_itr++) {
|
|
drop = *loot_drop_itr;
|
|
|
|
// if no killer is provided, then we are instantiating the spawn loot, quest related loot should be added on death of the spawn to check against spawn/group members
|
|
if(drop->no_drop_quest_completed_id && killer == nullptr)
|
|
continue;
|
|
else if(!drop->no_drop_quest_completed_id && killer) // skip since this doesn't have a quest id attached and we are doing after-math of death loot additions
|
|
continue;
|
|
else if(killer && drop->no_drop_quest_completed_id) // check if the player already completed quest related to item
|
|
{
|
|
Player* player = nullptr;
|
|
if(killer->IsPlayer())
|
|
{
|
|
player = (Player*)killer;
|
|
// player has already completed the quest
|
|
if(player->HasQuestBeenCompleted(drop->no_drop_quest_completed_id) && !player->GetGroupMemberInfo())
|
|
{
|
|
LogWrite(PLAYER__DEBUG, 0, "Player", "%s: Player has completed quest %u, skipping loot item %u", npc->GetName(), drop->no_drop_quest_completed_id, drop->item_id);
|
|
continue;
|
|
}
|
|
else if(player->GetGroupMemberInfo() && world.GetGroupManager()->HasGroupCompletedQuest(player->GetGroupMemberInfo()->group_id, drop->no_drop_quest_completed_id))
|
|
{
|
|
LogWrite(PLAYER__DEBUG, 0, "Player", "%s: Group %u has completed quest %u, skipping loot item %u", npc->GetName(), player->GetGroupMemberInfo()->group_id, drop->no_drop_quest_completed_id, drop->item_id);
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LogWrite(PLAYER__DEBUG, 0, "Player", "%s: Killer is not a player, skipping loot item %u", npc->GetName(), drop->item_id);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (npc->HasLootItemID(drop->item_id))
|
|
continue;
|
|
|
|
if (droppercenttotal >= 100)
|
|
droppercenttotal = 100;
|
|
chancedroptally += 100 / droppercenttotal * drop->probability;
|
|
//chancedrop = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / 100));
|
|
//LogWrite(PLAYER__DEBUG, 0, "Player", "Loot drop: '%i' Chance: %f Prob tally: %f min: %f", drop, chancedrop, chancedroptally, chancedroptally - drop->probability);
|
|
if ((chancedroptally == 100) || ((chancedroptally >= chancedrop) && (chancedroptally - (100 / droppercenttotal * drop->probability)) <= chancedrop)) {
|
|
|
|
//LogWrite(PLAYER__DEBUG, 0, "Player", "Loot drop: '%i' Chance: %f Prob: %f We have a loot drop winner", drop, chancedrop, chancedroptally);
|
|
count++;
|
|
npc->AddLootItem(drop->item_id, drop->item_charges);
|
|
//LogWrite(PLAYER__DEBUG, 0, "Player", "loot Count: '%i'",count);
|
|
//LogWrite(MISC__TODO, 1, "TODO", "Auto-Equip new looted items\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
|
|
//if(drop->equip_item)
|
|
|
|
}
|
|
// so many items on this table break out and cap it!
|
|
if (table->maxlootitems > 0 && count >= table->maxlootitems) {
|
|
breakIterMaxLoot = true;
|
|
break;
|
|
}
|
|
}
|
|
// hit our max item drop for this table already, break out of numberchances
|
|
if(breakIterMaxLoot) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::DeterminePosition(SpawnLocation* spawnlocation, Spawn* spawn){
|
|
if(!spawn || !spawnlocation)
|
|
return;
|
|
|
|
int offset = 0;
|
|
if(spawnlocation->x_offset > 0){
|
|
//since rand() deals with integers only, we are going to divide by 1000 later so that we can use fractions of integers
|
|
offset = (int)((spawnlocation->x_offset*1000)+1);
|
|
spawn->SetX(spawnlocation->x + ((float)(rand()%offset - rand()%offset))/1000);
|
|
}
|
|
else
|
|
spawn->SetX(spawnlocation->x);
|
|
if(spawnlocation->y_offset > 0){
|
|
//since rand() deals with integers only, we are going to divide by 1000 later so that we can use fractions of integers
|
|
offset = (int)((spawnlocation->y_offset*1000)+1);
|
|
spawn->SetY(spawnlocation->y + ((float)(rand()%offset - rand()%offset))/1000);
|
|
}
|
|
else
|
|
spawn->SetY(spawnlocation->y, true, true);
|
|
if(spawnlocation->z_offset > 0){
|
|
//since rand() deals with integers only, we are going to divide by 1000 later so that we can use fractions of integers
|
|
offset = (int)((spawnlocation->z_offset*1000)+1);
|
|
spawn->SetZ(spawnlocation->z + ((float)(rand()%offset - rand()%offset))/1000);
|
|
}
|
|
else
|
|
spawn->SetZ(spawnlocation->z);
|
|
spawn->SetHeading(spawnlocation->heading);
|
|
spawn->SetPitch(spawnlocation->pitch);
|
|
spawn->SetRoll(spawnlocation->roll);
|
|
spawn->SetSpawnOrigX(spawn->GetX());
|
|
spawn->SetSpawnOrigY(spawn->GetY());
|
|
spawn->SetSpawnOrigZ(spawn->GetZ());
|
|
spawn->SetSpawnOrigHeading(spawn->GetHeading());
|
|
spawn->SetSpawnOrigPitch(spawnlocation->pitch);
|
|
spawn->SetSpawnOrigRoll(spawnlocation->roll);
|
|
spawn->SetLocation(spawnlocation->grid_id);
|
|
spawn->SetSpawnLocationPlacementID(spawnlocation->placement_id);
|
|
}
|
|
|
|
NPC* ZoneServer::AddNPCSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnentry){
|
|
LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__);
|
|
NPC* npc = GetNewNPC(spawnentry->spawn_id);
|
|
if(npc && !npc->IsOmittedByDBFlag()){
|
|
InfoStruct* info = npc->GetInfoStruct();
|
|
DeterminePosition(spawnlocation, npc);
|
|
npc->SetDatabaseID(spawnentry->spawn_id);
|
|
npc->SetSpawnLocationID(spawnentry->spawn_location_id);
|
|
npc->SetSpawnEntryID(spawnentry->spawn_entry_id);
|
|
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
|
|
if(spawnentry->hp_override > 0){
|
|
npc->SetHP(spawnentry->hp_override);
|
|
}
|
|
if(spawnentry->lvl_override > 0){
|
|
npc->SetLevel(spawnentry->lvl_override);
|
|
}
|
|
if(spawnentry->mp_override > 0){
|
|
npc->SetPower(spawnentry->mp_override);
|
|
}
|
|
if(spawnentry->str_override > 0){
|
|
info->set_str_base(spawnentry->str_override);
|
|
info->set_str(spawnentry->str_override);
|
|
}
|
|
if(spawnentry->sta_override > 0){
|
|
info->set_sta_base(spawnentry->sta_override);
|
|
info->set_sta(spawnentry->sta_override);
|
|
}
|
|
if(spawnentry->wis_override > 0){
|
|
info->set_wis_base(spawnentry->wis_override);
|
|
info->set_wis(spawnentry->wis_override);
|
|
}
|
|
if(spawnentry->int_override > 0){
|
|
info->set_intel_base(spawnentry->int_override);
|
|
info->set_intel(spawnentry->int_override);
|
|
}
|
|
if(spawnentry->agi_override > 0){
|
|
info->set_agi_base(spawnentry->agi_override);
|
|
info->set_agi(spawnentry->agi_override);
|
|
}
|
|
if(spawnentry->heat_override > 0){
|
|
info->set_heat_base(spawnentry->heat_override);
|
|
info->set_heat(spawnentry->heat_override);
|
|
}
|
|
if(spawnentry->cold_override > 0){
|
|
info->set_cold_base(spawnentry->cold_override);
|
|
info->set_cold(spawnentry->cold_override);
|
|
}
|
|
if(spawnentry->magic_override > 0){
|
|
info->set_magic_base(spawnentry->magic_override);
|
|
info->set_magic(spawnentry->magic_override);
|
|
}
|
|
if(spawnentry->mental_override > 0){
|
|
info->set_mental_base(spawnentry->mental_override);
|
|
info->set_mental(spawnentry->mental_override);
|
|
}
|
|
if(spawnentry->divine_override > 0){
|
|
info->set_divine_base(spawnentry->divine_override);
|
|
info->set_divine(spawnentry->divine_override);
|
|
}
|
|
if(spawnentry->disease_override > 0){
|
|
info->set_disease_base(spawnentry->disease_override);
|
|
info->set_disease(spawnentry->disease_override);
|
|
}
|
|
if(spawnentry->poison_override > 0){
|
|
info->set_poison_base(spawnentry->poison_override);
|
|
info->set_poison(spawnentry->poison_override);
|
|
}
|
|
if(spawnentry->difficulty_override > 0){
|
|
npc->SetDifficulty(spawnentry->difficulty_override, 1);
|
|
}
|
|
if (spawnentry->expire_time > 0)
|
|
AddSpawnExpireTimer(npc, spawnentry->expire_time, spawnentry->expire_offset);
|
|
AddLoot(npc);
|
|
|
|
SetSpawnScript(spawnentry, npc);
|
|
|
|
CallSpawnScript(npc, SPAWN_SCRIPT_PRESPAWN);
|
|
|
|
AddSpawn(npc);
|
|
}
|
|
LogWrite(SPAWN__TRACE, 1, "Spawn", "Exit %s", __FUNCTION__);
|
|
return npc;
|
|
}
|
|
|
|
vector<int32>* ZoneServer::GetAssociatedLocations(set<int32>* groups){
|
|
vector<int32>* ret = 0;
|
|
LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__);
|
|
if(groups){
|
|
int32 group_id = 0;
|
|
set<int32>::iterator group_itr;
|
|
for (group_itr = groups->begin(); group_itr != groups->end(); group_itr++) {
|
|
if(!ret)
|
|
ret = new vector<int32>();
|
|
group_id = *group_itr;
|
|
|
|
MSpawnGroupLocations.readlock(__FUNCTION__, __LINE__);
|
|
if(spawn_group_locations.count(group_id) > 0){
|
|
map<int32, int32>::iterator itr;
|
|
for (itr = spawn_group_locations[group_id]->begin(); itr != spawn_group_locations[group_id]->end(); itr++) {
|
|
ret->push_back(itr->first);
|
|
}
|
|
}
|
|
MSpawnGroupLocations.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
LogWrite(SPAWN__TRACE, 1, "Spawn", "Exit %s", __FUNCTION__);
|
|
return ret;
|
|
}
|
|
|
|
set<int32>* ZoneServer::GetAssociatedGroups(int32 group_id) {
|
|
set<int32>* ret = 0;
|
|
MSpawnGroupAssociation.readlock(__FUNCTION__, __LINE__);
|
|
LogWrite(SPAWN__TRACE, 1, "Spawn", "Enter %s", __FUNCTION__);
|
|
if(spawn_group_associations.count(group_id) > 0)
|
|
ret = spawn_group_associations[group_id];
|
|
LogWrite(SPAWN__TRACE, 1, "Spawn", "Exit %s", __FUNCTION__);
|
|
MSpawnGroupAssociation.releasereadlock(__FUNCTION__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
map<int32, int32>* ZoneServer::GetSpawnLocationsByGroup(int32 group_id) {
|
|
map<int32, int32>* ret = 0;
|
|
|
|
MSpawnGroupLocations.readlock(__FUNCTION__, __LINE__);
|
|
if(spawn_group_locations.count(group_id) > 0)
|
|
ret = spawn_group_locations[group_id];
|
|
MSpawnGroupLocations.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
list<int32>* ZoneServer::GetSpawnGroupsByLocation(int32 location_id){
|
|
list<int32>* ret = 0;
|
|
|
|
MSpawnLocationGroups.readlock(__FUNCTION__, __LINE__);
|
|
if(spawn_location_groups.count(location_id) > 0)
|
|
ret = spawn_location_groups[location_id];
|
|
MSpawnLocationGroups.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
float ZoneServer::GetSpawnGroupChance(int32 group_id){
|
|
float ret = -1;
|
|
|
|
MSpawnGroupChances.readlock(__FUNCTION__, __LINE__);
|
|
if(spawn_group_chances.count(group_id) > 0)
|
|
ret = spawn_group_chances[group_id];
|
|
MSpawnGroupChances.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::AddSpawnGroupChance(int32 group_id, float percent){
|
|
MSpawnGroupChances.writelock(__FUNCTION__, __LINE__);
|
|
spawn_group_chances[group_id] = percent;
|
|
MSpawnGroupChances.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::AddSpawnGroupAssociation(int32 group_id1, int32 group_id2) {
|
|
MSpawnGroupAssociation.writelock(__FUNCTION__, __LINE__);
|
|
//Check if we already have containers for these group ids, if not create them
|
|
if (spawn_group_associations.count(group_id1) == 0)
|
|
spawn_group_associations[group_id1] = new set<int32>;
|
|
if (spawn_group_associations.count(group_id2) == 0)
|
|
spawn_group_associations[group_id2] = new set<int32>;
|
|
|
|
//Associate groups 1 and 2 now
|
|
set<int32>* group_1 = spawn_group_associations.find(group_id1)->second;
|
|
set<int32>* group_2 = spawn_group_associations.find(group_id2)->second;
|
|
group_1->insert(group_id2);
|
|
group_2->insert(group_id1);
|
|
|
|
//Associate the remaining groups together
|
|
set<int32>::iterator itr;
|
|
for (itr = group_1->begin(); itr != group_1->end(); itr++){
|
|
group_2->insert(*itr);
|
|
map<int32, set<int32>*>::iterator assoc_itr = spawn_group_associations.find(*itr);
|
|
if (assoc_itr != spawn_group_associations.end())
|
|
assoc_itr->second->insert(group_id2);
|
|
else {
|
|
set<int32>* new_set = new set<int32>;
|
|
spawn_group_associations[*itr] = new_set;
|
|
new_set->insert(group_id2);
|
|
}
|
|
}
|
|
for (itr = group_2->begin(); itr != group_2->end(); itr++){
|
|
group_1->insert(*itr);
|
|
map<int32, set<int32>*>::iterator assoc_itr = spawn_group_associations.find(*itr);
|
|
if (assoc_itr != spawn_group_associations.end())
|
|
assoc_itr->second->insert(group_id1);
|
|
else {
|
|
set<int32>* new_set = new set<int32>;
|
|
spawn_group_associations[*itr] = new_set;
|
|
new_set->insert(group_id1);
|
|
}
|
|
}
|
|
MSpawnGroupAssociation.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::AddSpawnGroupLocation(int32 group_id, int32 location_id, int32 spawn_location_id) {
|
|
MSpawnGroupLocations.writelock(__FUNCTION__, __LINE__);
|
|
if(spawn_group_locations.count(group_id) == 0)
|
|
spawn_group_locations[group_id] = new map<int32, int32>();
|
|
(*spawn_group_locations[group_id])[location_id] = spawn_location_id;
|
|
MSpawnGroupLocations.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
MSpawnLocationGroups.writelock(__FUNCTION__, __LINE__);
|
|
if(spawn_location_groups.count(location_id) == 0)
|
|
spawn_location_groups[location_id] = new list<int32>();
|
|
spawn_location_groups[location_id]->push_back(group_id);
|
|
MSpawnLocationGroups.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
MSpawnGroupAssociation.writelock(__FUNCTION__, __LINE__);
|
|
if(spawn_group_associations.count(group_id) == 0)
|
|
spawn_group_associations[group_id] = new set<int32>();
|
|
spawn_group_associations[group_id]->insert(group_id);
|
|
MSpawnGroupAssociation.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
bool ZoneServer::CallSpawnScript(Spawn* npc, int8 type, Spawn* spawn, const char* message, bool is_door_open, sint32 input_value, sint32* return_value){
|
|
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
|
if(!npc)
|
|
return false;
|
|
|
|
const char* script = npc->GetSpawnScript();
|
|
if ( script == nullptr || strlen(script) < 1 )
|
|
{
|
|
if (!npc->IsPet() && npc->GetZone() != nullptr)
|
|
{
|
|
string tmpScript;
|
|
tmpScript.append("SpawnScripts/");
|
|
tmpScript.append(npc->GetZone()->GetZoneName());
|
|
tmpScript.append("/");
|
|
int count = 0;
|
|
for (int s = 0; s < strlen(npc->GetName()); s++)
|
|
{
|
|
if (isalnum((unsigned char)npc->GetName()[s]))
|
|
{
|
|
tmpScript += npc->GetName()[s];
|
|
count++;
|
|
}
|
|
}
|
|
|
|
tmpScript.append(".lua");
|
|
|
|
if (count < 1)
|
|
{
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Could not form script name %s..", __FUNCTION__);
|
|
}
|
|
else
|
|
{
|
|
struct stat buffer;
|
|
bool fileExists = (stat(tmpScript.c_str(), &buffer) == 0);
|
|
if (fileExists)
|
|
{
|
|
LogWrite(SPAWN__WARNING, 0, "Spawn", "No script file described in the database, overriding with SpawnScript at %s", (char*)tmpScript.c_str());
|
|
npc->SetSpawnScript(tmpScript);
|
|
script = npc->GetSpawnScript();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool result = false;
|
|
if(lua_interface && script){
|
|
result = true; // default to true, if we don't match a switch case, return false in default case
|
|
switch(type){
|
|
case SPAWN_SCRIPT_SPAWN:{
|
|
lua_interface->RunSpawnScript(script, "spawn", npc);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_RESPAWN:{
|
|
lua_interface->RunSpawnScript(script, "respawn", npc);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_ATTACKED:{
|
|
lua_interface->RunSpawnScript(script, "attacked", npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_TARGETED:{
|
|
lua_interface->RunSpawnScript(script, "targeted", npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_HAILED:{
|
|
result = lua_interface->RunSpawnScript(script, "hailed", npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_HAILED_BUSY:{
|
|
lua_interface->RunSpawnScript(script, "hailed_busy", npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_DEATH:{
|
|
lua_interface->RunSpawnScript(script, "death", npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_KILLED:{
|
|
lua_interface->RunSpawnScript(script, "killed", npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_AGGRO:{
|
|
lua_interface->RunSpawnScript(script, "aggro", npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_HEALTHCHANGED:{
|
|
result = lua_interface->RunSpawnScript(script, "healthchanged", npc, spawn, 0, false, input_value, return_value);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_RANDOMCHAT:{
|
|
lua_interface->RunSpawnScript(script, "randomchat", npc, 0, message);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_CUSTOM:
|
|
case SPAWN_SCRIPT_TIMER:
|
|
case SPAWN_SCRIPT_CONVERSATION:{
|
|
lua_interface->RunSpawnScript(script, message, npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_CASTED_ON: {
|
|
lua_interface->RunSpawnScript(script, "casted_on", npc, spawn, message);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_AUTO_ATTACK_TICK: {
|
|
lua_interface->RunSpawnScript(script, "auto_attack_tick", npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_COMBAT_RESET: {
|
|
lua_interface->RunSpawnScript(script, "CombatReset", npc);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_GROUP_DEAD: {
|
|
lua_interface->RunSpawnScript(script, "group_dead", npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_HEAR_SAY: {
|
|
lua_interface->RunSpawnScript(script, "hear_say", npc, spawn, message);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_PRESPAWN: {
|
|
lua_interface->RunSpawnScript(script, "prespawn", npc);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_USEDOOR: {
|
|
result = lua_interface->RunSpawnScript(script, "usedoor", npc, spawn, "", is_door_open);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_BOARD: {
|
|
result = lua_interface->RunSpawnScript(script, "board", npc, spawn);
|
|
break;
|
|
}
|
|
case SPAWN_SCRIPT_DEBOARD: {
|
|
result = lua_interface->RunSpawnScript(script, "deboard", npc, spawn);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
result = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
|
|
return result;
|
|
}
|
|
|
|
void ZoneServer::DeleteTransporters() {
|
|
MTransportLocations.writelock(__FUNCTION__, __LINE__);
|
|
transporter_locations.clear(); //world takes care of actually deleting the data
|
|
MTransportLocations.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::ReloadTransporters(){
|
|
MutexList<LocationTransportDestination*>* locations = GetLocationTransporters(GetZoneID());
|
|
if(locations){
|
|
MutexList<LocationTransportDestination*>::iterator itr = locations->begin();
|
|
while(itr.Next())
|
|
AddTransporter(itr->value);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::CheckTransporters(Client* client) {
|
|
MTransportLocations.readlock(__FUNCTION__, __LINE__);
|
|
if(transporter_locations.size() > 0){
|
|
LocationTransportDestination* loc = 0;
|
|
list<LocationTransportDestination*>::iterator itr;
|
|
for (itr = transporter_locations.begin(); itr != transporter_locations.end(); itr++) {
|
|
loc = *itr;
|
|
if(client->GetPlayer()->GetDistance(loc->trigger_x, loc->trigger_y, loc->trigger_z) <= loc->trigger_radius){
|
|
if(loc->destination_zone_id == 0 || loc->destination_zone_id == GetZoneID()){
|
|
EQ2Packet* packet = client->GetPlayer()->Move(loc->destination_x, loc->destination_y, loc->destination_z, client->GetVersion());
|
|
if(packet)
|
|
client->QueuePacket(packet);
|
|
}
|
|
else{
|
|
ZoneChangeDetails zone_details;
|
|
bool foundZone = zone_list.GetZone(&zone_details, loc->destination_zone_id);
|
|
if(foundZone){
|
|
client->GetPlayer()->SetX(loc->destination_x);
|
|
client->GetPlayer()->SetY(loc->destination_y);
|
|
client->GetPlayer()->SetZ(loc->destination_z);
|
|
client->GetPlayer()->SetHeading(loc->destination_heading);
|
|
client->Zone(&zone_details, (ZoneServer*)zone_details.zonePtr);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
MTransportLocations.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::AddTransporter(LocationTransportDestination* loc) {
|
|
MTransportLocations.writelock(__FUNCTION__, __LINE__);
|
|
transporter_locations.push_back(loc);
|
|
MTransportLocations.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
Sign* ZoneServer::AddSignSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnentry){
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
|
Sign* sign = GetNewSign(spawnentry->spawn_id);
|
|
if(sign && !sign->IsOmittedByDBFlag()){
|
|
DeterminePosition(spawnlocation, sign);
|
|
sign->SetDatabaseID(spawnentry->spawn_id);
|
|
sign->SetSpawnLocationID(spawnentry->spawn_location_id);
|
|
sign->SetSpawnEntryID(spawnentry->spawn_entry_id);
|
|
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);
|
|
|
|
SetSpawnScript(spawnentry, sign);
|
|
|
|
CallSpawnScript(sign, SPAWN_SCRIPT_PRESPAWN);
|
|
|
|
AddSpawn(sign);
|
|
}
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
return sign;
|
|
}
|
|
|
|
Widget* ZoneServer::AddWidgetSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnentry){
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
|
Widget* widget = GetNewWidget(spawnentry->spawn_id);
|
|
if(widget && !widget->IsOmittedByDBFlag()){
|
|
DeterminePosition(spawnlocation, widget);
|
|
widget->SetDatabaseID(spawnentry->spawn_id);
|
|
widget->SetSpawnLocationID(spawnentry->spawn_location_id);
|
|
widget->SetSpawnEntryID(spawnentry->spawn_entry_id);
|
|
if(!widget->GetIncludeLocation()){
|
|
widget->SetX(widget->GetWidgetX());
|
|
if(widget->GetCloseY() != 0)
|
|
widget->SetY(widget->GetCloseY());
|
|
widget->SetZ(widget->GetWidgetZ());
|
|
}
|
|
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)
|
|
AddSpawnExpireTimer(widget, spawnentry->expire_time, spawnentry->expire_offset);
|
|
|
|
SetSpawnScript(spawnentry, widget);
|
|
|
|
CallSpawnScript(widget, SPAWN_SCRIPT_PRESPAWN);
|
|
|
|
AddSpawn(widget);
|
|
}
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
return widget;
|
|
}
|
|
|
|
Object* ZoneServer::AddObjectSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnentry){
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
|
Object* object = GetNewObject(spawnentry->spawn_id);
|
|
if(object && !object->IsOmittedByDBFlag()){
|
|
DeterminePosition(spawnlocation, object);
|
|
object->SetDatabaseID(spawnentry->spawn_id);
|
|
object->SetSpawnLocationID(spawnentry->spawn_location_id);
|
|
object->SetSpawnEntryID(spawnentry->spawn_entry_id);
|
|
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);
|
|
|
|
SetSpawnScript(spawnentry, object);
|
|
|
|
CallSpawnScript(object, SPAWN_SCRIPT_PRESPAWN);
|
|
|
|
AddSpawn(object);
|
|
}
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
return object;
|
|
}
|
|
|
|
GroundSpawn* ZoneServer::AddGroundSpawn(SpawnLocation* spawnlocation, SpawnEntry* spawnentry){
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Enter %s", __FUNCTION__);
|
|
GroundSpawn* spawn = GetNewGroundSpawn(spawnentry->spawn_id);
|
|
if(spawn && !spawn->IsOmittedByDBFlag()){
|
|
DeterminePosition(spawnlocation, spawn);
|
|
spawn->SetDatabaseID(spawnentry->spawn_id);
|
|
spawn->SetSpawnLocationID(spawnentry->spawn_location_id);
|
|
spawn->SetSpawnEntryID(spawnentry->spawn_entry_id);
|
|
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()) {
|
|
float rand_heading = MakeRandomFloat(0.0f, 360.0f);
|
|
spawn->SetHeading(rand_heading);
|
|
}
|
|
else {
|
|
spawn->SetHeading(spawnlocation->heading);
|
|
}
|
|
|
|
if (spawnentry->expire_time > 0)
|
|
AddSpawnExpireTimer(spawn, spawnentry->expire_time, spawnentry->expire_offset);
|
|
|
|
SetSpawnScript(spawnentry, spawn);
|
|
|
|
CallSpawnScript(spawn, SPAWN_SCRIPT_PRESPAWN);
|
|
|
|
AddSpawn(spawn);
|
|
}
|
|
LogWrite(SPAWN__TRACE, 0, "Spawn", "Exit %s", __FUNCTION__);
|
|
return spawn;
|
|
}
|
|
|
|
void ZoneServer::AddSpawn(Spawn* spawn) {
|
|
if(!spawn->IsPlayer()) {
|
|
spawn->SetZone(this); // we already set it on loadCharacter
|
|
}
|
|
else {
|
|
pNumPlayers++;
|
|
}
|
|
MIgnoredWidgets.lock_shared();
|
|
std::map<int32, bool>::iterator itr;
|
|
for(itr = ignored_widgets.begin(); itr != ignored_widgets.end(); itr++) {
|
|
spawn->AddIgnoredWidget(itr->first);
|
|
}
|
|
MIgnoredWidgets.unlock_shared();
|
|
spawn->position_changed = false;
|
|
spawn->info_changed = false;
|
|
spawn->vis_changed = false;
|
|
spawn->changed = false;
|
|
|
|
// Write locking the spawn list here will cause deadlocks, so instead add it to a temp list that the
|
|
// main spawn thread will put into the spawn_list when ever it has a chance.
|
|
MPendingSpawnListAdd.writelock(__FUNCTION__, __LINE__);
|
|
pending_spawn_list_add.push_back(spawn);
|
|
MPendingSpawnListAdd.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
spawn_range.Trigger();
|
|
|
|
if (GetInstanceType() == PERSONAL_HOUSE_INSTANCE && spawn->IsObject())
|
|
{
|
|
spawn->AddSecondaryEntityCommand("Examine", 20, "house_spawn_examine", "", 0, 0);
|
|
spawn->AddSecondaryEntityCommand("Move", 20, "move_item", "", 0, 0);
|
|
spawn->AddSecondaryEntityCommand("Pack in Moving Crate", 20, "house_spawn_pack_in_moving_crate", "", 0, 0);
|
|
spawn->AddSecondaryEntityCommand("Pick Up", 20, "pickup", "", 0, 0);
|
|
spawn->SetShowCommandIcon(1);
|
|
}
|
|
|
|
if(spawn->IsNPC())
|
|
AddEnemyList((NPC*)spawn);
|
|
if(spawn->IsPlayer() && ((Player*)spawn)->GetGroupMemberInfo())
|
|
spawn->SendGroupUpdate();
|
|
if (spawn->IsPlayer()) {
|
|
((Player*)spawn)->GetInfoStruct()->set_rain(rain);
|
|
((Player*)spawn)->SetCharSheetChanged(true);
|
|
}
|
|
|
|
if (movementMgr != nullptr && spawn->IsEntity()) {
|
|
movementMgr->AddMob((Entity*)spawn);
|
|
}
|
|
|
|
AddSpawnProximities(spawn);
|
|
|
|
AddSpawnToGrid(spawn, spawn->GetLocation());
|
|
spawn->SetAddedToWorldTimestamp(Timer::GetCurrentTime2());
|
|
}
|
|
|
|
void ZoneServer::AddClient(Client* client){
|
|
MClientList.writelock(__FUNCTION__, __LINE__);
|
|
lifetime_client_count++;
|
|
DecrementIncomingClients();
|
|
clients.push_back(client);
|
|
MClientList.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
connected_clients.Add(client);
|
|
}
|
|
|
|
void ZoneServer::RemoveClient(Client* client)
|
|
{
|
|
Guild *guild;
|
|
|
|
bool dismissPets = false;
|
|
if(client)
|
|
{
|
|
if (client->GetPlayer())
|
|
client_list.RemovePlayerFromInvisHistory(client->GetPlayer()->GetID());
|
|
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Sending login equipment appearance updates...");
|
|
loginserver.SendImmediateEquipmentUpdatesForChar(client->GetPlayer()->GetCharacterID());
|
|
|
|
if (!client->IsZoning())
|
|
{
|
|
client->SaveSpells();
|
|
|
|
client->GetPlayer()->DeleteSpellEffects(true);
|
|
|
|
if ((guild = client->GetPlayer()->GetGuild()) != NULL)
|
|
guild->GuildMemberLogoff(client->GetPlayer());
|
|
|
|
chat.LeaveAllChannels(client);
|
|
}
|
|
|
|
if(!zoneShuttingDown && !client->IsZoning())
|
|
{
|
|
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
|
GroupMemberInfo* gmi = client->GetPlayer()->GetGroupMemberInfo();
|
|
int32 group_id = 0;
|
|
if (gmi) {
|
|
group_id = gmi->group_id;
|
|
}
|
|
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
|
if (group_id) {
|
|
int32 size = world.GetGroupManager()->GetGroupSize(group_id);
|
|
if (size > 1) {
|
|
bool send_left_message = size > 2;
|
|
// removegroupmember can delete the gmi, so make sure we still have a group_id after
|
|
world.GetGroupManager()->RemoveGroupMember(group_id, client->GetPlayer());
|
|
if (send_left_message)
|
|
world.GetGroupManager()->GroupMessage(group_id, "%s has left the group.", client->GetPlayer()->GetName());
|
|
}
|
|
}
|
|
if( (client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0)
|
|
{
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Removing client '%s' (%u) due to LD/Exit...", client->GetPlayer()->GetName(), client->GetPlayer()->GetCharacterID());
|
|
}
|
|
else
|
|
{
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Removing client '%s' (%u) due to Camp/Quit...", client->GetPlayer()->GetName(), client->GetPlayer()->GetCharacterID());
|
|
}
|
|
|
|
dismissPets = true;
|
|
//}
|
|
}
|
|
else
|
|
{
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Removing client '%s' (%u) due to some client zoning...", client->GetPlayer()->GetName(), client->GetPlayer()->GetCharacterID());
|
|
}
|
|
|
|
map<int32, int32>::iterator itr;
|
|
for (itr = client->GetPlayer()->SpawnedBots.begin(); itr != client->GetPlayer()->SpawnedBots.end(); itr++) {
|
|
Spawn* spawn = GetSpawnByID(itr->second);
|
|
if (spawn && spawn->IsBot()) {
|
|
((Entity*)spawn)->SetOwner(nullptr);
|
|
((Bot*)spawn)->Camp();
|
|
}
|
|
}
|
|
|
|
if(dismissPets) {
|
|
((Entity*)client->GetPlayer())->DismissAllPets();
|
|
}
|
|
|
|
MClientList.writelock(__FUNCTION__, __LINE__);
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Calling clients.Remove(client)...");
|
|
|
|
std::vector<Client*>::iterator itr2 = find(clients.begin(), clients.end(), client);
|
|
if (itr2 != clients.end())
|
|
clients.erase(itr2);
|
|
MClientList.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
LogWrite(ZONE__INFO, 0, "Zone", "Scheduling client '%s' for removal.", client->GetPlayer()->GetName());
|
|
database.ToggleCharacterOnline(client, 0);
|
|
|
|
RemoveSpawn(client->GetPlayer(), false, true, true, true, true);
|
|
|
|
int32 DisconnectClientTimer = rule_manager.GetGlobalRule(R_World, RemoveDisconnectedClientsTimer)->GetInt32();
|
|
connected_clients.Remove(client, true, DisconnectClientTimer); // changed from a hardcoded 30000 (30 sec) to the DisconnectClientTimer rule
|
|
}
|
|
}
|
|
|
|
void ZoneServer::RemoveClientImmediately(Client* client) {
|
|
Guild *guild;
|
|
|
|
if(client)
|
|
{
|
|
if(client->GetPlayer()) {
|
|
if((client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0) {
|
|
client->GetPlayer()->SetActivityStatus(client->GetPlayer()->GetActivityStatus() - ACTIVITY_STATUS_LINKDEAD);
|
|
}
|
|
if ((client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_CAMPING) == 0) {
|
|
client->GetPlayer()->SetActivityStatus(client->GetPlayer()->GetActivityStatus() + ACTIVITY_STATUS_CAMPING);
|
|
}
|
|
client->Disconnect();
|
|
}
|
|
MClientList.writelock(__FUNCTION__, __LINE__);
|
|
std::vector<Client*>::iterator itr = find(clients.begin(), clients.end(), client);
|
|
if (itr != clients.end())
|
|
clients.erase(itr);
|
|
MClientList.releasewritelock(__FUNCTION__, __LINE__);
|
|
//clients.Remove(client, true);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::ClientProcess(bool ignore_shutdown_timer)
|
|
{
|
|
if(!ignore_shutdown_timer && connected_clients.size(true) == 0)
|
|
{
|
|
MIncomingClients.readlock(__FUNCTION__, __LINE__);
|
|
bool shutdownDelayCheck = shutdownDelayTimer.Check();
|
|
if((!AlwaysLoaded() && !shutdownTimer.Enabled()) || (!AlwaysLoaded() && shutdownDelayCheck))
|
|
{
|
|
if(incoming_clients && !shutdownDelayTimer.Enabled()) {
|
|
LogWrite(ZONE__INFO, 0, "Zone", "Incoming clients (%u) expected for %s, delaying shutdown timer...", incoming_clients, GetZoneName());
|
|
int32 timerDelay = rule_manager.GetGlobalRule(R_Zone, ShutdownDelayTimer)->GetInt32();
|
|
|
|
if(timerDelay < 10) {
|
|
LogWrite(ZONE__INFO, 0, "Zone", "Overriding %s shutdown delay timer as other clients are incoming, value %u too short, setting to 10...", GetZoneName(), timerDelay);
|
|
timerDelay = 10;
|
|
}
|
|
shutdownDelayTimer.Start(timerDelay, true);
|
|
}
|
|
else if(!incoming_clients || shutdownDelayCheck) {
|
|
if(!shutdownTimer.Enabled()) {
|
|
LogWrite(ZONE__INFO, 0, "Zone", "Starting zone shutdown timer for %s...", GetZoneName());
|
|
shutdownTimer.Start();
|
|
}
|
|
else {
|
|
LogWrite(ZONE__INFO, 0, "Zone", "zone shutdown timer for %s has %u remaining...", GetZoneName(), shutdownTimer.GetRemainingTime());
|
|
}
|
|
}
|
|
}
|
|
else if(AlwaysLoaded() && shutdownTimer.Enabled()) {
|
|
shutdownTimer.Disable();
|
|
}
|
|
MIncomingClients.releasereadlock(__FUNCTION__, __LINE__);
|
|
return;
|
|
}
|
|
|
|
shutdownTimer.Disable();
|
|
shutdownDelayTimer.Disable();
|
|
Client* client = 0;
|
|
MutexList<Client*>::iterator iterator = connected_clients.begin();
|
|
|
|
while(iterator.Next())
|
|
{
|
|
client = iterator->value;
|
|
#ifndef NO_CATCH
|
|
try
|
|
{
|
|
#endif
|
|
if(zoneShuttingDown || !client->Process(true))
|
|
{
|
|
if(!zoneShuttingDown && !client->IsZoning())
|
|
{
|
|
// avoid spam of messages while we await linkdead to complete
|
|
if(!client->IsLinkdeadTimerEnabled()) {
|
|
bool camping = (client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_CAMPING);
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Client is disconnecting in %s (camping = %s)", __FUNCTION__, camping ? "true" : "false");
|
|
if(!camping) {
|
|
client->setConnection(nullptr);
|
|
}
|
|
}
|
|
|
|
if((client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_LINKDEAD) > 0) {
|
|
client->StartLinkdeadTimer();
|
|
}
|
|
else if( (client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_CAMPING) == 0 )
|
|
{
|
|
//only set LD flag if we're disconnecting but not camping/quitting
|
|
client->GetPlayer()->SetActivityStatus(client->GetPlayer()->GetActivityStatus() + ACTIVITY_STATUS_LINKDEAD);
|
|
client->StartLinkdeadTimer();
|
|
}
|
|
else {
|
|
// camp timer completed, remove client
|
|
RemoveClient(client);
|
|
client->Disconnect();
|
|
}
|
|
}
|
|
else {
|
|
// force boot all players or clients zoning
|
|
RemoveClient(client);
|
|
client->Disconnect();
|
|
}
|
|
}
|
|
#ifndef NO_CATCH
|
|
}
|
|
catch(...)
|
|
{
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "Exception caught when in ZoneServer::ClientProcess() for zone '%s'!\n%s, %i", GetZoneName(), __FUNCTION__, __LINE__);
|
|
try{
|
|
bool isLinkdead = false;
|
|
if(!client->IsZoning())
|
|
{
|
|
if( (client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_CAMPING) == 0 )
|
|
{
|
|
client->GetPlayer()->SetActivityStatus(client->GetPlayer()->GetActivityStatus() + ACTIVITY_STATUS_LINKDEAD);
|
|
client->StartLinkdeadTimer();
|
|
isLinkdead = true;
|
|
if(client->GetPlayer()->GetGroupMemberInfo())
|
|
world.GetGroupManager()->GroupMessage(client->GetPlayer()->GetGroupMemberInfo()->group_id, "%s has gone Linkdead.", client->GetPlayer()->GetName());
|
|
}
|
|
}
|
|
|
|
if(!isLinkdead) {
|
|
RemoveClient(client);
|
|
client->Disconnect();
|
|
}
|
|
}
|
|
catch(...){
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "Exception caught when in ZoneServer::ClientProcess(), second try\n%s, %i", __FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SimpleMessage(int8 type, const char* message, Spawn* from, float distance, bool send_to_sender){
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(from && client && client->IsConnected() && (send_to_sender || from != client->GetPlayer()) && from->GetDistance(client->GetPlayer()) <= distance){
|
|
client->SimpleMessage(type, message);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::HandleChatMessage(Client* client, Spawn* from, const char* to, int16 channel, const char* message, float distance, const char* channel_name, bool show_bubble, int32 language) {
|
|
if ((!distance || from->GetDistance(client->GetPlayer()) <= distance) && (!from || !client->GetPlayer()->IsIgnored(from->GetName()))) {
|
|
PacketStruct* packet = configReader.getStruct("WS_HearChat", client->GetVersion());
|
|
if (packet) {
|
|
if (from)
|
|
packet->setMediumStringByName("from", from->GetName());
|
|
if (client->GetPlayer() != from)
|
|
packet->setMediumStringByName("to", client->GetPlayer()->GetName());
|
|
int8 clientchannel = client->GetMessageChannelColor(channel);
|
|
packet->setDataByName("channel", client->GetMessageChannelColor(channel));
|
|
if (from && ((from == client->GetPlayer()) || (client->GetPlayer()->WasSentSpawn(from->GetID()))))
|
|
packet->setDataByName("from_spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(from));
|
|
else
|
|
packet->setDataByName("from_spawn_id", 0xFFFFFFFF);
|
|
packet->setDataByName("to_spawn_id", 0xFFFFFFFF);
|
|
packet->setMediumStringByName("message", message);
|
|
packet->setDataByName("language", language);
|
|
|
|
bool hasLanguage = client->GetPlayer()->HasLanguage(language);
|
|
if (language > 0 && !hasLanguage)
|
|
packet->setDataByName("understood", 0);
|
|
else
|
|
packet->setDataByName("understood", 1);
|
|
|
|
show_bubble == true ? packet->setDataByName("show_bubble", 1) : packet->setDataByName("show_bubble", 0);
|
|
if (channel_name)
|
|
packet->setMediumStringByName("channel_name", channel_name);
|
|
EQ2Packet* outapp = packet->serialize();
|
|
//DumpPacket(outapp);
|
|
client->QueuePacket(outapp);
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::HandleChatMessage(Client* client, std::string fromName, const char* to, int16 channel, const char* message, float distance, const char* channel_name, int32 language) {
|
|
if (!client->GetPlayer()->IsIgnored(fromName.c_str())) {
|
|
PacketStruct* packet = configReader.getStruct("WS_HearChat", client->GetVersion());
|
|
if (packet) {
|
|
packet->setMediumStringByName("from", fromName.c_str());
|
|
|
|
int8 clientchannel = client->GetMessageChannelColor(channel);
|
|
packet->setDataByName("channel", client->GetMessageChannelColor(channel));
|
|
packet->setDataByName("from_spawn_id", 0xFFFFFFFF);
|
|
packet->setDataByName("to_spawn_id", 0xFFFFFFFF);
|
|
packet->setMediumStringByName("message", message);
|
|
packet->setDataByName("language", language);
|
|
|
|
bool hasLanguage = client->GetPlayer()->HasLanguage(language);
|
|
if (language > 0 && !hasLanguage)
|
|
packet->setDataByName("understood", 0);
|
|
else
|
|
packet->setDataByName("understood", 1);
|
|
|
|
packet->setDataByName("show_bubble", 0);
|
|
if (channel_name)
|
|
packet->setMediumStringByName("channel_name", channel_name);
|
|
EQ2Packet* outapp = packet->serialize();
|
|
//DumpPacket(outapp);
|
|
client->QueuePacket(outapp);
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::HandleChatMessage(Spawn* from, const char* to, int16 channel, const char* message, float distance, const char* channel_name, bool show_bubble, int32 language){
|
|
vector<Client*>::iterator client_itr;
|
|
Client* client = 0;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(client && client->IsConnected())
|
|
HandleChatMessage(client, from, to, channel, message, distance, channel_name, show_bubble, language);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::HandleChatMessage(std::string fromName, const char* to, int16 channel, const char* message, float distance, const char* channel_name, int32 language){
|
|
vector<Client*>::iterator client_itr;
|
|
Client* client = 0;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(client && client->IsConnected())
|
|
HandleChatMessage(client, fromName, to, channel, message, distance, channel_name, language);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::HandleBroadcast(const char* message) {
|
|
vector<Client*>::iterator client_itr;
|
|
Client* client = 0;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(client && client->IsConnected())
|
|
client->SimpleMessage(CHANNEL_BROADCAST, message);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::HandleAnnouncement(const char* message) {
|
|
vector<Client*>::iterator client_itr;
|
|
Client* client = 0;
|
|
int32 words = ::CountWordsInString(message);
|
|
if (words < 5)
|
|
words = 5;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(client && client->IsConnected()) {
|
|
client->SimpleMessage(CHANNEL_BROADCAST, message);
|
|
client->SendPopupMessage(10, message, "ui_harvest_normal", words, 0xFF, 0xFF, 0x00);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendTimeUpdate(Client* client){
|
|
if(client){
|
|
PacketStruct* packet = world.GetWorldTime(client->GetVersion());
|
|
if(packet){
|
|
client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SendTimeUpdateToAllClients(){
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(client && client->IsConnected())
|
|
SendTimeUpdate(client);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::UpdateVitality(float amount){
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(client && client->GetPlayer()->GetInfoStruct()->get_xp_vitality() < 100){
|
|
if((client->GetPlayer()->GetInfoStruct()->get_xp_vitality() + amount) > 100)
|
|
client->GetPlayer()->GetInfoStruct()->set_xp_vitality(100);
|
|
else
|
|
client->GetPlayer()->GetInfoStruct()->add_xp_vitality(amount);
|
|
client->GetPlayer()->SetCharSheetChanged(true);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendSpawn(Spawn* spawn, Client* client){
|
|
EQ2Packet* outapp = spawn->serialize(client->GetPlayer(), client->GetVersion());
|
|
|
|
if(!client->GetPlayer()->IsSendingSpawn(spawn->GetID())) {
|
|
safe_delete(outapp);
|
|
}
|
|
else {
|
|
LogWrite(ZONE__DEBUG, 7, "Zone", "%s: Processing SendSpawn for spawn index %u (%s)...", client->GetPlayer()->GetName(), client->GetPlayer()->GetIndexForSpawn(spawn), spawn->GetName());
|
|
if(outapp)
|
|
client->QueuePacket(outapp, true);
|
|
|
|
client->GetPlayer()->SetSpawnSentState(spawn, SpawnState::SPAWN_STATE_SENT_WAIT);
|
|
}
|
|
|
|
/*
|
|
vis flags:
|
|
2 = show icon
|
|
4 = targetable
|
|
16 = show name
|
|
32 = show level/border
|
|
activity_status:
|
|
4 - linkdead
|
|
8 - camping
|
|
16 - LFG
|
|
32 - LFW
|
|
2048 - mentoring
|
|
4096 - displays shield
|
|
8192 - immunity gained
|
|
16384 - immunity remaining
|
|
attackable_status
|
|
1 - no_hp_bar
|
|
4 - not attackable
|
|
npc_con
|
|
-4 = scowls
|
|
-3 = threatening
|
|
-2 = dubiously
|
|
-1 = apprehensively
|
|
0 = indifferent
|
|
1 = amiably
|
|
2 = kindly
|
|
3 = warmly
|
|
4 = ally
|
|
quest_flag
|
|
1 = new quest
|
|
2 = update and new quest
|
|
3 = update
|
|
*/
|
|
if(spawn->IsEntity() && spawn->HasTrapTriggered())
|
|
client->QueueStateCommand(client->GetPlayer()->GetIDWithPlayerSpawn(spawn), spawn->GetTrapState());
|
|
}
|
|
|
|
Client* ZoneServer::GetClientByName(char* name) {
|
|
Client* ret = 0;
|
|
vector<Client*>::iterator itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
if ((*itr)->GetPlayer()) {
|
|
if (strncmp((*itr)->GetPlayer()->GetName(), name, strlen(name)) == 0) {
|
|
ret = *itr;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
Client* ZoneServer::GetClientByCharID(int32 charid) {
|
|
Client* ret = 0;
|
|
vector<Client*>::iterator itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
if ((*itr)->GetCharacterID() == charid) {
|
|
ret = *itr;
|
|
break;
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::AddMovementNPC(Spawn* spawn){
|
|
if (spawn)
|
|
movement_spawns.Put(spawn->GetID(), 1);
|
|
}
|
|
|
|
void ZoneServer::RemoveMovementNPC(Spawn* spawn){
|
|
if (spawn)
|
|
remove_movement_spawns.Add(spawn->GetID());
|
|
}
|
|
|
|
void ZoneServer::PlayFlavor(Client* client, Spawn* spawn, const char* mp3, const char* text, const char* emote, int32 key1, int32 key2, int8 language){
|
|
if(!client || !spawn)
|
|
return;
|
|
|
|
PacketStruct* packet = configReader.getStruct("WS_PlayFlavor", client->GetVersion());
|
|
if(packet){
|
|
packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(spawn));
|
|
packet->setDataByName("unknown1", 0xFFFFFFFF);
|
|
packet->setDataByName("unknown5", 1, 1);
|
|
packet->setDataByName("unknown5", 1, 6);
|
|
if(mp3){
|
|
packet->setMediumStringByName("mp3", mp3);
|
|
packet->setDataByName("key", key1);
|
|
packet->setDataByName("key", key2, 1);
|
|
}
|
|
packet->setMediumStringByName("name", spawn->GetName());
|
|
if(text)
|
|
packet->setMediumStringByName("text", text);
|
|
if(emote) {
|
|
if(client->GetVersion() > 561) {
|
|
packet->setMediumStringByName("emote", emote);
|
|
}
|
|
else {
|
|
HandleEmote(spawn, std::string(emote));
|
|
}
|
|
}
|
|
if (language != 0)
|
|
packet->setDataByName("language", language);
|
|
|
|
//We should probably add Common = language id 0 or 0xFF so admins can customize more..
|
|
if (language == 0 || client->GetPlayer()->HasLanguage(language))
|
|
packet->setDataByName("understood", 1);
|
|
|
|
EQ2Packet* app = packet->serialize();
|
|
//DumpPacket(app);
|
|
client->QueuePacket(app);
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::PlayVoice(Client* client, Spawn* spawn, const char* mp3, int32 key1, int32 key2){
|
|
if(!client || !spawn)
|
|
return;
|
|
|
|
PacketStruct* packet = configReader.getStruct("WS_PlayVoice", client->GetVersion());
|
|
if(packet){
|
|
packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(spawn));
|
|
packet->setMediumStringByName("mp3", mp3);
|
|
packet->setDataByName("key", key1);
|
|
packet->setDataByName("key", key2, 1);
|
|
client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::PlayFlavor(Spawn* spawn, const char* mp3, const char* text, const char* emote, int32 key1, int32 key2, int8 language){
|
|
if(!spawn)
|
|
return;
|
|
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client || !client->IsReadyForUpdates() || !client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->GetDistance(spawn) > 30)
|
|
continue;
|
|
PlayFlavor(client, spawn, mp3, text, emote, key1, key2, language);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::PlayFlavorID(Spawn* spawn, int8 type, int32 id, int16 index, int8 language){
|
|
if(!spawn)
|
|
return;
|
|
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
VoiceOverStruct non_garble, garble;
|
|
bool garble_success = false;
|
|
bool success = world.FindVoiceOver(type, id, index, &non_garble, &garble_success, &garble);
|
|
|
|
VoiceOverStruct* resStruct = nullptr;
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client || !client->IsReadyForUpdates() || !client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->GetDistance(spawn) > 30)
|
|
continue;
|
|
|
|
client->SendPlayFlavor(spawn, language, &non_garble, &garble, success, garble_success);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::PlayVoice(Spawn* spawn, const char* mp3, int32 key1, int32 key2){
|
|
if(!spawn || !mp3)
|
|
return;
|
|
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client || !client->IsReadyForUpdates() || !client->GetPlayer()->WasSentSpawn(spawn->GetID()))
|
|
continue;
|
|
PlayVoice(client, spawn, mp3, key1, key2);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::PlaySoundFile(Client* client, const char* name, float origin_x, float origin_y, float origin_z){
|
|
if(!name)
|
|
return;
|
|
|
|
PacketStruct* packet = 0;
|
|
if(client){
|
|
packet = configReader.getStruct("WS_Play3DSound", client->GetVersion());
|
|
if(packet){
|
|
packet->setMediumStringByName("name", name);
|
|
packet->setDataByName("x", origin_x);
|
|
packet->setDataByName("y", origin_y);
|
|
packet->setDataByName("z", origin_z);
|
|
packet->setDataByName("unknown1", 1);
|
|
packet->setDataByName("unknown2", 2.5);
|
|
packet->setDataByName("unknown3", 15);
|
|
client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
else{
|
|
EQ2Packet* outapp = 0;
|
|
int16 packet_version = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(client && (!packet || packet_version != client->GetVersion())){
|
|
safe_delete(packet);
|
|
safe_delete(outapp);
|
|
packet_version = client->GetVersion();
|
|
packet = configReader.getStruct("WS_Play3DSound", packet_version);
|
|
if(packet){
|
|
packet->setMediumStringByName("name", name);
|
|
packet->setDataByName("x", origin_x);
|
|
packet->setDataByName("y", origin_y);
|
|
packet->setDataByName("z", origin_z);
|
|
packet->setDataByName("unknown1", 1);
|
|
packet->setDataByName("unknown2", 2.5);
|
|
packet->setDataByName("unknown3", 15);
|
|
outapp = packet->serialize();
|
|
}
|
|
}
|
|
if(outapp && client && client->IsReadyForUpdates())
|
|
client->QueuePacket(outapp->Copy());
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
safe_delete(packet);
|
|
safe_delete(outapp);
|
|
}
|
|
}
|
|
|
|
bool ZoneServer::HasWidgetTimer(Spawn* widget){
|
|
bool ret = false;
|
|
if (widget) {
|
|
int32 id = widget->GetID();
|
|
map<int32, int32>::iterator itr;
|
|
MWidgetTimers.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = widget_timers.begin(); itr != widget_timers.end(); itr++) {
|
|
if(itr->first == id){
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
MWidgetTimers.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::CheckWidgetTimers(){
|
|
vector<int32> remove_list;
|
|
map<int32, int32>::iterator itr;
|
|
|
|
MWidgetTimers.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = widget_timers.begin(); itr != widget_timers.end(); itr++) {
|
|
if(Timer::GetCurrentTime2() >= itr->second){
|
|
/*Spawn* widget = GetSpawnByID(itr->first);
|
|
if (widget && widget->IsWidget())
|
|
((Widget*)widget)->HandleTimerUpdate();*/
|
|
|
|
remove_list.push_back(itr->first);
|
|
}
|
|
}
|
|
MWidgetTimers.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
for (int32 i = 0; i < remove_list.size(); i++) {
|
|
Spawn* widget = GetSpawnByID(remove_list[i]);
|
|
if (widget && widget->IsWidget())
|
|
((Widget*)widget)->HandleTimerUpdate();
|
|
}
|
|
|
|
MWidgetTimers.writelock(__FUNCTION__, __LINE__);
|
|
for(int32 i=0;i<remove_list.size(); i++)
|
|
widget_timers.erase(remove_list[i]);
|
|
MWidgetTimers.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::AddWidgetTimer(Spawn* widget, float time) {
|
|
if (widget && widget->IsWidget()) {
|
|
MWidgetTimers.writelock(__FUNCTION__, __LINE__);
|
|
widget_timers[widget->GetID()] = ((int32)(time * 1000)) + Timer::GetCurrentTime2();
|
|
MWidgetTimers.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
|
|
Spawn* ZoneServer::GetSpawnGroup(int32 id){
|
|
Spawn* ret = 0;
|
|
Spawn* spawn = 0;
|
|
|
|
if(id < 1)
|
|
return 0;
|
|
|
|
bool lookup = false;
|
|
if(quick_group_id_lookup.count(id) > 0) {
|
|
ret = GetSpawnByID(quick_group_id_lookup.Get(id));
|
|
lookup = true;
|
|
}
|
|
if(ret == NULL) {
|
|
if(lookup)
|
|
quick_group_id_lookup.erase(id);
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if(spawn){
|
|
if(spawn->Alive() && spawn->GetSpawnGroupID() == id){
|
|
ret = spawn;
|
|
quick_group_id_lookup.Put(id, spawn->GetID());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool ZoneServer::IsSpawnGroupAlive(int32 id){
|
|
bool ret = false;
|
|
if(id < 1)
|
|
return ret;
|
|
|
|
Spawn* spawn = GetSpawnGroup(id);
|
|
if(spawn) {
|
|
vector<Spawn*> groupMembers;
|
|
if (!spawn->IsPlayer() && spawn->HasSpawnGroup()) {
|
|
groupMembers = *spawn->GetSpawnGroup();
|
|
}
|
|
|
|
Spawn* group_spawn = 0;
|
|
vector<Spawn*>::iterator itr;
|
|
for(itr = groupMembers.begin(); itr != groupMembers.end(); itr++){
|
|
group_spawn = *itr;
|
|
if(group_spawn->Alive()) {
|
|
ret = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Spawn* ZoneServer::GetSpawnByLocationID(int32 location_id) {
|
|
Spawn* ret = 0;
|
|
Spawn* current_spawn = 0;
|
|
|
|
if(location_id < 1)
|
|
return 0;
|
|
|
|
bool lookup = false;
|
|
if(quick_location_id_lookup.count(location_id) > 0) {
|
|
ret = GetSpawnByID(quick_location_id_lookup.Get(location_id));
|
|
lookup = true;
|
|
}
|
|
if(ret == NULL) {
|
|
if(lookup)
|
|
quick_location_id_lookup.erase(location_id);
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
current_spawn = itr->second;
|
|
if (current_spawn && current_spawn->GetSpawnLocationID() == location_id) {
|
|
ret = current_spawn;
|
|
quick_location_id_lookup.Put(location_id, ret->GetID());
|
|
break;
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Spawn* ZoneServer::GetSpawnByDatabaseID(int32 id){
|
|
Spawn* ret = 0;
|
|
|
|
if(id < 1)
|
|
return 0;
|
|
|
|
bool lookup = false;
|
|
|
|
if(quick_database_id_lookup.count(id) > 0) {
|
|
ret = GetSpawnByID(quick_database_id_lookup.Get(id));
|
|
lookup = true;
|
|
}
|
|
if(ret == NULL){
|
|
if(lookup)
|
|
quick_database_id_lookup.erase(id);
|
|
Spawn* spawn = 0;
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++){
|
|
spawn = itr->second;
|
|
if(spawn){
|
|
if(spawn->GetDatabaseID() == id){
|
|
quick_database_id_lookup.Put(id, spawn->GetID());
|
|
ret = spawn;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Spawn* ZoneServer::GetSpawnByID(int32 id, bool spawnListLocked) {
|
|
Spawn* ret = 0;
|
|
if (!spawnListLocked )
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
|
|
if (spawn_list.count(id) > 0)
|
|
ret = spawn_list[id];
|
|
|
|
if (!spawnListLocked)
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
bool ZoneServer::SendRemoveSpawn(Client* client, Spawn* spawn, PacketStruct* packet, bool delete_spawn)
|
|
{
|
|
if(!client || !spawn || (client && client->GetPlayer() == spawn))
|
|
return false;
|
|
|
|
if(client->GetPlayerPOVGhostSpawnID() == spawn->GetID()) {
|
|
client->SetPlayerPOVGhost(nullptr);
|
|
}
|
|
|
|
spawn->RemoveSpawnFromPlayer(client->GetPlayer());
|
|
return true;
|
|
}
|
|
|
|
void ZoneServer::SetSpawnCommand(Spawn* spawn, int8 type, char* value, Client* client){
|
|
//commands
|
|
LogWrite(MISC__TODO, 1, "TODO", "%s does nothing!\n%s, %i", __FUNCTION__, __FILE__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SetSpawnCommand(int32 spawn_id, int8 type, char* value, Client* client){
|
|
LogWrite(MISC__TODO, 1, "TODO", "%s does nothing!\n%s, %i", __FUNCTION__, __FILE__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::ApplySetSpawnCommand(Client* client, Spawn* target, int8 type, const char* value){
|
|
// This will apply the /spawn set command to all the spawns in the zone with the same DB ID, we do not want to set
|
|
// location values (x, y, z, heading, grid) for all spawns in the zone with the same DB ID, only the targeted spawn
|
|
if(type == SPAWN_SET_VALUE_SPAWNENTRY_SCRIPT || type == SPAWN_SET_VALUE_SPAWNLOCATION_SCRIPT || (type >= SPAWN_SET_VALUE_X && type <= SPAWN_SET_VALUE_LOCATION) ||
|
|
type == SPAWN_SET_VALUE_PITCH || type == SPAWN_SET_VALUE_ROLL)
|
|
return;
|
|
|
|
Spawn* tmp = 0;
|
|
if(target->IsNPC())
|
|
tmp = GetNPC(target->GetDatabaseID());
|
|
else if(target->IsObject())
|
|
tmp = GetObject(target->GetDatabaseID());
|
|
else if(target->IsGroundSpawn())
|
|
tmp = GetGroundSpawn(target->GetDatabaseID());
|
|
else if(target->IsSign())
|
|
tmp = GetSign(target->GetDatabaseID());
|
|
else if(target->IsWidget())
|
|
tmp = GetWidget(target->GetDatabaseID());
|
|
if(tmp && type == SPAWN_SET_VALUE_SPAWN_SCRIPT)
|
|
tmp->SetSpawnScript(value);
|
|
else if(tmp)
|
|
commands.SetSpawnCommand(client, tmp, type, value); // set the master spawn
|
|
Spawn* spawn = 0;
|
|
|
|
// this check needs to be here otherwise every spawn with 0 will be set
|
|
if ( target->GetDatabaseID ( ) > 0 )
|
|
{
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if(spawn && spawn->GetDatabaseID() == target->GetDatabaseID()){
|
|
if(type == SPAWN_SET_VALUE_SPAWN_SCRIPT)
|
|
spawn->SetSpawnScript(value);
|
|
else
|
|
commands.SetSpawnCommand(client, spawn, type, value);
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::StopSpawnScriptTimer(Spawn* spawn, std::string functionName){
|
|
MSpawnScriptTimers.writelock(__FUNCTION__, __LINE__);
|
|
MRemoveSpawnScriptTimersList.writelock(__FUNCTION__, __LINE__);
|
|
if(spawn_script_timers.size() > 0){
|
|
set<SpawnScriptTimer*>::iterator itr;
|
|
SpawnScriptTimer* timer = 0;
|
|
for (itr = spawn_script_timers.begin(); itr != spawn_script_timers.end(); ) {
|
|
timer = *itr;
|
|
if(timer->spawn == spawn->GetID() && (functionName == "" || timer->function == functionName) && remove_spawn_script_timers_list.count(timer) == 0) {
|
|
itr = spawn_script_timers.erase(itr);
|
|
safe_delete(timer);
|
|
}
|
|
else
|
|
itr++;
|
|
}
|
|
}
|
|
MRemoveSpawnScriptTimersList.releasewritelock(__FUNCTION__, __LINE__);
|
|
MSpawnScriptTimers.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::DeleteSpawnScriptTimers(Spawn* spawn, bool all){
|
|
MSpawnScriptTimers.writelock(__FUNCTION__, __LINE__);
|
|
MRemoveSpawnScriptTimersList.writelock(__FUNCTION__, __LINE__);
|
|
if(spawn_script_timers.size() > 0){
|
|
set<SpawnScriptTimer*>::iterator itr;
|
|
SpawnScriptTimer* timer = 0;
|
|
for (itr = spawn_script_timers.begin(); itr != spawn_script_timers.end(); itr++) {
|
|
timer = *itr;
|
|
if((all || timer->spawn == spawn->GetID()) && remove_spawn_script_timers_list.count(timer) == 0)
|
|
remove_spawn_script_timers_list.insert(timer);
|
|
}
|
|
|
|
if(all)
|
|
spawn_script_timers.clear();
|
|
}
|
|
MRemoveSpawnScriptTimersList.releasewritelock(__FUNCTION__, __LINE__);
|
|
MSpawnScriptTimers.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::DeleteSpawnScriptTimers() {
|
|
MSpawnScriptTimers.writelock(__FUNCTION__, __LINE__);
|
|
MRemoveSpawnScriptTimersList.writelock(__FUNCTION__, __LINE__);
|
|
if(remove_spawn_script_timers_list.size() > 0){
|
|
set<SpawnScriptTimer*>::iterator itr;
|
|
SpawnScriptTimer* timer = 0;
|
|
|
|
for (itr = remove_spawn_script_timers_list.begin(); itr != remove_spawn_script_timers_list.end(); itr++) {
|
|
timer = *itr;
|
|
set<SpawnScriptTimer*>::iterator itr2;
|
|
|
|
itr2 = spawn_script_timers.find(timer);
|
|
|
|
if(itr2 != spawn_script_timers.end())
|
|
spawn_script_timers.erase(itr2);
|
|
|
|
safe_delete(timer);
|
|
}
|
|
remove_spawn_script_timers_list.clear();
|
|
}
|
|
MRemoveSpawnScriptTimersList.releasewritelock(__FUNCTION__, __LINE__);
|
|
MSpawnScriptTimers.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::CheckSpawnScriptTimers(){
|
|
DeleteSpawnScriptTimers();
|
|
SpawnScriptTimer* timer = 0;
|
|
vector<SpawnScriptTimer> call_timers;
|
|
MSpawnScriptTimers.readlock(__FUNCTION__, __LINE__);
|
|
MRemoveSpawnScriptTimersList.writelock(__FUNCTION__, __LINE__);
|
|
if(spawn_script_timers.size() > 0){
|
|
int32 current_time = Timer::GetCurrentTime2();
|
|
set<SpawnScriptTimer*>::iterator itr;
|
|
for (itr = spawn_script_timers.begin(); itr != spawn_script_timers.end(); itr++) {
|
|
timer = *itr;
|
|
if(remove_spawn_script_timers_list.count(timer) == 0 &&
|
|
timer->current_count < timer->max_count && current_time >= timer->timer){
|
|
timer->current_count++;
|
|
SpawnScriptTimer tmpTimer;
|
|
tmpTimer.current_count = timer->current_count;
|
|
tmpTimer.function = timer->function;
|
|
tmpTimer.player = timer->player;
|
|
tmpTimer.spawn = timer->spawn;
|
|
tmpTimer.max_count = timer->max_count;
|
|
call_timers.push_back(tmpTimer);
|
|
}
|
|
if(timer->current_count >= timer->max_count && remove_spawn_script_timers_list.count(timer) == 0)
|
|
remove_spawn_script_timers_list.insert(timer);
|
|
}
|
|
}
|
|
MRemoveSpawnScriptTimersList.releasewritelock(__FUNCTION__, __LINE__);
|
|
MSpawnScriptTimers.releasereadlock(__FUNCTION__, __LINE__);
|
|
if(call_timers.size() > 0){
|
|
vector<SpawnScriptTimer>::iterator itr;
|
|
for(itr = call_timers.begin(); itr != call_timers.end(); itr++){
|
|
SpawnScriptTimer tmpTimer = (SpawnScriptTimer)*itr;
|
|
CallSpawnScript(GetSpawnByID(tmpTimer.spawn), SPAWN_SCRIPT_TIMER, tmpTimer.player > 0 ? GetSpawnByID(tmpTimer.player) : 0, tmpTimer.function.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::KillSpawnByDistance(Spawn* spawn, float max_distance, bool include_players, bool send_packet){
|
|
if(!spawn)
|
|
return;
|
|
|
|
auto loc = glm::vec3(spawn->GetX(), spawn->GetZ(), spawn->GetY());
|
|
std::vector<int32> grids_by_radius;
|
|
if(spawn->GetMap()) {
|
|
grids_by_radius = GetGridsByLocation(spawn, loc, max_distance);
|
|
}
|
|
else {
|
|
grids_by_radius.push_back(spawn->GetLocation());
|
|
}
|
|
|
|
Spawn* test_spawn = 0;
|
|
MGridMaps.lock_shared();
|
|
std::vector<int32>::iterator grid_radius_itr;
|
|
for(grid_radius_itr = grids_by_radius.begin(); grid_radius_itr != grids_by_radius.end(); grid_radius_itr++) {
|
|
std::map<int32, GridMap*>::iterator grids = grid_maps.find((*grid_radius_itr));
|
|
if(grids != grid_maps.end()) {
|
|
grids->second->MSpawns.lock_shared();
|
|
typedef map <int32, Spawn*> SpawnMapType;
|
|
for( SpawnMapType::iterator it = grids->second->spawns.begin(); it != grids->second->spawns.end(); ++it ) {
|
|
test_spawn = it->second;
|
|
if(test_spawn && test_spawn->Alive() && test_spawn->GetID() > 0 && test_spawn->GetID() != spawn->GetID() && test_spawn->IsEntity() &&
|
|
(!test_spawn->IsPlayer() || include_players)){
|
|
if(test_spawn->GetDistance(spawn) < max_distance)
|
|
KillSpawn(true, test_spawn, spawn, send_packet);
|
|
}
|
|
}
|
|
grids->second->MSpawns.unlock_shared();
|
|
}
|
|
}
|
|
MGridMaps.unlock_shared();
|
|
}
|
|
|
|
void ZoneServer::SpawnSetByDistance(Spawn* spawn, float max_distance, string field, string value){
|
|
if(!spawn)
|
|
return;
|
|
|
|
Spawn* test_spawn = 0;
|
|
int32 type = commands.GetSpawnSetType(field);
|
|
if(type == 0xFFFFFFFF)
|
|
return;
|
|
|
|
auto loc = glm::vec3(spawn->GetX(), spawn->GetZ(), spawn->GetY());
|
|
std::vector<int32> grids_by_radius;
|
|
if(spawn->GetMap()) {
|
|
grids_by_radius = GetGridsByLocation(spawn, loc, max_distance);
|
|
}
|
|
else {
|
|
grids_by_radius.push_back(spawn->GetLocation());
|
|
}
|
|
|
|
MGridMaps.lock_shared();
|
|
std::vector<int32>::iterator grid_radius_itr;
|
|
for(grid_radius_itr = grids_by_radius.begin(); grid_radius_itr != grids_by_radius.end(); grid_radius_itr++) {
|
|
std::map<int32, GridMap*>::iterator grids = grid_maps.find((*grid_radius_itr));
|
|
grids->second->MSpawns.lock_shared();
|
|
typedef map <int32, Spawn*> SpawnMapType;
|
|
for( SpawnMapType::iterator it = grids->second->spawns.begin(); it != grids->second->spawns.end(); ++it ) {
|
|
test_spawn = it->second;
|
|
if(test_spawn && test_spawn->GetID() > 0 && test_spawn->GetID() != spawn->GetID() && !test_spawn->IsPlayer()){
|
|
if(test_spawn->GetDistance(spawn) < max_distance){
|
|
commands.SetSpawnCommand(0, test_spawn, type, value.c_str());
|
|
}
|
|
}
|
|
}
|
|
grids->second->MSpawns.unlock_shared();
|
|
}
|
|
MGridMaps.unlock_shared();
|
|
}
|
|
|
|
void ZoneServer::AddSpawnScriptTimer(SpawnScriptTimer* timer){
|
|
MSpawnScriptTimers.writelock(__FUNCTION__, __LINE__);
|
|
spawn_script_timers.insert(timer);
|
|
MSpawnScriptTimers.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
/*
|
|
void ZoneServer::RemoveFromRangeMap(Client* client){
|
|
spawn_range_map.erase(client);
|
|
}
|
|
*/
|
|
|
|
void ZoneServer::RemoveSpawn(Spawn* spawn, bool delete_spawn, bool respawn, bool lock, bool erase_from_spawn_list, bool lock_spell_process)
|
|
{
|
|
if(!spawn->IsDeletedSpawn() && spawn->IsPlayer()) {
|
|
if(pNumPlayers > 0)
|
|
pNumPlayers--;
|
|
}
|
|
LogWrite(ZONE__DEBUG, 3, "Zone", "Processing RemoveSpawn function for %s (%i)...", spawn->GetName(),spawn->GetID());
|
|
|
|
PacketStruct* packet = 0;
|
|
int16 packet_version = 0;
|
|
Client* client = 0;
|
|
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
|
|
if (client && (client->GetVersion() > 373 || !client->IsZoning() || client->GetPlayer() != spawn)) { //don't send destroy ghost of 283 client when zoning
|
|
if (client->GetPlayer()->HasTarget() && client->GetPlayer()->GetTarget() == spawn)
|
|
client->GetPlayer()->SetTarget(0);
|
|
if(client->GetPlayer()->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->IsSendingSpawn(spawn->GetID()))
|
|
SendRemoveSpawn(client, spawn, packet, delete_spawn);
|
|
if (spawn_range_map.count(client) > 0)
|
|
spawn_range_map.Get(client)->erase(spawn->GetID());
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
safe_delete(packet);
|
|
|
|
spawn->RemoveSpawnProximities();
|
|
RemoveSpawnProximities(spawn);
|
|
|
|
if (movementMgr != nullptr && spawn->IsEntity()) {
|
|
movementMgr->RemoveMob((Entity*)spawn);
|
|
}
|
|
|
|
RemoveSpawnSupportFunctions(spawn, lock_spell_process);
|
|
if (reloading)
|
|
RemoveDeadEnemyList(spawn);
|
|
|
|
if (lock)
|
|
MDeadSpawns.writelock(__FUNCTION__, __LINE__);
|
|
|
|
if (dead_spawns.count(spawn->GetID()) > 0)
|
|
dead_spawns.erase(spawn->GetID());
|
|
if (lock)
|
|
MDeadSpawns.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
if (spawn_expire_timers.count(spawn->GetID()) > 0)
|
|
spawn_expire_timers.erase(spawn->GetID());
|
|
|
|
spawn->SetDeletedSpawn(true);
|
|
|
|
// we will remove the spawn ptr and entry in the spawn_list later.. it is not safe right now (lua? client process? spawn process? etc? too many factors)
|
|
if(erase_from_spawn_list)
|
|
AddPendingSpawnRemove(spawn->GetID());
|
|
|
|
if(respawn && !spawn->IsPlayer() && spawn->GetSpawnLocationID() > 0) {
|
|
LogWrite(ZONE__DEBUG, 3, "Zone", "Handle NPC Respawn for '%s'.", spawn->GetName());
|
|
AddRespawn(spawn);
|
|
}
|
|
|
|
RemoveSpawnFromGrid(spawn, spawn->GetLocation());
|
|
|
|
// Do we really need the mutex locks and check to dead_spawns as we remove it from dead spawns at the start of this function
|
|
if (lock && !respawn)
|
|
MDeadSpawns.readlock(__FUNCTION__, __LINE__);
|
|
if(delete_spawn && dead_spawns.count(spawn->GetID()) == 0)
|
|
AddPendingDelete(spawn);
|
|
if (lock && !respawn)
|
|
MDeadSpawns.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
LogWrite(ZONE__DEBUG, 3, "Zone", "Done processing RemoveSpawn function...");
|
|
}
|
|
|
|
Spawn* ZoneServer::GetClosestSpawn(Spawn* spawn, int32 spawn_id){
|
|
Spawn* closest_spawn = 0;
|
|
Spawn* test_spawn = 0;
|
|
float closest_distance = 1000000;
|
|
float test_distance = 0;
|
|
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
test_spawn = itr->second;
|
|
if(test_spawn && test_spawn != spawn && test_spawn->GetDatabaseID() == spawn_id){
|
|
test_distance = test_spawn->GetDistance(spawn);
|
|
if(test_distance < closest_distance){
|
|
closest_distance = test_distance;
|
|
closest_spawn = test_spawn;
|
|
}
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
return closest_spawn;
|
|
}
|
|
|
|
int32 ZoneServer::GetClosestLocation(Spawn* spawn){
|
|
Spawn* closest_spawn = 0;
|
|
Spawn* test_spawn = 0;
|
|
float closest_distance = 1000000;
|
|
float test_distance = 0;
|
|
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
test_spawn = itr->second;
|
|
if(test_spawn){
|
|
test_distance = test_spawn->GetDistance(spawn);
|
|
if(test_distance < closest_distance){
|
|
closest_distance = test_distance;
|
|
closest_spawn = test_spawn;
|
|
if(closest_distance < 10)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
if(closest_spawn)
|
|
return closest_spawn->GetLocation();
|
|
return 0;
|
|
}
|
|
|
|
void ZoneServer::SendQuestUpdates(Client* client, Spawn* spawn){
|
|
if(!client)
|
|
return;
|
|
|
|
if(spawn){
|
|
if(client->GetPlayer()->WasSentSpawn(spawn->GetID()))
|
|
SendSpawnChanges(spawn, client, false, true);
|
|
}
|
|
else{
|
|
client->GetCurrentZone()->SendAllSpawnsForVisChange(client);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SendAllSpawnsForLevelChange(Client* client) {
|
|
Spawn* spawn = 0;
|
|
if (spawn_range_map.count(client) > 0) {
|
|
MutexMap<int32, float >::iterator itr = spawn_range_map.Get(client)->begin();
|
|
while (itr.Next()) {
|
|
spawn = GetSpawnByID(itr->first);
|
|
if (spawn && client->GetPlayer()->WasSentSpawn(spawn->GetID())) {
|
|
SendSpawnChanges(spawn, client, false, true);
|
|
// Attempt to slow down the packet spam sent to the client
|
|
// who the bloody fuck put a Sleep here
|
|
//Sleep(5);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ZoneServer::SendAllSpawnsForSeeInvisChange(Client* client) {
|
|
Spawn* spawn = 0;
|
|
if (spawn_range_map.count(client) > 0) {
|
|
MutexMap<int32, float >::iterator itr = spawn_range_map.Get(client)->begin();
|
|
while (itr.Next()) {
|
|
spawn = GetSpawnByID(itr->first);
|
|
if (spawn && spawn->IsEntity() && (((Entity*)spawn)->IsInvis() || ((Entity*)spawn)->IsStealthed()) && client->GetPlayer()->WasSentSpawn(spawn->GetID())) {
|
|
SendSpawnChanges(spawn, client, true, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ZoneServer::SendAllSpawnsForVisChange(Client* client, bool limitToEntities) {
|
|
Spawn* spawn = 0;
|
|
if (spawn_range_map.count(client) > 0) {
|
|
MutexMap<int32, float >::iterator itr = spawn_range_map.Get(client)->begin();
|
|
while (itr.Next()) {
|
|
spawn = GetSpawnByID(itr->first);
|
|
if (spawn && (!limitToEntities || (limitToEntities && spawn->IsEntity())) && client->GetPlayer()->WasSentSpawn(spawn->GetID())) {
|
|
SendSpawnChanges(spawn, client, false, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::StartZoneSpawnsForLevelThread(Client* client){
|
|
if(zoneShuttingDown)
|
|
return;
|
|
|
|
#ifdef WIN32
|
|
_beginthread(SendLevelChangedSpawns, 0, client);
|
|
#else
|
|
pthread_t thread;
|
|
pthread_create(&thread, NULL, SendLevelChangedSpawns, client);
|
|
pthread_detach(thread);
|
|
#endif
|
|
}
|
|
|
|
void ZoneServer::ReloadClientQuests(){
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(client)
|
|
client->ReloadQuests();
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendCalculatedXP(Player* player, Spawn* victim){
|
|
if (player && victim) {
|
|
if (player->GetGroupMemberInfo()) {
|
|
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
|
|
|
PlayerGroup* group = world.GetGroupManager()->GetGroup(player->GetGroupMemberInfo()->group_id);
|
|
if (group)
|
|
{
|
|
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
|
deque<GroupMemberInfo*>* members = group->GetMembers();
|
|
deque<GroupMemberInfo*>::iterator itr;
|
|
bool skipGrayMob = false;
|
|
|
|
for (itr = members->begin(); itr != members->end(); itr++) {
|
|
GroupMemberInfo* gmi = *itr;
|
|
if (gmi->client) {
|
|
Player* group_member = gmi->client->GetPlayer();
|
|
if(group_member && group_member->GetArrowColor(victim->GetLevel()) == ARROW_COLOR_GRAY) {
|
|
skipGrayMob = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (itr = members->begin(); !skipGrayMob && itr != members->end(); itr++) {
|
|
GroupMemberInfo* gmi = *itr;
|
|
if (gmi->client) {
|
|
Player* group_member = gmi->client->GetPlayer();
|
|
if(group_member) {
|
|
float xp = group_member->CalculateXP(victim) / members->size();
|
|
if (xp > 0) {
|
|
group_member->AddXP((int32)xp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
|
}
|
|
else {
|
|
float xp = player->CalculateXP(victim);
|
|
if (xp > 0) {
|
|
Client* client = ((Player*)player)->GetClient();
|
|
if(!client)
|
|
return;
|
|
player->AddXP((int32)xp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::ProcessFaction(Spawn* spawn, Client* client)
|
|
{
|
|
if(client && !spawn->IsPlayer() && spawn->GetFactionID() > 10)
|
|
{
|
|
bool update_result = false;
|
|
Faction* faction = 0;
|
|
vector<int32>* factions = 0;
|
|
Player* player = client->GetPlayer();
|
|
|
|
bool hasfaction = database.VerifyFactionID(player->GetCharacterID(), spawn->GetFactionID());
|
|
//if 0 they dont have an entry in the db for this faction. if one they do and we can skip it.
|
|
if(hasfaction == 0) {
|
|
//Find out the default for this faction
|
|
sint32 defaultfaction = master_faction_list.GetDefaultFactionValue(spawn->GetFactionID());
|
|
//add the default faction for the player.
|
|
player->SetFactionValue(spawn->GetFactionID(), defaultfaction);
|
|
//save the character so the new default gets written to the db.
|
|
client->Save();
|
|
}
|
|
|
|
if(player->GetFactions()->ShouldDecrease(spawn->GetFactionID()))
|
|
{
|
|
|
|
update_result = player->GetFactions()->DecreaseFaction(spawn->GetFactionID());
|
|
faction = master_faction_list.GetFaction(spawn->GetFactionID());
|
|
|
|
if(faction && update_result)
|
|
client->Message(CHANNEL_FACTION, "Your faction standing with %s got worse.", faction->name.c_str());
|
|
else if(faction)
|
|
client->Message(CHANNEL_FACTION, "Your faction standing with %s could not possibly get any worse.", faction->name.c_str());
|
|
|
|
factions = master_faction_list.GetHostileFactions(spawn->GetFactionID());
|
|
|
|
if(factions)
|
|
{
|
|
vector<int32>::iterator itr;
|
|
|
|
for(itr = factions->begin(); itr != factions->end(); itr++)
|
|
{
|
|
if(player->GetFactions()->ShouldIncrease(*itr))
|
|
{
|
|
|
|
update_result = player->GetFactions()->IncreaseFaction(*itr);
|
|
faction = master_faction_list.GetFaction(*itr);
|
|
|
|
if(faction && update_result)
|
|
client->Message(CHANNEL_FACTION, "Your faction standing with %s got better.", faction->name.c_str());
|
|
else if(faction)
|
|
client->Message(CHANNEL_FACTION, "Your faction standing with %s could not possibly get any better.", faction->name.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
factions = master_faction_list.GetFriendlyFactions(spawn->GetFactionID());
|
|
|
|
if(factions)
|
|
{
|
|
vector<int32>::iterator itr;
|
|
|
|
for(itr = factions->begin(); itr != factions->end(); itr++)
|
|
{
|
|
if(player->GetFactions()->ShouldDecrease(*itr))
|
|
{
|
|
bool hasfaction = database.VerifyFactionID(player->GetCharacterID(),spawn->GetFactionID());
|
|
if(hasfaction == 0) {
|
|
//they do not have the faction. Lets get the default value and feed it in.
|
|
sint32 defaultfaction = master_faction_list.GetDefaultFactionValue(spawn->GetFactionID());
|
|
//add the default faction for the player.
|
|
player->SetFactionValue(spawn->GetFactionID(), defaultfaction);
|
|
}
|
|
|
|
update_result = player->GetFactions()->DecreaseFaction(*itr);
|
|
faction = master_faction_list.GetFaction(*itr);
|
|
|
|
if(faction && update_result)
|
|
client->Message(CHANNEL_FACTION, "Your faction standing with %s got worse.", faction->name.c_str());
|
|
else if(faction)
|
|
client->Message(CHANNEL_FACTION, "Your faction standing with %s could not possibly get any worse.", faction->name.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
EQ2Packet* outapp = client->GetPlayer()->GetFactions()->FactionUpdate(client->GetVersion());
|
|
|
|
if(outapp)
|
|
client->QueuePacket(outapp);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::Despawn(Spawn* spawn, int32 timer){
|
|
if (spawn && movementMgr != nullptr) {
|
|
movementMgr->RemoveMob((Entity*)spawn);
|
|
}
|
|
if(!spawn || spawn->IsPlayer())
|
|
return;
|
|
if(spawn->IsEntity())
|
|
((Entity*)spawn)->InCombat(false);
|
|
if(timer == 0)
|
|
timer = 1;
|
|
AddDeadSpawn(spawn, timer);
|
|
}
|
|
|
|
void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, bool send_packet, int8 type, int8 damage_type, int16 kill_blow_type)
|
|
{
|
|
bool isSpell = (type == DAMAGE_PACKET_TYPE_SIPHON_SPELL || type == DAMAGE_PACKET_TYPE_SIPHON_SPELL2 ||
|
|
type == DAMAGE_PACKET_TYPE_SPELL_DAMAGE || type == DAMAGE_PACKET_TYPE_SPELL_CRIT_DMG ||
|
|
type == DAMAGE_PACKET_TYPE_SPELL_DAMAGE2 || type == DAMAGE_PACKET_TYPE_SPELL_DAMAGE3);
|
|
|
|
MDeadSpawns.readlock(__FUNCTION__, __LINE__);
|
|
if(!dead || this->dead_spawns.count(dead->GetID()) > 0) {
|
|
MDeadSpawns.releasereadlock(__FUNCTION__, __LINE__);
|
|
return;
|
|
}
|
|
MDeadSpawns.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
PacketStruct* packet = 0;
|
|
Client* client = 0;
|
|
vector<int32>* encounter = 0;
|
|
int32 encounter_player_bot_count = 1;
|
|
bool killer_in_encounter = false;
|
|
int8 loot_state = dead->GetLockedNoLoot();
|
|
|
|
if(dead->IsEntity())
|
|
{
|
|
// add any special quest related loot (no_drop_quest_completed)
|
|
if(dead->IsNPC() && ((NPC*)dead)->Brain()) {
|
|
if(!((NPC*)dead)->Brain()->PlayerInEncounter() || (loot_state != ENCOUNTER_STATE_LOCKED && loot_state != ENCOUNTER_STATE_OVERMATCHED)) {
|
|
LogWrite(LOOT__DEBUG, 0, "Loot", "NPC %s bypassed loot drop due to no player in encounter, or encounter state not locked.", ((NPC*)dead)->GetName());
|
|
}
|
|
else {
|
|
Entity* hated = ((NPC*)dead)->Brain()->GetMostHated();
|
|
if(hated) {
|
|
GroupLootMethod loot_method = GroupLootMethod::METHOD_FFA;
|
|
int8 item_rarity = 0;
|
|
if(hated->GetGroupMemberInfo()) {
|
|
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
|
PlayerGroup* group = world.GetGroupManager()->GetGroup(hated->GetGroupMemberInfo()->group_id);
|
|
if (group) {
|
|
loot_method = (GroupLootMethod)group->GetGroupOptions()->loot_method;
|
|
item_rarity = group->GetGroupOptions()->loot_items_rarity;
|
|
LogWrite(LOOT__DEBUG, 0, "Loot", "%s: Loot method set to %u.", dead->GetName(), loot_method);
|
|
}
|
|
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
|
}
|
|
AddLoot((NPC*)dead, hated, loot_method, item_rarity, hated->GetGroupMemberInfo() ? hated->GetGroupMemberInfo()->group_id : 0);
|
|
}
|
|
}
|
|
}
|
|
((Entity*)dead)->InCombat(false);
|
|
dead->SetInitialState(16512, false); // This will make aerial npc's fall after death
|
|
dead->SetHP(0);
|
|
dead->SetSpawnType(3);
|
|
dead->appearance.attackable = 0;
|
|
|
|
|
|
// Remove hate towards dead from all npc's in the zone
|
|
ClearHate((Entity*)dead);
|
|
|
|
// Check kill and death procs
|
|
if (killer && dead != killer){
|
|
if (dead->IsEntity())
|
|
((Entity*)dead)->CheckProcs(PROC_TYPE_DEATH, killer);
|
|
if (killer->IsEntity())
|
|
((Entity*)killer)->CheckProcs(PROC_TYPE_KILL, dead);
|
|
}
|
|
|
|
//Check if caster is alive after death proc called, incase of deathsave
|
|
if (dead->Alive())
|
|
return;
|
|
|
|
RemoveSpellTimersFromSpawn(dead, true, !dead->IsPlayer(), true, !isSpell);
|
|
((Entity*)dead)->IsCasting(false);
|
|
|
|
if(dead->IsPlayer())
|
|
{
|
|
((Player*)dead)->UpdatePlayerStatistic(STAT_PLAYER_TOTAL_DEATHS, 1);
|
|
client = ((Player*)dead)->GetClient();
|
|
|
|
((Entity*)dead)->HandleDeathExperienceDebt(killer);
|
|
|
|
if(client) {
|
|
|
|
if(client->GetPlayer()->DamageEquippedItems(10, client))
|
|
client->QueuePacket(client->GetPlayer()->GetEquipmentList()->serialize(client->GetVersion(), client->GetPlayer()));
|
|
|
|
client->DisplayDeadWindow();
|
|
}
|
|
}
|
|
else if (dead->IsNPC()) {
|
|
encounter = ((NPC*)dead)->Brain()->GetEncounter();
|
|
encounter_player_bot_count = ((NPC*)dead)->Brain()->CountPlayerBotInEncounter();
|
|
if(encounter_player_bot_count < 1)
|
|
encounter_player_bot_count = 1;
|
|
}
|
|
|
|
}
|
|
|
|
dead->SetActionState(0);
|
|
dead->SetTempActionState(0);
|
|
|
|
// Needs npc to have access to the encounter list for who is allowed to loot
|
|
NPC* chest = 0;
|
|
|
|
if (dead->IsNPC() && !((NPC*)dead)->Brain()->PlayerInEncounter()) {
|
|
dead->SetLootCoins(0);
|
|
dead->ClearLoot();
|
|
}
|
|
|
|
Spawn* groupMemberAlive = nullptr;
|
|
// If dead has loot attempt to drop a chest
|
|
if (dead->HasLoot()) {
|
|
if(!(groupMemberAlive = dead->IsSpawnGroupMembersAlive(dead))) {
|
|
chest = ((Entity*)dead)->DropChest();
|
|
}
|
|
else {
|
|
switch(dead->GetLootDropType()) {
|
|
case 0:
|
|
// default drop all chest type as a group
|
|
dead->TransferLoot(groupMemberAlive);
|
|
break;
|
|
case 1:
|
|
// this is a primary mob it drops its own loot
|
|
chest = ((Entity*)dead)->DropChest();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// If dead is an npc get the encounter and loop through it giving out the rewards, no rewards for pets
|
|
if (dead->IsNPC() && !dead->IsPet() && !dead->IsBot()) {
|
|
Spawn* spawn = 0;
|
|
int8 size = encounter->size();
|
|
|
|
for (int8 i = 0; i < encounter->size(); i++) {
|
|
spawn = GetSpawnByID(encounter->at(i), spawnListLocked);
|
|
// set a flag to let us know if the killer is in the encounter
|
|
if (!killer_in_encounter && spawn == killer)
|
|
killer_in_encounter = true;
|
|
|
|
if (spawn && spawn->IsPlayer()) {
|
|
// Update players total kill count
|
|
((Player*)spawn)->UpdatePlayerStatistic(STAT_PLAYER_TOTAL_NPC_KILLS, 1);
|
|
|
|
// If this was an epic mob kill send the announcement for this player
|
|
if (dead->GetDifficulty() >= 10)
|
|
SendEpicMobDeathToGuild((Player*)spawn, dead);
|
|
|
|
// Clear hostile spells from the players spell queue
|
|
spellProcess->RemoveSpellFromQueue((Player*)spawn, true);
|
|
|
|
// Get the client of the player
|
|
client = ((Player*)spawn)->GetClient();
|
|
// valid client?
|
|
if (client) {
|
|
// Check for quest kill updates
|
|
if(!dead->IsNPC() || loot_state != ENCOUNTER_STATE_BROKEN) {
|
|
client->CheckPlayerQuestsKillUpdate(dead);
|
|
}
|
|
// If the dead mob is not a player and if it had a faction with an ID greater or equal to 10 the send faction changes
|
|
if (!dead->IsPlayer() && dead->GetFactionID() > 10)
|
|
ProcessFaction(dead, client);
|
|
|
|
// Send xp...this is currently wrong fix it
|
|
if (spawn != dead && ((Player*)spawn)->GetArrowColor(dead->GetLevel()) >= ARROW_COLOR_GREEN) {
|
|
//SendCalculatedXP((Player*)spawn, dead);
|
|
|
|
float xp = ((Player*)spawn)->CalculateXP(dead) / encounter_player_bot_count;
|
|
if (xp > 0) {
|
|
((Player*)spawn)->AddXP((int32)xp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// If a chest is being dropped add this spawn to the chest's encounter so they can loot it
|
|
if (chest && spawn && spawn->IsEntity())
|
|
chest->Brain()->AddToEncounter((Entity*)spawn);
|
|
}
|
|
}
|
|
|
|
// If a chest is being dropped add it to the world and set the timer to remove it.
|
|
if (chest) {
|
|
AddSpawn(chest);
|
|
AddDeadSpawn(chest, 0xFFFFFFFF);
|
|
LogWrite(LOOT__DEBUG, 0, "Loot", "Adding a chest to the world...");
|
|
}
|
|
|
|
// Reset client pointer
|
|
client = 0;
|
|
|
|
// Killer was not in the encounter, give them the faction hit but no xp
|
|
if (!killer_in_encounter) {
|
|
// make sure the killer is a player and the dead spawn had a faction and wasn't a player
|
|
if (killer && killer->IsPlayer()) {
|
|
if (!dead->IsPlayer() && dead->GetFactionID() > 10) {
|
|
client = ((Player*)killer)->GetClient();
|
|
if (client)
|
|
ProcessFaction(dead, client);
|
|
}
|
|
|
|
// Clear hostile spells from the killers spell queue
|
|
spellProcess->RemoveSpellFromQueue((Player*)killer, true);
|
|
}
|
|
}
|
|
|
|
// Reset client pointer
|
|
client = 0;
|
|
|
|
|
|
vector<Spawn*>* group = dead->GetSpawnGroup();
|
|
if (group && group->size() == 1)
|
|
CallSpawnScript(dead, SPAWN_SCRIPT_GROUP_DEAD, killer);
|
|
safe_delete(group);
|
|
|
|
|
|
// Remove the support functions for the dead spawn
|
|
RemoveSpawnSupportFunctions(dead, !isSpell);
|
|
|
|
// Erase the expire timer if it has one
|
|
if (spawn_expire_timers.count(dead->GetID()) > 0)
|
|
spawn_expire_timers.erase(dead->GetID());
|
|
|
|
// If dead is an npc or object call the spawn scrip and handle instance stuff
|
|
if(dead->IsNPC() || dead->IsObject())
|
|
{
|
|
// handle instance spawn db info
|
|
// we don't care if a NPC or a client kills the spawn, we could have events that cause NPCs to kill NPCs.
|
|
if(dead->GetZone()->GetInstanceID() > 0 && dead->GetSpawnLocationID() > 0)
|
|
{
|
|
// use respawn time to either insert/update entry (likely insert in this situation)
|
|
if(dead->IsNPC())
|
|
database.CreateInstanceSpawnRemoved(dead->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_NPC, dead->GetRespawnTime(),dead->GetZone()->GetInstanceID());
|
|
else if ( dead->IsObject ( ) )
|
|
database.CreateInstanceSpawnRemoved(dead->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_OBJECT, dead->GetRespawnTime(),dead->GetZone()->GetInstanceID());
|
|
}
|
|
else if(!groupMemberAlive && dead->GetSpawnLocationID() > 0 && dead->GetRespawnTime() && !DuplicatedZone()) {
|
|
if(dead->IsNPC())
|
|
database.CreatePersistedRespawn(dead->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_NPC,dead->GetRespawnTime(),GetZoneID());
|
|
else if(dead->IsObject())
|
|
database.CreatePersistedRespawn(dead->GetSpawnLocationID(),SPAWN_ENTRY_TYPE_OBJECT,dead->GetRespawnTime(),GetZoneID());
|
|
}
|
|
|
|
// Call the spawn scripts death() function
|
|
CallSpawnScript(dead, SPAWN_SCRIPT_DEATH, killer);
|
|
const char* zone_script = world.GetZoneScript(this->GetZoneID());
|
|
if (zone_script && lua_interface)
|
|
lua_interface->RunZoneScript(zone_script, "spawn_killed", this, dead, 0, 0, killer);
|
|
}
|
|
|
|
int32 victim_id = dead->GetID();
|
|
int32 attacker_id = 0xFFFFFFFF;
|
|
|
|
if(killer)
|
|
attacker_id = killer->GetID();
|
|
|
|
if(send_packet)
|
|
{
|
|
vector<Client*>::iterator client_itr;
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client->GetPlayer()->WasSentSpawn(victim_id) || (attacker_id != 0xFFFFFFFF && !client->GetPlayer()->WasSentSpawn(attacker_id)) )
|
|
continue;
|
|
else if(killer && killer->GetDistance(client->GetPlayer()) > HEAR_SPAWN_DISTANCE)
|
|
continue;
|
|
|
|
packet = configReader.getStruct("WS_HearDeath", client->GetVersion());
|
|
if(packet)
|
|
{
|
|
if(killer)
|
|
packet->setDataByName("attacker", client->GetPlayer()->GetIDWithPlayerSpawn(killer));
|
|
else
|
|
packet->setDataByName("attacker", 0xFFFFFFFF);
|
|
|
|
packet->setDataByName("defender", client->GetPlayer()->GetIDWithPlayerSpawn(dead));
|
|
packet->setDataByName("damage_type", damage_type);
|
|
packet->setDataByName("blow_type", kill_blow_type);
|
|
|
|
client->QueuePacket(packet->serialize());
|
|
LogWrite(COMBAT__DEBUG, 0, "Combat", "Zone Killing of '%s' by '%s' damage type %u, blow type %u", dead->GetName(), killer ? killer->GetName() : "", damage_type, kill_blow_type);
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
|
|
int32 pop_timer = 0xFFFFFFFF;
|
|
if(killer && killer->IsNPC())
|
|
{
|
|
// Call the spawn scripts killed() function
|
|
CallSpawnScript(killer, SPAWN_SCRIPT_KILLED, dead);
|
|
|
|
if(!dead->IsPlayer())
|
|
{
|
|
LogWrite(MISC__TODO, 1, "TODO", "Whenever pets are added, check for pet kills\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
|
|
// Set the time for the corpse to linger to 5 sec
|
|
//pop_timer = 5000;
|
|
// commented out the timer so in the event the killer is not a player (pet, guard, something else i haven't thought of)
|
|
// the corpse doesn't vanish sooner then it should if it had loot for the players. AddDeadSpawn() will set timers based on if
|
|
// the corpse has loot or not if the timer value (pop_timer) is 0xFFFFFFFF
|
|
}
|
|
}
|
|
|
|
// If the dead spawns was not a player add it to the dead spawn list
|
|
if (!dead->IsPlayer() && !dead->IsBot())
|
|
AddDeadSpawn(dead, pop_timer);
|
|
|
|
// if dead was a player clear hostile spells from its spell queue
|
|
if (dead->IsPlayer())
|
|
spellProcess->RemoveSpellFromQueue((Player*)dead, true);
|
|
|
|
if (dead->IsNPC())
|
|
((NPC*)dead)->Brain()->ClearHate();
|
|
|
|
safe_delete(encounter);
|
|
}
|
|
|
|
void ZoneServer::SendDamagePacket(Spawn* attacker, Spawn* victim, int8 type1, int8 type2, int8 damage_type, int16 damage, const char* spell_name) {
|
|
//Scat: was set but never being used anywhere. i see references to 0xFFFFFFFF below so could be old code not used anymore
|
|
//int32 attacker_id = 0xFFFFFFFF;
|
|
//if(attacker)
|
|
// attacker_id = attacker->GetID();
|
|
PacketStruct* packet = 0;
|
|
Client* client = 0;
|
|
if (attacker && victim && victim->IsPlayer() && victim->GetTarget() == 0) {
|
|
client = ((Player*)victim)->GetClient();
|
|
if (client)
|
|
client->TargetSpawn(attacker);
|
|
}
|
|
|
|
if(damage_type == DAMAGE_PACKET_DAMAGE_TYPE_FOCUS) {
|
|
damage_type = 0;
|
|
type2 = DAMAGE_PACKET_RESULT_FOCUS;
|
|
}
|
|
|
|
vector<Client*>::iterator client_itr;
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if (!client || (client->GetPlayer() != attacker && client->GetPlayer() != victim && ((attacker && client->GetPlayer()->WasSentSpawn(attacker->GetID()) == false) || (victim && client->GetPlayer()->WasSentSpawn(victim->GetID()) == false))))
|
|
continue;
|
|
if (attacker && attacker->GetDistance(client->GetPlayer()) > 50)
|
|
continue;
|
|
if (victim && victim->GetDistance(client->GetPlayer()) > 50)
|
|
continue;
|
|
|
|
int8 mod_type1 = type1;
|
|
if(client->GetVersion() <= 561) {
|
|
mod_type1 = DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE;
|
|
}
|
|
|
|
switch (mod_type1) {
|
|
case DAMAGE_PACKET_TYPE_SIPHON_SPELL:
|
|
case DAMAGE_PACKET_TYPE_SIPHON_SPELL2:
|
|
packet = configReader.getStruct("WS_HearSiphonSpellDamage", client->GetVersion());
|
|
break;
|
|
case DAMAGE_PACKET_TYPE_MULTIPLE_DAMAGE:
|
|
if (client->GetVersion() > 561)
|
|
packet = configReader.getStruct("WS_HearMultipleDamage", client->GetVersion());
|
|
else
|
|
packet = configReader.getStruct("WS_HearSimpleDamage", client->GetVersion());
|
|
break;
|
|
case DAMAGE_PACKET_TYPE_SIMPLE_CRIT_DMG:
|
|
case DAMAGE_PACKET_TYPE_SIMPLE_DAMAGE:
|
|
packet = configReader.getStruct("WS_HearSimpleDamage", client->GetVersion());
|
|
break;
|
|
case DAMAGE_PACKET_TYPE_SPELL_DAMAGE2:
|
|
case DAMAGE_PACKET_TYPE_SPELL_DAMAGE3:
|
|
case DAMAGE_PACKET_TYPE_SPELL_CRIT_DMG:
|
|
case DAMAGE_PACKET_TYPE_SPELL_DAMAGE:
|
|
if (client->GetVersion() > 561)
|
|
packet = configReader.getStruct("WS_HearSpellDamage", client->GetVersion());
|
|
else
|
|
packet = configReader.getStruct("WS_HearSimpleDamage", client->GetVersion());
|
|
if (packet)
|
|
packet->setSubstructDataByName("header", "unknown", 5);
|
|
break;
|
|
case DAMAGE_PACKET_TYPE_RANGE_DAMAGE:
|
|
packet = configReader.getStruct("WS_HearRangeDamage", client->GetVersion());
|
|
break;
|
|
case DAMAGE_PACKET_TYPE_RANGE_SPELL_DMG:
|
|
case DAMAGE_PACKET_TYPE_RANGE_SPELL_DMG2:
|
|
packet = configReader.getStruct("WS_HearRangeDamage", client->GetVersion());
|
|
break;
|
|
default:
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "Unknown Damage Packet type: %i in ZoneServer::SendDamagePacket.", type1);
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return;
|
|
}
|
|
|
|
if (packet) {
|
|
if (client->GetVersion() > 561) {
|
|
packet->setSubstructDataByName("header", "packet_type", type1);
|
|
packet->setSubstructDataByName("header", "result_type", type2);
|
|
packet->setDataByName("damage_type", damage_type);
|
|
packet->setDataByName("damage", damage);
|
|
}
|
|
else {
|
|
switch (type2) {
|
|
case DAMAGE_PACKET_RESULT_MISS:
|
|
packet->setSubstructDataByName("header", "result_type", 1);
|
|
break;
|
|
case DAMAGE_PACKET_RESULT_DODGE:
|
|
packet->setSubstructDataByName("header", "result_type", 2);
|
|
break;
|
|
case DAMAGE_PACKET_RESULT_PARRY:
|
|
packet->setSubstructDataByName("header", "result_type", 3);
|
|
break;
|
|
case DAMAGE_PACKET_RESULT_RIPOSTE:
|
|
packet->setSubstructDataByName("header", "result_type", 4);
|
|
break;
|
|
case DAMAGE_PACKET_RESULT_BLOCK:
|
|
packet->setSubstructDataByName("header", "result_type", 5);
|
|
break;
|
|
case DAMAGE_PACKET_RESULT_INVULNERABLE:
|
|
packet->setSubstructDataByName("header", "result_type", 7);
|
|
break;
|
|
case DAMAGE_PACKET_RESULT_RESIST:
|
|
packet->setSubstructDataByName("header", "result_type", 9);
|
|
break;
|
|
case DAMAGE_PACKET_RESULT_REFLECT:
|
|
packet->setSubstructDataByName("header", "result_type", 10);
|
|
break;
|
|
case DAMAGE_PACKET_RESULT_IMMUNE:
|
|
packet->setSubstructDataByName("header", "result_type", 11);
|
|
break;
|
|
}
|
|
packet->setArrayLengthByName("num_dmg", 1);
|
|
packet->setSubstructDataByName("header", "defender_proxy", client->GetPlayer()->GetIDWithPlayerSpawn(victim));
|
|
packet->setArrayDataByName("damage_type", damage_type);
|
|
packet->setArrayDataByName("damage", damage);
|
|
}
|
|
|
|
if (!attacker)
|
|
packet->setSubstructDataByName("header", "attacker", 0xFFFFFFFF);
|
|
else
|
|
packet->setSubstructDataByName("header", "attacker", client->GetPlayer()->GetIDWithPlayerSpawn(attacker));
|
|
packet->setSubstructDataByName("header", "defender", client->GetPlayer()->GetIDWithPlayerSpawn(victim));
|
|
if (spell_name) {
|
|
packet->setDataByName("spell", 1);
|
|
packet->setDataByName("spell_name", spell_name);
|
|
}
|
|
EQ2Packet* app = packet->serialize();
|
|
//DumpPacket(app);
|
|
client->QueuePacket(app);
|
|
safe_delete(packet);
|
|
packet = 0;
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendHealPacket(Spawn* caster, Spawn* target, int16 heal_type, int32 heal_amt, const char* spell_name){
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client || (client->GetPlayer() != caster && ((caster && client->GetPlayer()->WasSentSpawn(caster->GetID()) == false) || (target && client->GetPlayer()->WasSentSpawn(target->GetID()) == false))))
|
|
continue;
|
|
if(caster && caster->GetDistance(client->GetPlayer()) > 50)
|
|
continue;
|
|
if(target && target->GetDistance(client->GetPlayer()) > 50)
|
|
continue;
|
|
|
|
|
|
PacketStruct* packet = configReader.getStruct("WS_HearHeal", client->GetVersion());
|
|
if (packet) {
|
|
packet->setDataByName("caster", client->GetPlayer()->GetIDWithPlayerSpawn(caster));
|
|
packet->setDataByName("target", client->GetPlayer()->GetIDWithPlayerSpawn(target));
|
|
packet->setDataByName("heal_amt", heal_amt);
|
|
packet->setDataByName("spellname", spell_name);
|
|
packet->setDataByName("type", heal_type);
|
|
packet->setDataByName("unknown2", 1);
|
|
EQ2Packet* app = packet->serialize();
|
|
client->QueuePacket(app);
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendThreatPacket(Spawn* caster, Spawn* target, int32 threat_amt, const char* spell_name) {
|
|
Client* client = 0;
|
|
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client || (client->GetPlayer() != caster && ((caster && client->GetPlayer()->WasSentSpawn(caster->GetID()) == false) || (target && client->GetPlayer()->WasSentSpawn(target->GetID()) == false))))
|
|
continue;
|
|
if(caster && caster->GetDistance(client->GetPlayer()) > 50)
|
|
continue;
|
|
if(target && target->GetDistance(client->GetPlayer()) > 50)
|
|
continue;
|
|
|
|
if(client->GetVersion() <= 561) {
|
|
int8 channel = 46;
|
|
|
|
if(client->GetPlayer() == caster || client->GetPlayer() == target)
|
|
channel = 42;
|
|
|
|
client->Message(channel, "%s increases %s hate with %s for %u threat.", spell_name, (client->GetPlayer() == caster) ? "YOUR" : caster->GetName(), (client->GetPlayer() == target) ? "YOU" : target->GetName(), threat_amt);
|
|
}
|
|
else {
|
|
PacketStruct* packet = configReader.getStruct("WS_HearThreatCmd", client->GetVersion());
|
|
if (packet) {
|
|
packet->setDataByName("spell_name", spell_name);
|
|
packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(caster));
|
|
packet->setDataByName("target", client->GetPlayer()->GetIDWithPlayerSpawn(target));
|
|
packet->setDataByName("threat_amount", threat_amt);
|
|
|
|
client->QueuePacket(packet->serialize());
|
|
}
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
|
|
void ZoneServer::SendYellPacket(Spawn* yeller, float max_distance) {
|
|
Client* client = 0;
|
|
|
|
|
|
string yellMsg = std::string(yeller->GetName()) + " yelled for help!";
|
|
vector<Client*>::iterator client_itr;
|
|
PacketStruct* packet = nullptr;
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client || !client->GetPlayer() || client->GetPlayer()->WasSentSpawn(yeller->GetID()) == false)
|
|
continue;
|
|
if(client->GetPlayer()->GetDistance(yeller) > max_distance)
|
|
continue;
|
|
|
|
if(packet && packet->GetVersion() == client->GetVersion()) {
|
|
client->QueuePacket(packet->serialize());
|
|
}
|
|
else {
|
|
safe_delete(packet);
|
|
packet = configReader.getStruct("WS_EncounterBroken", client->GetVersion());
|
|
if (packet) {
|
|
packet->setDataByName("message", yellMsg.c_str());
|
|
/* none of the other data seems necessary, keeping for reference for future disassembly
|
|
packet2->setDataByName("unknown2", 0x40);
|
|
packet2->setDataByName("unknown3", 0x40);
|
|
packet2->setDataByName("unknown4", 0xFF);
|
|
packet2->setDataByName("unknown5", 0xFF);
|
|
packet2->setDataByName("unknown6", 0xFF);*/
|
|
client->QueuePacket(packet->serialize());
|
|
}
|
|
}
|
|
}
|
|
safe_delete(packet);
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendSpellFailedPacket(Client* client, int16 error){
|
|
if(!client)
|
|
return;
|
|
|
|
PacketStruct* packet = configReader.getStruct("WS_DisplaySpellFailed", client->GetVersion());
|
|
if(packet){
|
|
/* Temp solution, need to modify the error code before this function and while we still have access to the spell/combat art */
|
|
error = master_spell_list.GetSpellErrorValue(client->GetVersion(), error);
|
|
|
|
if(client->GetVersion() > 373 && client->GetVersion() <= 561 && error) {
|
|
error += 1;
|
|
}
|
|
|
|
packet->setDataByName("error_code", error);
|
|
//packet->PrintPacket();
|
|
client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SendInterruptPacket(Spawn* interrupted, LuaSpell* spell, bool fizzle){
|
|
if(!interrupted || !spell)
|
|
return;
|
|
|
|
EQ2Packet* outapp = 0;
|
|
PacketStruct* packet = 0;
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client || !client->GetPlayer()->WasSentSpawn(interrupted->GetID()))
|
|
continue;
|
|
packet = configReader.getStruct(fizzle ? "WS_SpellFizzle" : "WS_Interrupt", client->GetVersion());
|
|
if(packet){
|
|
packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(interrupted));
|
|
packet->setArrayLengthByName("num_targets", spell->targets.size());
|
|
for (int32 i = 0; i < spell->targets.size(); i++)
|
|
packet->setArrayDataByName("target_id", client->GetPlayer()->GetIDWithPlayerSpawn(client->GetPlayer()->GetZone()->GetSpawnByID(spell->targets[i])), i);
|
|
packet->setDataByName("spell_id", spell->spell->GetSpellID());
|
|
outapp = packet->serialize();
|
|
client->QueuePacket(outapp);
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
safe_delete(packet);
|
|
}
|
|
|
|
void ZoneServer::SendCastSpellPacket(LuaSpell* spell, Entity* caster, int32 spell_visual_override, int16 casttime_override){
|
|
EQ2Packet* outapp = 0;
|
|
PacketStruct* packet = 0;
|
|
Client* client = 0;
|
|
if(!caster || !spell || !spell->spell || spell->interrupted)
|
|
return;
|
|
|
|
if(spell->is_damage_spell && (!spell->has_damaged || spell->resisted)) {
|
|
// we did not successfully hit target, so we should not send the visual
|
|
return;
|
|
}
|
|
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client)
|
|
continue;
|
|
|
|
packet = configReader.getStruct("WS_HearCastSpell", client->GetVersion());
|
|
if(packet){
|
|
int32 caster_id = client->GetPlayer()->GetIDWithPlayerSpawn(caster);
|
|
|
|
if(!caster_id) {
|
|
safe_delete(packet);
|
|
continue;
|
|
}
|
|
|
|
packet->setDataByName("spawn_id", caster_id);
|
|
packet->setArrayLengthByName("num_targets", spell->targets.size());
|
|
for (int32 i = 0; i < spell->targets.size(); i++) {
|
|
int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(spell->caster->GetZone()->GetSpawnByID(spell->targets[i]));
|
|
if(target_id) {
|
|
packet->setArrayDataByName("target", target_id, i);
|
|
}
|
|
else {
|
|
packet->setArrayDataByName("target", 0xFFFFFFFF, i);
|
|
}
|
|
}
|
|
|
|
int32 visual_to_use = spell_visual_override > 0 ? spell_visual_override : spell->spell->GetSpellData()->spell_visual;
|
|
int32 visual = client->GetSpellVisualOverride(visual_to_use);
|
|
|
|
packet->setDataByName("spell_visual", visual); //result
|
|
if(casttime_override != 0xFFFF) {
|
|
packet->setDataByName("cast_time", casttime_override*.01f); //delay
|
|
}
|
|
else {
|
|
packet->setDataByName("cast_time", spell->spell->GetSpellData()->cast_time*.01f); //delay
|
|
}
|
|
packet->setDataByName("spell_id", spell->spell->GetSpellID());
|
|
packet->setDataByName("spell_level", 1);
|
|
packet->setDataByName("spell_tier", spell->spell->GetSpellData()->tier);
|
|
outapp = packet->serialize();
|
|
client->QueuePacket(outapp);
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
safe_delete(packet);
|
|
}
|
|
|
|
void ZoneServer::SendCastSpellPacket(int32 spell_visual, Spawn* target, Spawn* caster) {
|
|
if (target) {
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
Client* client = *client_itr;
|
|
if (!client)
|
|
continue;
|
|
PacketStruct* packet = configReader.getStruct("WS_HearCastSpell", client->GetVersion());
|
|
if (packet) {
|
|
|
|
int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(target);
|
|
if(!target_id) { // client is not aware of spawn
|
|
safe_delete(packet);
|
|
continue;
|
|
}
|
|
|
|
if (!caster) {
|
|
packet->setDataByName("spawn_id", 0xFFFFFFFF);
|
|
}
|
|
else {
|
|
int32 caster_id = client->GetPlayer()->GetIDWithPlayerSpawn(caster);
|
|
|
|
if(!caster_id) { // client is not aware of spawn
|
|
safe_delete(packet);
|
|
continue;
|
|
}
|
|
packet->setDataByName("spawn_id", caster_id);
|
|
}
|
|
packet->setArrayLengthByName("num_targets", 1);
|
|
packet->setArrayDataByName("target", target_id);
|
|
|
|
int32 visual = client->GetSpellVisualOverride(spell_visual);
|
|
|
|
packet->setDataByName("spell_visual", visual);
|
|
packet->setDataByName("cast_time", 0);
|
|
packet->setDataByName("spell_id", 0);
|
|
packet->setDataByName("spell_level", 0);
|
|
packet->setDataByName("spell_tier", 1);
|
|
client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SendCastEntityCommandPacket(EntityCommand* entity_command, int32 spawn_id, int32 target_id) {
|
|
if (entity_command) {
|
|
Spawn* spawn = GetSpawnByID(spawn_id);
|
|
Spawn* target = GetSpawnByID(target_id);
|
|
if (!spawn || !target)
|
|
return;
|
|
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if (!client || !client->GetPlayer()->WasSentSpawn(spawn_id) || !client->GetPlayer()->WasSentSpawn(target_id))
|
|
continue;
|
|
PacketStruct* packet = configReader.getStruct("WS_HearCastSpell", client->GetVersion());
|
|
if (packet) {
|
|
int32 caster_id = client->GetPlayer()->GetIDWithPlayerSpawn(spawn);
|
|
int32 target_id = client->GetPlayer()->GetIDWithPlayerSpawn(target);
|
|
|
|
if(!caster_id || !target_id)
|
|
continue;
|
|
|
|
packet->setDataByName("spawn_id", caster_id);
|
|
packet->setArrayLengthByName("num_targets", 1);
|
|
packet->setArrayDataByName("target", target_id);
|
|
packet->setDataByName("num_targets", 1);
|
|
packet->setDataByName("spell_visual", entity_command->spell_visual); //result
|
|
packet->setDataByName("cast_time", entity_command->cast_time * 0.01); //delay
|
|
packet->setDataByName("spell_id", 1);
|
|
packet->setDataByName("spell_level", 1);
|
|
packet->setDataByName("spell_tier", 1);
|
|
EQ2Packet* outapp = packet->serialize();
|
|
client->QueuePacket(outapp);
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::StartZoneInitialSpawnThread(Client* client){
|
|
if(zoneShuttingDown)
|
|
return;
|
|
|
|
#ifdef WIN32
|
|
_beginthread(SendInitialSpawns, 0, client);
|
|
#else
|
|
pthread_t thread;
|
|
pthread_create(&thread, NULL, SendInitialSpawns, client);
|
|
pthread_detach(thread);
|
|
#endif
|
|
}
|
|
|
|
void ZoneServer::SendZoneSpawns(Client* client){
|
|
int8 count = 0;
|
|
while (LoadingData && count <= 6000) { //sleep for max of 60 seconds (60000ms) while the maps are loading
|
|
count++;
|
|
Sleep(10);
|
|
}
|
|
count = 0;
|
|
int16 size = 0;
|
|
//give the spawn thread a tad bit of time to add the pending_spawns to spawn_list (up to 10 seconds)
|
|
while (count < 1000) {
|
|
MPendingSpawnListAdd.readlock(__FUNCTION__, __LINE__);
|
|
size = pending_spawn_list_add.size();
|
|
MPendingSpawnListAdd.releasereadlock(__FUNCTION__, __LINE__);
|
|
if (size == 0)
|
|
break;
|
|
Sleep(10);
|
|
count++;
|
|
}
|
|
initial_spawn_threads_active++;
|
|
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
Spawn* spawn = itr->second;
|
|
if (spawn) {
|
|
if(spawn == client->GetPlayer() && (client->IsReloadingZone() || client->GetPlayer()->IsReturningFromLD()))
|
|
{
|
|
if(!client->GetPlayer()->SetSpawnMap(spawn))
|
|
continue;
|
|
}
|
|
|
|
CheckSpawnRange(client, spawn, true);
|
|
}
|
|
}
|
|
|
|
CheckSendSpawnToClient(client, true);
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
client->SetConnected(true);
|
|
ClientPacketFunctions::SendFinishedEntitiesList(client);
|
|
initial_spawn_threads_active--;
|
|
}
|
|
|
|
vector<Entity*> ZoneServer::GetPlayers(){
|
|
vector<Entity*> ret;
|
|
Client* client = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
ret.push_back(client->GetPlayer());
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int16 ZoneServer::SetSpawnTargetable(Spawn* spawn, float distance){
|
|
Spawn* test_spawn = 0;
|
|
int16 ret_val = 0;
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
test_spawn = itr->second;
|
|
if(test_spawn){
|
|
if(test_spawn->GetDistance(spawn) <= distance){
|
|
test_spawn->SetTargetable(1);
|
|
ret_val++;
|
|
}
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return ret_val;
|
|
}
|
|
|
|
int16 ZoneServer::SetSpawnTargetable(int32 spawn_id){
|
|
Spawn* spawn = 0;
|
|
int16 ret_val = 0;
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if(spawn){
|
|
if(spawn->GetDatabaseID() == spawn_id){
|
|
spawn->SetTargetable(1);
|
|
ret_val++;
|
|
}
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return ret_val;
|
|
}
|
|
|
|
ZoneInfoSlideStruct* ZoneServer::GenerateSlideStruct(float unknown1a, float unknown1b, int32 unknown2a, int32 unknown2b, int32 unknown3, int32 unknown4, const char* slide, const char* voiceover, int32 key1, int32 key2) {
|
|
ZoneInfoSlideStructInfo* info = new ZoneInfoSlideStructInfo();
|
|
memset(info, 0, sizeof(ZoneInfoSlideStructInfo));
|
|
info->unknown1[0] = unknown1a;
|
|
info->unknown1[1] = unknown1b;
|
|
info->unknown2[0] = unknown2a;
|
|
info->unknown2[1] = unknown2b;
|
|
info->unknown3 = unknown3;
|
|
info->unknown4 = unknown4;
|
|
int8 length = strlen(slide);
|
|
if (length >= 128)
|
|
length = 127;
|
|
strncpy(info->slide, slide, length);
|
|
length = strlen(voiceover);
|
|
if (length >= 128)
|
|
length = 127;
|
|
strncpy(info->voiceover, voiceover, length);
|
|
info->key1 = key1;
|
|
info->key2 = key2;
|
|
ZoneInfoSlideStruct* ret = new ZoneInfoSlideStruct();
|
|
ret->info = info;
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::AddZoneInfoSlideStructTransitionInfo(ZoneInfoSlideStruct* info, int32 x, int32 y, float zoom, float transition_time) {
|
|
ZoneInfoSlideStructTransitionInfo* transition_info = new ZoneInfoSlideStructTransitionInfo();
|
|
transition_info->transition_x = x;
|
|
transition_info->transition_y = y;
|
|
transition_info->transition_zoom = zoom;
|
|
transition_info->transition_time = transition_time;
|
|
info->slide_transition_info.push_back(transition_info);
|
|
}
|
|
|
|
vector<ZoneInfoSlideStruct*>* ZoneServer::GenerateTutorialSlides() {
|
|
vector<ZoneInfoSlideStruct*>* slides = new vector<ZoneInfoSlideStruct*>();
|
|
ZoneInfoSlideStruct* slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt01_final001.dds", "voiceover/english/antonia_intro/antonia_intro_001_64.mp3", 2519553957, 1010319376);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 495, 1, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 495, 1.5, 16);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt02_final001.dds", "voiceover/english/antonia_intro/antonia_intro_002_64.mp3", 567178266, 3055063399);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 600, 365, 1.60000002384186, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 800, 370, 1.39999997615814, 9);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 420, 1.20000004768372, 8);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt03_final001.dds", "voiceover/english/antonia_intro/antonia_intro_003_64.mp3", 3171561318, 593374281);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 420, 1.20000004768372, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 750, 320, 1.60000002384186, 10);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 575, 265, 2.29999995231628, 10);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt04_final001.dds", "voiceover/english/antonia_intro/antonia_intro_004_64.mp3", 1959944485, 4285605574);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 420, 2.5, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 420, 1.70000004768372, 8);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 675, 390, 2.20000004768372, 11);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt05_final001.dds", "voiceover/english/antonia_intro/antonia_intro_005_64.mp3", 609693392, 260295215);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 750, 500, 2.79999995231628, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 720, 300, 2.5, 9);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 975, 270, 2.20000004768372, 9);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt06_final001.dds", "voiceover/english/antonia_intro/antonia_intro_006_64.mp3", 3056613203, 775201556);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 495, 1.89999997615814, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 475, 1, 24);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt07_final001.dds", "voiceover/english/antonia_intro/antonia_intro_007_64.mp3", 3113327662, 1299367895);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 1400, 420, 2.40000009536743, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 1200, 375, 1.70000004768372, 7);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 800, 225, 2.29999995231628, 7);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt08_final001.dds", "voiceover/english/antonia_intro/antonia_intro_008_64.mp3", 2558791235, 2674773065);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 495, 1, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 495, 1.5, 27);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt09_final001.dds", "voiceover/english/antonia_intro/antonia_intro_009_64.mp3", 4029296401, 1369011033);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 715, 305, 2.40000009536743, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 730, 325, 1.79999995231628, 6);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 395, 1.5, 5);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 1360, 330, 1.79999995231628, 9);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt10_final001.dds", "voiceover/english/antonia_intro/antonia_intro_010_64.mp3", 3055524517, 3787058332);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 670, 675, 2.20000004768372, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 710, 390, 1.79999995231628, 7);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 415, 1.60000002384186, 5.5);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 1250, 675, 1.79999995231628, 8);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt11_final001.dds", "voiceover/english/antonia_intro/antonia_intro_011_64.mp3", 3525586740, 812068950);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 495, 1, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 495, 2, 19);
|
|
slides->push_back(slide);
|
|
|
|
slide = GenerateSlideStruct(0.5, 0.5, 15, 15, 1842, 1012, "images/slideshows/boat_06p_tutorial02/lore_chapt12_final001.dds", "voiceover/english/antonia_intro/antonia_intro_012_64.mp3", 3493874350, 2037661816);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 495, 2, 0);
|
|
AddZoneInfoSlideStructTransitionInfo(slide, 920, 495, 1, 43);
|
|
slides->push_back(slide);
|
|
|
|
return slides;
|
|
}
|
|
|
|
EQ2Packet* ZoneServer::GetZoneInfoPacket(Client* client){
|
|
PacketStruct* packet = configReader.getStruct("WS_ZoneInfo", client->GetVersion());
|
|
packet->setSmallStringByName("server1",net.GetWorldName());
|
|
packet->setSmallStringByName("server2",net.GetWorldName());
|
|
packet->setDataByName("unknown1", 1, 1);//1, 1
|
|
int32 expansions = EXPANSION_UNKNOWN + EXPANSION_DOF + EXPANSION_KOS + EXPANSION_EOF + EXPANSION_ROK + EXPANSION_TSO + EXPANSION_DOV;
|
|
//packet->setDataByName("expansions_enabled", 82313211);//expansions 63181
|
|
//packet->setDataByName("expansions_enabled", 552075103);//expansions 63182
|
|
packet->setDataByName("expansions_enabled", 4294967295);//expansions 1096 //612499455 works
|
|
if (client->GetVersion() >= 1193) {
|
|
packet->setDataByName("unknown3", 4294967295, 0); // DOV and down
|
|
packet->setDataByName("unknown3", 4294967295, 1); //COE and up
|
|
packet->setDataByName("unknown3", 4294967295, 2);
|
|
}
|
|
else
|
|
packet->setDataByName("unknown3", 4294967295, 0); // DOV and down
|
|
|
|
packet->setSmallStringByName("auction_website", "eq2emulator.net");
|
|
packet->setDataByName("auction_port", 80);
|
|
packet->setSmallStringByName("upload_page", "test_upload.m");
|
|
packet->setSmallStringByName("upload_key", "dsya987yda9");
|
|
packet->setSmallStringByName("zone", GetZoneFile());
|
|
//packet->setSmallStringByName("zone2", GetZoneName());
|
|
|
|
//if ( strlen(GetZoneSkyFile()) > 0 )
|
|
// packet->setSmallStringByName("zone_unknown2", GetZoneSkyFile()); // used for the sky map
|
|
|
|
packet->setSmallStringByName("zone_desc", GetZoneDescription());
|
|
packet->setSmallStringByName("char_name", client->GetPlayer()->GetName());
|
|
|
|
packet->setDataByName("x", client->GetPlayer()->GetX());
|
|
packet->setDataByName("y", client->GetPlayer()->GetY());
|
|
packet->setDataByName("z", client->GetPlayer()->GetZ());
|
|
|
|
if ((GetZoneFile() && strcmp("boat_06p_tutorial02", GetZoneFile()) == 0) && client->GetPlayer()->GetX() == this->GetSafeX() && client->GetPlayer()->GetY() == this->GetSafeY() && client->GetPlayer()->GetZ() == this->GetSafeZ()) { //basically the only time the player will see this is if their zone in coords are the exact same as the safe coords (they haven't moved)
|
|
vector<ZoneInfoSlideStruct*>* slides = GenerateTutorialSlides();
|
|
if (slides) {
|
|
packet->setArrayLengthByName("num_slides", slides->size());
|
|
ZoneInfoSlideStruct* slide = 0;
|
|
for (int8 i = 0; i < slides->size(); i++) {
|
|
slide = slides->at(i);
|
|
packet->setArrayDataByName("unknown1", slide->info->unknown1[0], i, 0);
|
|
packet->setArrayDataByName("unknown1", slide->info->unknown1[1], i, 1);
|
|
packet->setArrayDataByName("unknown2", slide->info->unknown2[0], i, 0);
|
|
packet->setArrayDataByName("unknown2", slide->info->unknown2[1], i, 1);
|
|
packet->setArrayDataByName("unknown3", slide->info->unknown3, i);
|
|
packet->setArrayDataByName("unknown4", slide->info->unknown4, i);
|
|
packet->setArrayDataByName("slide", slide->info->slide, i);
|
|
packet->setArrayDataByName("voiceover", slide->info->voiceover, i);
|
|
packet->setArrayDataByName("key1", slide->info->key1, i);
|
|
packet->setArrayDataByName("key2", slide->info->key2, i);
|
|
packet->setSubArrayLengthByName("num_transitions", slide->slide_transition_info.size(), i);
|
|
for (int8 x = 0; x < slide->slide_transition_info.size(); x++) {
|
|
packet->setSubArrayDataByName("transition_x", slide->slide_transition_info[x]->transition_x, i, x);
|
|
packet->setSubArrayDataByName("transition_y", slide->slide_transition_info[x]->transition_y, i, x);
|
|
packet->setSubArrayDataByName("transition_zoom", slide->slide_transition_info[x]->transition_zoom, i, x);
|
|
packet->setSubArrayDataByName("transition_time", slide->slide_transition_info[x]->transition_time, i, x);
|
|
safe_delete(slide->slide_transition_info[x]);
|
|
}
|
|
safe_delete(slide->info);
|
|
safe_delete(slide);
|
|
}
|
|
}
|
|
safe_delete(slides);
|
|
}
|
|
|
|
if(rule_manager.GetZoneRule(GetZoneID(), R_Zone, UseMapUnderworldCoords)->GetBool() && client->GetPlayer()->GetMap()) {
|
|
packet->setDataByName("underworld", client->GetPlayer()->GetMap()->GetMinY() + rule_manager.GetZoneRule(GetZoneID(), R_Zone, MapUnderworldCoordOffset)->GetFloat());
|
|
}
|
|
else {
|
|
packet->setDataByName("underworld", underworld);
|
|
}
|
|
|
|
// unknown3 can prevent screen shots from being taken if
|
|
//packet->setDataByName("unknown3", 2094661567, 1); // Screenshots allowed with this value
|
|
//packet->setDataByName("unknown3", 3815767999, 1); // Screenshots disabled with this value
|
|
//packet->setDataByName("unknown3", 1, 2);
|
|
|
|
/*if (client->GetVersion() >= 63587) {
|
|
packet->setArrayLengthByName("num_exp_feature_bytes", 9);
|
|
packet->setArrayDataByName("exp_feature_bytes", 95, 0);//kos and dof
|
|
packet->setArrayDataByName("exp_feature_bytes", 255, 1);//eof rok tso sf dov coe tov
|
|
packet->setArrayDataByName("exp_feature_bytes", 247, 2);//aom tot ka exp14
|
|
packet->setArrayDataByName("exp_feature_bytes", 32, 3);//rum cellar
|
|
packet->setArrayDataByName("exp_feature_bytes", 140, 4);
|
|
packet->setArrayDataByName("exp_feature_bytes", 62, 5);
|
|
packet->setArrayDataByName("exp_feature_bytes", 0, 6);
|
|
packet->setArrayDataByName("exp_feature_bytes", 45, 7);
|
|
packet->setArrayDataByName("exp_feature_bytes", 128, 8);
|
|
|
|
packet->setArrayLengthByName("num_unknown3b_bytes", 9);
|
|
packet->setArrayDataByName("unknown3b_bytes", 95, 0);
|
|
packet->setArrayDataByName("unknown3b_bytes", 255, 1);
|
|
packet->setArrayDataByName("unknown3b_bytes", 247, 2);
|
|
packet->setArrayDataByName("unknown3b_bytes", 237, 3);
|
|
packet->setArrayDataByName("unknown3b_bytes", 143, 4);
|
|
packet->setArrayDataByName("unknown3b_bytes", 255, 5);
|
|
packet->setArrayDataByName("unknown3b_bytes", 255, 6);
|
|
packet->setArrayDataByName("unknown3b_bytes", 255, 7);
|
|
packet->setArrayDataByName("unknown3b_bytes", 128, 8);
|
|
}
|
|
else if (client->GetVersion() >= 63214) {
|
|
packet->setArrayLengthByName("num_exp_feature_bytes", 9);
|
|
packet->setArrayDataByName("exp_feature_bytes", 95, 0);//kos and dof
|
|
packet->setArrayDataByName("exp_feature_bytes", 255, 1);//eof rok tso sf dov coe tov
|
|
packet->setArrayDataByName("exp_feature_bytes", 247, 2);//aom tot ka exp14
|
|
packet->setArrayDataByName("exp_feature_bytes", 32, 3);//rum cellar
|
|
packet->setArrayDataByName("exp_feature_bytes", 140, 4);
|
|
packet->setArrayDataByName("exp_feature_bytes", 62, 5);
|
|
packet->setArrayDataByName("exp_feature_bytes", 0, 6);
|
|
packet->setArrayDataByName("exp_feature_bytes", 45, 7);
|
|
packet->setArrayDataByName("exp_feature_bytes", 128, 8);
|
|
|
|
packet->setArrayLengthByName("num_unknown3b_bytes", 9);
|
|
packet->setArrayDataByName("unknown3b_bytes", 95, 0);
|
|
packet->setArrayDataByName("unknown3b_bytes", 255, 1);
|
|
packet->setArrayDataByName("unknown3b_bytes", 247, 2);
|
|
packet->setArrayDataByName("unknown3b_bytes", 237, 3);
|
|
packet->setArrayDataByName("unknown3b_bytes", 143, 4);
|
|
packet->setArrayDataByName("unknown3b_bytes", 255, 5);
|
|
packet->setArrayDataByName("unknown3b_bytes", 255, 6);
|
|
packet->setArrayDataByName("unknown3b_bytes", 255, 7);
|
|
packet->setArrayDataByName("unknown3b_bytes", 128, 8);
|
|
}*/
|
|
if (client->GetVersion() >= 64644) {
|
|
packet->setDataByName("unknown3a", 12598924);
|
|
packet->setDataByName("unknown3b", 3992452959);
|
|
packet->setDataByName("unknown3c", 4294967183);
|
|
packet->setDataByName("unknown2a", 9);
|
|
packet->setDataByName("unknown2b", 9);
|
|
}
|
|
else if (client->GetVersion() >= 63181) {
|
|
packet->setDataByName("unknown3a", 750796556);//63182 73821356
|
|
packet->setDataByName("unknown3b", 3991404383);// 63182 3991404383
|
|
packet->setDataByName("unknown3c", 4278189967);// 63182 4278189967
|
|
packet->setDataByName("unknown2a", 8);// 63182
|
|
packet->setDataByName("unknown2b", 8);// 63182
|
|
|
|
}
|
|
else{
|
|
//packet->setDataByName("unknown3", 872447025,0);//63181
|
|
//packet->setDataByName("unknown3", 3085434875,1);// 63181
|
|
//packet->setDataByName("unknown3", 2147483633,2);// 63181
|
|
}
|
|
|
|
packet->setDataByName("year", world.GetWorldTimeStruct()->year);
|
|
packet->setDataByName("month", world.GetWorldTimeStruct()->month);
|
|
packet->setDataByName("day", world.GetWorldTimeStruct()->day);
|
|
packet->setDataByName("hour", world.GetWorldTimeStruct()->hour);
|
|
packet->setDataByName("minute", world.GetWorldTimeStruct()->minute);
|
|
packet->setDataByName("unknown", 0);
|
|
packet->setDataByName("unknown7", 1);
|
|
packet->setDataByName("unknown7", 1, 1);
|
|
|
|
packet->setDataByName("unknown9", 13);
|
|
//packet->setDataByName("unknown10", 25188959);4294967295
|
|
//packet->setDataByName("unknown10", 25190239);
|
|
packet->setDataByName("unknown10", 25191524);//25191524
|
|
packet->setDataByName("unknown10b", 1);
|
|
packet->setDataByName("permission_level",3);// added on 63182 for now till we figur it out 0=none,1=visitor,2=friend,3=trustee,4=owner
|
|
packet->setDataByName("num_adv", 9);
|
|
|
|
packet->setArrayDataByName("adv_name", "adv02_dun_drowned_caverns", 0);
|
|
packet->setArrayDataByName("adv_id", 6, 0);
|
|
packet->setArrayDataByName("adv_name", "adv02_dun_sundered_splitpaw_hub", 1);
|
|
packet->setArrayDataByName("adv_id", 5, 1);
|
|
packet->setArrayDataByName("adv_name", "exp03_rgn_butcherblock", 2);
|
|
packet->setArrayDataByName("adv_id", 8, 2);
|
|
packet->setArrayDataByName("adv_name", "exp03_rgn_greater_faydark", 3);
|
|
packet->setArrayDataByName("adv_id", 7, 3);
|
|
packet->setArrayDataByName("adv_name", "mod01_dun_crypt_of_thaen", 4);
|
|
packet->setArrayDataByName("adv_id", 3, 4);
|
|
packet->setArrayDataByName("adv_name", "mod01_dun_tombs_of_night", 5);
|
|
packet->setArrayDataByName("adv_id", 4, 5);
|
|
packet->setArrayDataByName("adv_name", "nektulos_mini01", 6);
|
|
packet->setArrayDataByName("adv_id", 0, 6);
|
|
packet->setArrayDataByName("adv_name", "nektulos_mini02", 7);
|
|
packet->setArrayDataByName("adv_id", 1, 7);
|
|
packet->setArrayDataByName("adv_name", "nektulos_mini03", 8);
|
|
packet->setArrayDataByName("adv_id", 2, 8);
|
|
|
|
|
|
|
|
|
|
LogWrite(MISC__TODO, 0, "TODO", "Put cl_ client commands back in variables (not Rules) so they can be dynamically maintained");
|
|
vector<Variable*>* variables = world.GetClientVariables();
|
|
packet->setArrayLengthByName("num_client_setup", variables->size());
|
|
for(int i=variables->size()-1;i>=0;i--)
|
|
packet->setArrayDataByName("client_cmds", variables->at(i)->GetNameValuePair().c_str(), i);
|
|
|
|
// For AoM clients so item link work
|
|
if (client->GetVersion() >= 60114)
|
|
packet->setArrayDataByName("client_cmds", "chat_linkbrackets_item 1", variables->size());
|
|
|
|
safe_delete(variables);
|
|
//packet->setDataByName("unknown8", ); story?
|
|
// AA Tabs for 1193+ clients
|
|
if (client->GetVersion() >= 1193) {
|
|
packet->setArrayLengthByName("tab_count", 48);
|
|
int8 i = 0;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":es24a58bd8fcaac8c2:All", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c727bd47a6:Racial Innate", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c75a96e23c:Tradeskill Advancement", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c744f1fd99:Focus Effects", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c71edd2a66:Heroic", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c76ee6239f:Shadows", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7e678b977:Prestige", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c77ee422d7:Animalist", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7f165af77:Bard", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7421b9375:Brawler", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7a03ae7d1:Cleric", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7c9605e9f:Crusader", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7f9424168:Druid", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c79cb9556c:Enchanter", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c70c8b6aa4:Predator", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c73a43b6dd:Rogue", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c759fe7d15:Sorcerer", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7ad610aca:Summoner", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c71e056728:Warrior", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7ba864c0b:Assassin", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7b8116aad:Beastlord", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7f53feb7b:Berserker", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c73d8a70e2:Brigand", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c770c766d6:Bruiser", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c79226984b:Coercer", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c70c58bb30:Conjurer", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c73dfe68d0:Defiler", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c792919a6b:Dirge", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7062e5f55:Fury", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c762c1fdfc:Guardian", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c78addfbf4:Illusionist", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7ece054a7:Inquisitor", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7d550d2e7:Monk", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c743cfeaa2:Mystic", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7f63c9c8c:Necromancer", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c70c5de0ae:Paladin", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c79bc97b3a:Ranger", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c78fbd2256:Shadowknight", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7781cc625:Shaman", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c77eecdcdb:Swashbuckler", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7648d181e:Templar", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c78df47d77:Troubador", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7c78ce0b8:Warden", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c76290dcfa:Warlock", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7d1d52cf5:Wizard", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c71c8f6f4d:Shaper", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c72f6e354d:Channeler", i);
|
|
|
|
i++;
|
|
packet->setArrayDataByName("tab_index", i, i);
|
|
packet->setArrayDataByName("tab_name", ":410385c7df8bd37d:Dragon", i);
|
|
}
|
|
packet->setDataByName("unknown_mj", 1);//int8
|
|
packet->setDataByName("unknown_mj1", 335544320);//int32
|
|
packet->setDataByName("unknown_mj2", 4);//int32
|
|
packet->setDataByName("unknown_mj3", 3962504088);//int32
|
|
packet->setDataByName("unknown_mj4", 3985947216);//int32
|
|
packet->setDataByName("unknown_mj5", 1);//int32
|
|
packet->setDataByName("unknown_mj6", 386);//int32
|
|
packet->setDataByName("unknown_mj7", 4294967295);//int32
|
|
packet->setDataByName("unknown_mj8", 2716312211);//int32
|
|
packet->setDataByName("unknown_mj9", 1774338333);//int32
|
|
packet->setDataByName("unknown_mj10", 1);//int32
|
|
packet->setDataByName("unknown_mj11", 391);//int32
|
|
packet->setDataByName("unknown_mj12", 4294967295);//int32
|
|
packet->setDataByName("unknown_mj13", 3168965163);//int32
|
|
packet->setDataByName("unknown_mj14", 4117025286);//int32
|
|
packet->setDataByName("unknown_mj15", 1);//int32
|
|
packet->setDataByName("unknown_mj16", 394);//int32
|
|
packet->setDataByName("unknown_mj17", 4294967295);//int32
|
|
packet->setDataByName("unknown_mj18", 1790669110);//int32
|
|
packet->setDataByName("unknown_mj19", 107158108);//int32
|
|
packet->setDataByName("unknown_mj20", 1);//int32
|
|
packet->setDataByName("unknown_mj21", 393);//int32
|
|
packet->setDataByName("unknown_mj22", 4294967295);//int32
|
|
|
|
EQ2Packet* outapp = packet->serialize();
|
|
//packet->PrintPacket();
|
|
//DumpPacket(outapp);
|
|
safe_delete(packet);
|
|
return outapp;
|
|
}
|
|
|
|
void ZoneServer::SendUpdateDefaultCommand(Spawn* spawn, const char* command, float distance, Spawn* toPlayer){
|
|
if (spawn == nullptr || command == nullptr)
|
|
return;
|
|
|
|
if (toPlayer)
|
|
{
|
|
if (!toPlayer->IsPlayer())
|
|
return;
|
|
|
|
Client* client = ((Player*)toPlayer)->GetClient();
|
|
if (client)
|
|
{
|
|
client->SendDefaultCommand(spawn, command, distance);
|
|
}
|
|
// we don't override the primary command cause that would change ALL clients
|
|
return;
|
|
}
|
|
|
|
QueueDefaultCommand(spawn->GetID(), std::string(command), distance);
|
|
|
|
if (strlen(command)>0)
|
|
spawn->SetPrimaryCommand(command, command, distance);
|
|
}
|
|
|
|
void ZoneServer::CheckPlayerProximity(Spawn* spawn, Client* client){
|
|
if (player_proximities.size() < 1)
|
|
return;
|
|
|
|
if(player_proximities.count(spawn->GetID()) > 0){
|
|
PlayerProximity* prox = player_proximities.Get(spawn->GetID());
|
|
if(prox->clients_in_proximity.count(client) == 0 && spawn_range_map.count(client) > 0 && spawn_range_map.Get(client)->count(spawn->GetID()) > 0 && spawn_range_map.Get(client)->Get(spawn->GetID()) < prox->distance){
|
|
prox->clients_in_proximity[client] = true;
|
|
CallSpawnScript(spawn, SPAWN_SCRIPT_CUSTOM, client->GetPlayer(), prox->in_range_lua_function.c_str());
|
|
}
|
|
else if(prox->clients_in_proximity.count(client) > 0 && spawn_range_map.count(client) > 0 && spawn_range_map.Get(client)->count(spawn->GetID()) > 0 && spawn_range_map.Get(client)->Get(spawn->GetID()) > prox->distance){
|
|
if(prox->leaving_range_lua_function.length() > 0)
|
|
CallSpawnScript(spawn, SPAWN_SCRIPT_CUSTOM, client->GetPlayer(), prox->leaving_range_lua_function.c_str());
|
|
prox->clients_in_proximity.erase(client);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::AddPlayerProximity(Spawn* spawn, float distance, string in_range_function, string leaving_range_function){
|
|
RemovePlayerProximity(spawn);
|
|
PlayerProximity* prox = new PlayerProximity;
|
|
prox->distance = distance;
|
|
prox->in_range_lua_function = in_range_function;
|
|
prox->leaving_range_lua_function = leaving_range_function;
|
|
player_proximities.Put(spawn->GetID(), prox);
|
|
}
|
|
|
|
void ZoneServer::RemovePlayerProximity(Client* client){
|
|
PlayerProximity* prox = 0;
|
|
MutexMap<int32, PlayerProximity*>::iterator itr = player_proximities.begin();
|
|
while(itr.Next()){
|
|
prox = itr->second;
|
|
if(prox->clients_in_proximity.count(client) > 0)
|
|
prox->clients_in_proximity.erase(client);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::RemovePlayerProximity(Spawn* spawn, bool all){
|
|
if(all){
|
|
MutexMap<int32, PlayerProximity*>::iterator itr = player_proximities.begin();
|
|
while(itr.Next()){
|
|
player_proximities.erase(itr->first, false, true, 10000);
|
|
}
|
|
}
|
|
else if(player_proximities.count(spawn->GetID()) > 0){
|
|
player_proximities.erase(spawn->GetID(), false, true, 10000);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::AddLocationProximity(float x, float y, float z, float max_variation, string in_range_function, string leaving_range_function) {
|
|
LocationProximity* prox = new LocationProximity;
|
|
prox->x = x;
|
|
prox->y = y;
|
|
prox->z = z;
|
|
prox->max_variation = max_variation;
|
|
prox->in_range_lua_function = in_range_function;
|
|
prox->leaving_range_lua_function = leaving_range_function;
|
|
location_proximities.Add(prox);
|
|
}
|
|
|
|
void ZoneServer::CheckLocationProximity() {
|
|
const char* zone_script = world.GetZoneScript(this->GetZoneID());
|
|
if (!zone_script)
|
|
return;
|
|
|
|
if (location_proximities.size() > 0 && connected_clients.size() > 0) {
|
|
Client* client = 0;
|
|
MutexList<Client*>::iterator iterator = connected_clients.begin();
|
|
while(iterator.Next()){
|
|
client = iterator->value;
|
|
if (client->IsConnected() && client->IsReadyForUpdates() && !client->IsZoning()) {
|
|
try {
|
|
MutexList<LocationProximity*>::iterator itr = location_proximities.begin();
|
|
LocationProximity* prox = 0;
|
|
while(itr.Next()){
|
|
prox = itr->value;
|
|
bool in_range = false;
|
|
float char_x = client->GetPlayer()->GetX();
|
|
float char_y = client->GetPlayer()->GetY();
|
|
float char_z = client->GetPlayer()->GetZ();
|
|
float x = prox->x;
|
|
float y = prox->y;
|
|
float z = prox->z;
|
|
float max_variation = prox->max_variation;
|
|
float total_diff = 0;
|
|
float diff = x - char_x; //Check X
|
|
if(diff < 0)
|
|
diff *= -1;
|
|
if(diff <= max_variation) {
|
|
total_diff += diff;
|
|
diff = z - char_z; //Check Z (we check Z first because it is far more likely to be a much greater variation than y)
|
|
if(diff < 0)
|
|
diff *= -1;
|
|
if(diff <= max_variation) {
|
|
total_diff += diff;
|
|
if(total_diff <= max_variation) { //Check Total
|
|
diff = y - char_y; //Check Y
|
|
if(diff < 0)
|
|
diff *= -1;
|
|
if(diff <= max_variation) {
|
|
total_diff += diff;
|
|
if(total_diff <= max_variation) {
|
|
in_range = true;
|
|
if(lua_interface && prox->in_range_lua_function.length() > 0 && prox->clients_in_proximity.count(client) == 0) { //Check Total
|
|
prox->clients_in_proximity[client] = true;
|
|
lua_interface->RunZoneScript(zone_script, prox->in_range_lua_function.c_str(), this, client->GetPlayer());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!in_range) {
|
|
if(lua_interface && prox->leaving_range_lua_function.length() > 0 && prox->clients_in_proximity.count(client) > 0) {
|
|
lua_interface->RunZoneScript(zone_script, prox->leaving_range_lua_function.c_str(), this, client->GetPlayer());
|
|
prox->clients_in_proximity.erase(client);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (...) {
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "Except caught in ZoneServer::CheckLocationProximity");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::CheckLocationGrids() {
|
|
if (connected_clients.size() > 0 && location_grids.size() > 0) {
|
|
MutexList<Client*>::iterator client_itr = connected_clients.begin();
|
|
while (client_itr.Next()) {
|
|
Client* client = client_itr.value;
|
|
if (!client)
|
|
continue;
|
|
Player* player = client->GetPlayer();
|
|
float x = player->GetX();
|
|
float y = player->GetY();
|
|
float z = player->GetZ();
|
|
int32 grid_id = player->GetLocation();
|
|
MutexList<LocationGrid*>::iterator location_grid_itr = location_grids.begin();
|
|
while (location_grid_itr.Next()) {
|
|
LocationGrid* grid = location_grid_itr.value;
|
|
bool playerInGrid = false;
|
|
if (grid->locations.size() > 0 || (playerInGrid = (grid->grid_id > 0 && grid->grid_id == grid_id)) || grid->players.count(player) > 0) {
|
|
float x_small = 0;
|
|
float x_large = 0;
|
|
float y_small = 0;
|
|
float y_large = 0;
|
|
float z_small = 0;
|
|
float z_large = 0;
|
|
bool first = true;
|
|
bool in_grid = false;
|
|
if(grid->locations.size() == 0 && playerInGrid) { // no locations, we presume player is in grid
|
|
in_grid = true;
|
|
}
|
|
else {
|
|
MutexList<Location*>::iterator location_itr = grid->locations.begin();
|
|
while (location_itr.Next()) {
|
|
Location* location = location_itr.value;
|
|
if (first) {
|
|
x_small = location->x;
|
|
x_large = location->x;
|
|
if (grid->include_y) {
|
|
y_small = location->y;
|
|
y_large = location->y;
|
|
}
|
|
z_small = location->z;
|
|
z_large = location->z;
|
|
first = false;
|
|
}
|
|
else {
|
|
if (location->x < x_small)
|
|
x_small = location->x;
|
|
else if (location->x > x_large)
|
|
x_large = location->x;
|
|
if (grid->include_y) {
|
|
if (location->y < y_small)
|
|
y_small = location->y;
|
|
else if (location->y > y_large)
|
|
y_large = location->y;
|
|
}
|
|
if (location->z < z_small)
|
|
z_small = location->z;
|
|
else if (location->z > z_large)
|
|
z_large = location->z;
|
|
}
|
|
}
|
|
if (grid->include_y && (x >= x_small && x <= x_large && y >= y_small && y <= y_large && z >= z_small && z <= z_large))
|
|
in_grid = true;
|
|
else if (x >= x_small && x <= x_large && z >= z_small && z <= z_large)
|
|
in_grid = true;
|
|
}
|
|
if (in_grid && grid->players.count(player) == 0) {
|
|
grid->players.Put(player, true);
|
|
|
|
bool show_enter_location_popup = true;
|
|
bool discovery_enabled = rule_manager.GetZoneRule(GetZoneID(), R_World, EnablePOIDiscovery)->GetBool();
|
|
|
|
if( grid->discovery && discovery_enabled && !player->DiscoveredLocation(grid->id) )
|
|
{
|
|
// check if player has already discovered this location
|
|
|
|
// if not, process new discovery
|
|
char tmp[200] = {0};
|
|
sprintf(tmp, "\\#FFE400You have discovered\12\\#FFF283%s", grid->name.c_str());
|
|
client->SendPopupMessage(11, tmp, "ui_discovery", 2.25, 0xFF, 0xFF, 0xFF);
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Player '%s' discovered location '%s' (%u)", player->GetName(), grid->name.c_str(), grid->id);
|
|
|
|
player->UpdatePlayerHistory(HISTORY_TYPE_DISCOVERY, HISTORY_SUBTYPE_LOCATION, grid->id);
|
|
show_enter_location_popup = false;
|
|
|
|
// else, print standard location entry
|
|
}
|
|
|
|
if( show_enter_location_popup )
|
|
{
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Player '%s' entering location '%s' (%u)", player->GetName(), grid->name.c_str(), grid->id);
|
|
client->SendPopupMessage(10, grid->name.c_str(), 0, 2.5, 255, 255, 0);
|
|
}
|
|
}
|
|
else if (!in_grid && grid->players.count(player) > 0) {
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "Player '%s' leaving location '%s' (%u)", player->GetName(), grid->name.c_str(), grid->id);
|
|
grid->players.erase(player);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called from a command (client, main zone thread) and the main zone thread
|
|
// so no need for a mutex container
|
|
void ZoneServer::AddLocationGrid(LocationGrid* grid) {
|
|
if (grid)
|
|
location_grids.Add(grid);
|
|
}
|
|
|
|
void ZoneServer::RemoveLocationGrids() {
|
|
MutexList<LocationGrid*>::iterator itr = location_grids.begin();
|
|
while (itr.Next())
|
|
itr.value->locations.clear(true);
|
|
location_grids.clear(true);
|
|
}
|
|
|
|
void ZoneServer::RemoveSpellTimersFromSpawn(Spawn* spawn, bool remove_all, bool delete_recast, bool call_expire_function, bool lock_spell_process){
|
|
if(spellProcess)
|
|
spellProcess->RemoveSpellTimersFromSpawn(spawn, remove_all, delete_recast, call_expire_function, lock_spell_process);
|
|
}
|
|
|
|
void ZoneServer::Interrupted(Entity* caster, Spawn* interruptor, int16 error_code, bool cancel, bool from_movement){
|
|
if(spellProcess)
|
|
spellProcess->Interrupted(caster, interruptor, error_code, cancel, from_movement);
|
|
}
|
|
|
|
Spell* ZoneServer::GetSpell(Entity* caster){
|
|
Spell* spell = 0;
|
|
if(spellProcess)
|
|
spell = spellProcess->GetSpell(caster);
|
|
return spell;
|
|
}
|
|
|
|
void ZoneServer::ProcessSpell(Spell* spell, Entity* caster, Spawn* target, bool lock, bool harvest_spell, LuaSpell* customSpell, int16 custom_cast_time, bool in_heroic_opp){
|
|
if(spellProcess)
|
|
spellProcess->ProcessSpell(this, spell, caster, target, lock, harvest_spell, customSpell, custom_cast_time, in_heroic_opp);
|
|
}
|
|
|
|
void ZoneServer::ProcessEntityCommand(EntityCommand* entity_command, Entity* caster, Spawn* target, bool lock) {
|
|
if (target && target->GetSpawnScript()) {
|
|
Player* player = 0;
|
|
if (caster && caster->IsPlayer())
|
|
player = (Player*)caster;
|
|
CallSpawnScript(target, SPAWN_SCRIPT_CUSTOM, player, entity_command->command.c_str());
|
|
}
|
|
if (spellProcess)
|
|
spellProcess->ProcessEntityCommand(this, entity_command, caster, target, lock);
|
|
}
|
|
|
|
void ZoneServer::RemoveSpawnSupportFunctions(Spawn* spawn, bool lock_spell_process, bool shutdown) {
|
|
if(!spawn)
|
|
return;
|
|
|
|
// remove entity* in trade class
|
|
if(spawn->IsEntity()) {
|
|
((Entity*)spawn)->TerminateTrade();
|
|
}
|
|
|
|
if(spawn->IsPlayer() && spawn->GetZone()) {
|
|
spawn->GetZone()->RemovePlayerPassenger(((Player*)spawn)->GetCharacterID());
|
|
if(((Player*)spawn)->GetClient()) {
|
|
GetTradeskillMgr()->StopCrafting(((Player*)spawn)->GetClient());
|
|
}
|
|
}
|
|
if(spawn->IsEntity())
|
|
RemoveSpellTimersFromSpawn((Entity*)spawn, true, true, true, lock_spell_process);
|
|
|
|
if(!shutdown) { // in case of shutdown, DeleteData(true) handles the cleanup later via DeleteSpawnScriptTimers
|
|
StopSpawnScriptTimer(spawn, "");
|
|
}
|
|
|
|
if(spawn->IsEntity()) {
|
|
ClearHate((Entity*)spawn);
|
|
}
|
|
|
|
RemoveDamagedSpawn(spawn);
|
|
spawn->SendSpawnChanges(false);
|
|
RemoveChangedSpawn(spawn);
|
|
|
|
// Everything inside this if will be nuked during a reload in other spots, no need to do it twice
|
|
if (!reloading) {
|
|
RemoveDeadEnemyList(spawn);
|
|
|
|
spawn->changed = true;
|
|
spawn->info_changed = true;
|
|
spawn->vis_changed = true;
|
|
spawn->position_changed = true;
|
|
SendSpawnChanges(spawn);
|
|
|
|
if (spawn->GetSpawnGroupID() > 0) {
|
|
int32 group_id = spawn->GetSpawnGroupID();
|
|
spawn->RemoveSpawnFromGroup(false, (spawn->IsEntity() && !spawn->Alive()) ? true : false);
|
|
if (spawn_group_map.count(group_id) > 0)
|
|
spawn_group_map.Get(group_id).Remove(spawn->GetID());
|
|
}
|
|
|
|
if (!spawn->IsPlayer()) {
|
|
if(quick_database_id_lookup.count(spawn->GetDatabaseID()) > 0)
|
|
quick_database_id_lookup.erase(spawn->GetDatabaseID());
|
|
|
|
if(spawn->GetSpawnLocationID() > 0 && quick_location_id_lookup.count(spawn->GetSpawnLocationID()) > 0 && quick_location_id_lookup.Get(spawn->GetSpawnLocationID()) == spawn->GetID())
|
|
quick_location_id_lookup.erase(spawn->GetSpawnLocationID());
|
|
|
|
if(spawn->GetSpawnGroupID() > 0 && quick_group_id_lookup.count(spawn->GetSpawnGroupID()) > 0 && quick_group_id_lookup.Get(spawn->GetSpawnGroupID()) == spawn->GetID())
|
|
quick_group_id_lookup.erase(spawn->GetSpawnGroupID());
|
|
}
|
|
|
|
DeleteSpawnScriptTimers(spawn);
|
|
RemovePlayerProximity(spawn);
|
|
}
|
|
|
|
// We don't use RemoveMovementNPC() here as it caused a hell of a delay during reloads
|
|
// instead we remove it from the list directly
|
|
if (spawn->IsNPC())
|
|
movement_spawns.erase(spawn->GetID());
|
|
}
|
|
|
|
void ZoneServer::HandleEmote(Spawn* originator, string name, Spawn* opt_target, bool no_target) {
|
|
if (!originator) {
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "HandleEmote called with an invalid client");
|
|
return;
|
|
}
|
|
|
|
Spawn* target = originator->GetTarget();
|
|
if(opt_target)
|
|
target = opt_target;
|
|
|
|
if(no_target) // override having a target
|
|
target = nullptr;
|
|
|
|
Client* orig_client = (originator->IsPlayer() && ((Player*)originator)->GetClient()) ? ((Player*)originator)->GetClient() : nullptr;
|
|
Client* client = 0;
|
|
int32 cur_client_version = orig_client ? orig_client->GetVersion() : 546;
|
|
Emote* origEmote = visual_states.FindEmote(name, cur_client_version);
|
|
if(!origEmote){
|
|
if(orig_client) {
|
|
orig_client->Message(CHANNEL_COLOR_YELLOW, "Unable to find emote '%s'. If this should be a valid emote be sure to submit a /bug report.", name.c_str());
|
|
}
|
|
return;
|
|
}
|
|
Emote* emote = origEmote;
|
|
|
|
PacketStruct* packet = 0;
|
|
char* emoteResponse = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
map<int32, Emote*> emote_version_range;
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(!client || (client && originator->IsPlayer() && client->GetPlayer()->IsIgnored(originator->GetName())))
|
|
continue;
|
|
|
|
// establish appropriate emote for the version used by the client
|
|
if (client->GetVersion() != cur_client_version)
|
|
{
|
|
map<int32, Emote*>::iterator rangeitr = emote_version_range.find(client->GetVersion());
|
|
if (rangeitr == emote_version_range.end())
|
|
{
|
|
Emote* tmp_new_emote = visual_states.FindEmote(name, client->GetVersion());
|
|
if (tmp_new_emote)
|
|
{
|
|
emote_version_range.insert(make_pair(client->GetVersion(), tmp_new_emote));
|
|
emote = tmp_new_emote;
|
|
} // else its missing just use the current clients default
|
|
}
|
|
else // we have an existing emote already cached
|
|
emote = rangeitr->second;
|
|
}
|
|
else // since the client and originator client match use the original emote
|
|
emote = origEmote;
|
|
|
|
packet = configReader.getStruct("WS_CannedEmote", client->GetVersion());
|
|
if(packet){
|
|
packet->setDataByName("spawn_id" , client->GetPlayer()->GetIDWithPlayerSpawn(originator));
|
|
if(!emoteResponse){
|
|
string message;
|
|
if(target && target->GetID() != originator->GetID()){
|
|
message = emote->GetTargetedMessageString();
|
|
if(message.find("%t") < 0xFFFFFFFF)
|
|
message.replace(message.find("%t"), 2, target->GetName());
|
|
}
|
|
if(message.length() == 0)
|
|
message = emote->GetMessageString();
|
|
if(message.find("%g1") < 0xFFFFFFFF){
|
|
if(originator->GetGender() == 1)
|
|
message.replace(message.find("%g1"), 3, "his");
|
|
else
|
|
message.replace(message.find("%g1"), 3, "her");
|
|
}
|
|
if(message.find("%g2") < 0xFFFFFFFF){
|
|
if(originator->GetGender() == 1)
|
|
message.replace(message.find("%g2"), 3, "him");
|
|
else
|
|
message.replace(message.find("%g2"), 3, "her");
|
|
}
|
|
if(message.find("%g3") < 0xFFFFFFFF){
|
|
if(originator->GetGender() == 1)
|
|
message.replace(message.find("%g3"), 3, "he");
|
|
else
|
|
message.replace(message.find("%g3"), 3, "she");
|
|
}
|
|
if(message.length() > 0){
|
|
emoteResponse = new char[message.length() + strlen(originator->GetName()) + 10];
|
|
sprintf(emoteResponse,"%s %s", originator->GetName(), message.c_str());
|
|
}
|
|
}
|
|
if(originator->IsPlayer()) {
|
|
packet->setMediumStringByName("emote_msg", emoteResponse ? emoteResponse : "");
|
|
}
|
|
packet->setDataByName("anim_type", emote->GetVisualState());
|
|
client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
safe_delete_array(emoteResponse);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
|
|
void ZoneServer::SetupInstance(int32 createdInstanceID) {
|
|
if ( createdInstanceID == 0 ) // if this happens that isn't good!
|
|
instanceID = ++MinInstanceID;
|
|
else // db should pass the good ID
|
|
instanceID = createdInstanceID;
|
|
}
|
|
|
|
void ZoneServer::RemoveDeadSpawn(Spawn* spawn){
|
|
AddDeadSpawn(spawn, 0);
|
|
}
|
|
|
|
void ZoneServer::AddDeadSpawn(Spawn* spawn, int32 timer){
|
|
MDeadSpawns.writelock(__FUNCTION__, __LINE__);
|
|
if (dead_spawns.count(spawn->GetID()) > 0)
|
|
dead_spawns[spawn->GetID()] = Timer::GetCurrentTime2() + timer;
|
|
else if(timer != 0xFFFFFFFF)
|
|
dead_spawns.insert(make_pair(spawn->GetID(), Timer::GetCurrentTime2() + timer));
|
|
else{
|
|
if(spawn->IsEntity() && spawn->HasLoot()){
|
|
dead_spawns.insert(make_pair(spawn->GetID(), Timer::GetCurrentTime2() + (15000 * spawn->GetLevel() + 240000)));
|
|
SendUpdateDefaultCommand(spawn, "loot", 10);
|
|
}
|
|
else
|
|
dead_spawns.insert(make_pair(spawn->GetID(), Timer::GetCurrentTime2() + 10000));
|
|
}
|
|
MDeadSpawns.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::WritePlayerStatistics() {
|
|
MutexList<Client*>::iterator client_itr = connected_clients.begin();
|
|
while(client_itr.Next())
|
|
client_itr->value->GetPlayer()->WritePlayerStatistics();
|
|
}
|
|
|
|
bool ZoneServer::SendRadiusSpawnInfo(Client* client, float radius) {
|
|
if (!client)
|
|
return false;
|
|
|
|
Spawn* spawn = 0;
|
|
bool ret = false;
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if (spawn && spawn != client->GetPlayer() && !spawn->IsPlayer() && spawn->GetDistance(client->GetPlayer()) <= radius) {
|
|
const char* type = "NPC";
|
|
const char* specialTypeID = "N/A";
|
|
int32 specialID = 0, spawnEntryID = spawn->GetSpawnEntryID();
|
|
if (spawn->IsObject())
|
|
{
|
|
Object* obj = (Object*)spawn;
|
|
specialID = obj->GetID();
|
|
specialTypeID = "GetID";
|
|
type = "Object";
|
|
}
|
|
else if (spawn->IsSign())
|
|
{
|
|
Sign* sign = (Sign*)spawn;
|
|
specialID = sign->GetWidgetID();
|
|
specialTypeID = "WidgetID";
|
|
type = "Sign";
|
|
}
|
|
else if (spawn->IsWidget())
|
|
{
|
|
Widget* widget = (Widget*)spawn;
|
|
specialID = widget->GetWidgetID();
|
|
specialTypeID = "WidgetID";
|
|
if ( specialID == 0xFFFFFFFF )
|
|
specialTypeID = "WidgetID(spawn_widgets entry missing)";
|
|
|
|
type = "Widget";
|
|
}
|
|
else if (spawn->IsGroundSpawn())
|
|
{
|
|
GroundSpawn* gs = (GroundSpawn*)spawn;
|
|
specialID = gs->GetGroundSpawnEntryID();
|
|
specialTypeID = "GroundSpawnEntryID";
|
|
type = "GroundSpawn";
|
|
}
|
|
client->Message(CHANNEL_COLOR_RED, "Name: %s (%s), Spawn Table ID: %u, %s: %u", spawn->GetName(), type, spawn->GetDatabaseID(), specialTypeID, specialID);
|
|
client->Message(CHANNEL_COLOR_RED, "Spawn Location ID: %u, Spawn Group ID: %u, SpawnEntryID: %u, Grid ID: %u", spawn->GetSpawnLocationID(), spawn->GetSpawnGroupID(), spawnEntryID, spawn->GetLocation());
|
|
client->Message(CHANNEL_COLOR_RED, "Respawn Time: %u (sec), X: %f, Y: %f, Z: %f Heading: %f", spawn->GetRespawnTime(), spawn->GetX(), spawn->GetY(), spawn->GetZ(), spawn->GetHeading());
|
|
client->Message(CHANNEL_COLOR_YELLOW, "=============================");
|
|
ret = true;
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::FindSpawn(Client* client, char* regSearchStr)
|
|
{
|
|
if (!regSearchStr || strlen(regSearchStr) < 1)
|
|
{
|
|
client->SimpleMessage(CHANNEL_COLOR_RED, "Bad ZoneServer::FindSpawn(Client*, char* regSearchStr) attempt, regSearchStr is NULL or empty.");
|
|
return;
|
|
}
|
|
|
|
string resString = string(regSearchStr);
|
|
try
|
|
{
|
|
std::regex pre_re_check("^[a-zA-Z0-9_ ]+$");
|
|
bool output = std::regex_match(resString, pre_re_check);
|
|
if (output)
|
|
{
|
|
string newStr(".*");
|
|
newStr.append(regSearchStr);
|
|
newStr.append(".*");
|
|
resString = newStr;
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
client->SimpleMessage(CHANNEL_COLOR_RED, "Try/Catch ZoneServer::FindSpawn(Client*, char* regSearchStr) failure.");
|
|
return;
|
|
}
|
|
std::regex re;
|
|
try {
|
|
re = std::regex(resString, std::regex_constants::icase);
|
|
}
|
|
catch(...) {
|
|
client->SimpleMessage(CHANNEL_COLOR_RED, "Invalid regex for FindSpawn.");
|
|
return;
|
|
}
|
|
|
|
client->Message(CHANNEL_NARRATIVE, "RegEx Search Spawn List: %s", regSearchStr);
|
|
client->Message(CHANNEL_NARRATIVE, "Database ID | Spawn Name | X , Y , Z");
|
|
client->Message(CHANNEL_NARRATIVE, "========================");
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
int32 spawnsFound = 0;
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
Spawn* spawn = itr->second;
|
|
if (!spawn || !spawn->GetName())
|
|
continue;
|
|
bool output = false;
|
|
try {
|
|
output = std::regex_match(string(spawn->GetName()), re);
|
|
}
|
|
catch (...)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (output)
|
|
{
|
|
client->Message(CHANNEL_NARRATIVE, "%i | %s | %f , %f , %f", spawn->GetDatabaseID(), spawn->GetName(), spawn->GetX(), spawn->GetY(), spawn->GetZ());
|
|
spawnsFound++;
|
|
}
|
|
}
|
|
client->Message(CHANNEL_NARRATIVE, "========================", spawnsFound);
|
|
client->Message(CHANNEL_NARRATIVE, "%u Results Found.", spawnsFound);
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
|
|
void ZoneServer::AddPlayerTracking(Player* player) {
|
|
if (player && !player->GetIsTracking() && players_tracking.count(player->GetDatabaseID()) == 0) {
|
|
Client* client = ((Player*)player)->GetClient();
|
|
if (client) {
|
|
PacketStruct* packet = configReader.getStruct("WS_TrackingUpdate", client->GetVersion());
|
|
if (packet) {
|
|
player->SetIsTracking(true);
|
|
players_tracking.Put(client->GetCharacterID(), player);
|
|
packet->setDataByName("mode", TRACKING_START);
|
|
packet->setDataByName("type", TRACKING_TYPE_ENTITIES);
|
|
client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::RemovePlayerTracking(Player* player, int8 mode) {
|
|
if (player && player->GetIsTracking()) {
|
|
Client* client = ((Player*)player)->GetClient();
|
|
if (client) {
|
|
PacketStruct* packet = configReader.getStruct("WS_TrackingUpdate", client->GetVersion());
|
|
if (packet) {
|
|
player->SetIsTracking(false);
|
|
players_tracking.erase(client->GetCharacterID());
|
|
packet->setDataByName("mode", mode);
|
|
packet->setDataByName("type", TRACKING_TYPE_ENTITIES);
|
|
client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::ProcessTracking() {
|
|
MutexMap<int32, Player*>::iterator itr = players_tracking.begin();
|
|
while (itr.Next()) {
|
|
Player* player = itr->second;
|
|
if(player->GetClient()) {
|
|
ProcessTracking(player->GetClient());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::ProcessTracking(Client* client) {
|
|
if (!client)
|
|
return;
|
|
|
|
Player* player = client->GetPlayer();
|
|
if (player && player->GetIsTracking()) {
|
|
MutexMap<int32, Spawn*>::iterator spawn_itr;
|
|
PacketStruct* packet = configReader.getStruct("WS_TrackingUpdate", client->GetVersion());
|
|
if (packet) {
|
|
packet->setDataByName("mode", TRACKING_UPDATE);
|
|
packet->setDataByName("type", TRACKING_TYPE_ENTITIES);
|
|
vector<TrackedSpawn*> spawns_tracked;
|
|
while (spawn_itr.Next()) {
|
|
Spawn* spawn = spawn_itr->second;
|
|
float distance = player->GetDistance(spawn);
|
|
if (spawn->IsEntity() && distance <= 80 && spawn != player) {
|
|
TrackedSpawn* ts = new TrackedSpawn;
|
|
ts->spawn = spawn;
|
|
ts->distance = distance;
|
|
|
|
/* Add spawns in ascending order from closest to furthest */
|
|
if (spawns_tracked.empty())
|
|
spawns_tracked.push_back(ts);
|
|
else {
|
|
vector<TrackedSpawn*>::iterator tracked_itr;
|
|
bool added = false;
|
|
for (tracked_itr = spawns_tracked.begin(); tracked_itr != spawns_tracked.end(); tracked_itr++) {
|
|
TrackedSpawn* cur_ts = *tracked_itr;
|
|
if (ts->distance <= cur_ts->distance) {
|
|
spawns_tracked.insert(tracked_itr, ts);
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!added)
|
|
spawns_tracked.push_back(ts);
|
|
}
|
|
}
|
|
}
|
|
packet->setArrayLengthByName("num_spawns", spawns_tracked.size());
|
|
for (int32 i = 0; i < spawns_tracked.size(); i++) {
|
|
TrackedSpawn* ts = spawns_tracked[i];
|
|
|
|
LogWrite(ZONE__DEBUG, 0, "Zone", "%s (%f)", ts->spawn->GetName(), ts->distance);
|
|
|
|
packet->setArrayDataByName("spawn_id", player->GetIDWithPlayerSpawn(ts->spawn), i);
|
|
packet->setArrayDataByName("spawn_name", ts->spawn->GetName(), i);
|
|
if (ts->spawn->IsPlayer())
|
|
packet->setArrayDataByName("spawn_type", TRACKING_SPAWN_TYPE_PC, i);
|
|
else
|
|
packet->setArrayDataByName("spawn_type", TRACKING_SPAWN_TYPE_NPC, i);
|
|
packet->setArrayDataByName("spawn_con_color", player->GetArrowColor(ts->spawn->GetLevel()), i);
|
|
}
|
|
packet->setArrayLengthByName("num_array1", 0);
|
|
//for (int32 i = 0; i < spawns_tracked.size(); i++) {
|
|
//}
|
|
packet->setArrayLengthByName("num_spawns2", spawns_tracked.size());
|
|
for (int32 i = 0; i < spawns_tracked.size(); i++) {
|
|
TrackedSpawn* ts = spawns_tracked[i];
|
|
packet->setArrayDataByName("list_spawn_id", player->GetIDWithPlayerSpawn(ts->spawn), i);
|
|
packet->setArrayDataByName("list_number", i, i);
|
|
}
|
|
client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
for (int32 i = 0; i < spawns_tracked.size(); i++)
|
|
safe_delete(spawns_tracked[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SendEpicMobDeathToGuild(Player* killer, Spawn* victim) {
|
|
if (killer && victim) {
|
|
|
|
LogWrite(MISC__TODO, 1, "TODO" , "Check if player is in raid\n\t(%s, function: %s, line #: %i)", __FILE__, __FUNCTION__, __LINE__);
|
|
|
|
if (killer->GetGroupMemberInfo()) {
|
|
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
|
|
|
|
deque<GroupMemberInfo*>::iterator itr;
|
|
|
|
|
|
PlayerGroup* group = world.GetGroupManager()->GetGroup(killer->GetGroupMemberInfo()->group_id);
|
|
if (group)
|
|
{
|
|
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
|
|
deque<GroupMemberInfo*>* members = group->GetMembers();
|
|
for (itr = members->begin(); itr != members->end(); itr++) {
|
|
GroupMemberInfo* gmi = *itr;
|
|
if (gmi->client) {
|
|
Player* group_member = gmi->client->GetPlayer();
|
|
if (group_member && group_member->GetGuild()) {
|
|
Guild* guild = group_member->GetGuild();
|
|
string message = Guild::GetEpicMobDeathMessage(group_member->GetName(), victim->GetName());
|
|
guild->AddNewGuildEvent(GUILD_EVENT_KILLS_EPIC_MONSTER, message.c_str(), Timer::GetUnixTimeStamp());
|
|
guild->SendMessageToGuild(GUILD_EVENT_KILLS_EPIC_MONSTER, message.c_str());
|
|
}
|
|
}
|
|
}
|
|
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
|
|
}
|
|
else if (killer->GetGuild()) {
|
|
Guild* guild = killer->GetGuild();
|
|
string message = Guild::GetEpicMobDeathMessage(killer->GetName(), victim->GetName());
|
|
guild->AddNewGuildEvent(GUILD_EVENT_KILLS_EPIC_MONSTER, message.c_str(), Timer::GetUnixTimeStamp());
|
|
guild->SendMessageToGuild(GUILD_EVENT_KILLS_EPIC_MONSTER, message.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::ProcessAggroChecks(Spawn* spawn) {
|
|
if (spawn->GetFactionID() < 1 || spawn->EngagedInCombat())
|
|
return;
|
|
// If faction based combat is not allowed then no need to run the loops so just return out
|
|
if(!rule_manager.GetZoneRule(GetZoneID(), R_Faction, AllowFactionBasedCombat)->GetBool())
|
|
return;
|
|
|
|
if (spawn && spawn->IsNPC() && spawn->Alive())
|
|
CheckEnemyList((NPC*)spawn);
|
|
}
|
|
|
|
void ZoneServer::SendUpdateTitles(Client* client, Title* suffix, Title* prefix) {
|
|
assert(client);
|
|
if (client->GetVersion() > 561)
|
|
SendUpdateTitles(client->GetPlayer(), suffix, prefix);
|
|
}
|
|
|
|
void ZoneServer::SendUpdateTitles(Spawn *spawn, Title *suffix, Title *prefix) {
|
|
if (!spawn)
|
|
return;
|
|
|
|
vector<Client*>::iterator itr;
|
|
PacketStruct *packet;
|
|
Client* current_client;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
current_client = *itr;
|
|
|
|
if (current_client->GetVersion() <= 561)
|
|
continue;
|
|
|
|
if (!(packet = configReader.getStruct("WS_UpdateTitle", current_client->GetVersion())))
|
|
continue;
|
|
|
|
packet->setDataByName("player_id", current_client->GetPlayer()->GetIDWithPlayerSpawn(spawn));
|
|
packet->setDataByName("player_name", spawn->GetName());
|
|
packet->setDataByName("unknown1", 1, 1);
|
|
if(suffix)
|
|
packet->setDataByName("suffix_title", suffix->GetName());
|
|
else
|
|
packet->setDataByName("suffix_title", spawn->GetSuffixTitle());
|
|
if(prefix)
|
|
packet->setDataByName("prefix_title", prefix->GetName());
|
|
else
|
|
packet->setDataByName("prefix_title", spawn->GetPrefixTitle());
|
|
packet->setDataByName("last_name", spawn->GetLastName());
|
|
packet->setDataByName("sub_title", spawn->GetSubTitle());
|
|
current_client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::AddTransportSpawn(Spawn* spawn){
|
|
if(!spawn)
|
|
return;
|
|
MTransportSpawns.writelock(__FUNCTION__, __LINE__);
|
|
transport_spawns.push_back(spawn->GetID());
|
|
spawn->SetTransportSpawn(true);
|
|
MTransportSpawns.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
Spawn* ZoneServer::GetClosestTransportSpawn(float x, float y, float z){
|
|
Spawn* spawn = 0;
|
|
Spawn* closest_spawn = 0;
|
|
float closest_distance = 0.0;
|
|
MTransportSpawns.writelock(__FUNCTION__, __LINE__);
|
|
vector<int32>::iterator itr = transport_spawns.begin();
|
|
while(itr != transport_spawns.end()){
|
|
spawn = GetSpawnByID(*itr);
|
|
if(spawn){
|
|
if(closest_distance == 0.0){
|
|
closest_spawn = spawn;
|
|
closest_distance = spawn->GetDistance(x, y, z);
|
|
}
|
|
else if(spawn->GetDistance(x, y, z) < closest_distance){
|
|
closest_spawn = spawn;
|
|
closest_distance = spawn->GetDistance(x, y, z);
|
|
}
|
|
itr++;
|
|
}
|
|
else
|
|
itr = transport_spawns.erase(itr);
|
|
}
|
|
MTransportSpawns.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
return closest_spawn;
|
|
}
|
|
|
|
Spawn* ZoneServer::GetTransportByRailID(sint64 rail_id){
|
|
Spawn* spawn = 0;
|
|
Spawn* closest_spawn = 0;
|
|
MTransportSpawns.readlock(__FUNCTION__, __LINE__);
|
|
vector<int32>::iterator itr = transport_spawns.begin();
|
|
while(itr != transport_spawns.end()){
|
|
spawn = GetSpawnByID(*itr);
|
|
//printf("Rail id: %i vs %i\n", spawn ? spawn->GetRailID() : 0, rail_id);
|
|
if(spawn && spawn->GetRailID() == rail_id){
|
|
closest_spawn = spawn;
|
|
break;
|
|
}
|
|
itr++;
|
|
}
|
|
MTransportSpawns.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
return closest_spawn;
|
|
}
|
|
|
|
void ZoneServer::SetRain(float val) {
|
|
rain = val;
|
|
vector<Client*>::iterator itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
Client* client = *itr;
|
|
client->GetPlayer()->GetInfoStruct()->set_rain(val);
|
|
client->GetPlayer()->SetCharSheetChanged(true);
|
|
if( val >= 0.75 && !weather_signaled )
|
|
{
|
|
client->SimpleMessage(CHANNEL_NARRATIVE, "It starts to rain.");
|
|
}
|
|
else if( val < 0.75 && weather_signaled )
|
|
{
|
|
client->SimpleMessage(CHANNEL_NARRATIVE, "It stops raining.");
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
if (val >= 0.75 && !weather_signaled) {
|
|
weather_signaled = true;
|
|
ProcessSpawnConditional(SPAWN_CONDITIONAL_RAINING);
|
|
}
|
|
else if (val < 0.75 && weather_signaled) {
|
|
weather_signaled = false;
|
|
ProcessSpawnConditional(SPAWN_CONDITIONAL_NOT_RAINING);
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SetWind(float val) {
|
|
vector<Client*>::iterator itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
Client* client = *itr;
|
|
client->GetPlayer()->GetInfoStruct()->set_wind(val);
|
|
client->GetPlayer()->SetCharSheetChanged(true);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::ProcessWeather()
|
|
{
|
|
// if the global rule to disable weather is set, or if the `weather_allowed` field in the zone record == 0, do not process weather
|
|
if( !weather_enabled || !isWeatherAllowed() )
|
|
return;
|
|
|
|
LogWrite(ZONE__DEBUG, 1, "Zone", "%s: Processing weather changes", zone_name);
|
|
float new_weather = 0;
|
|
float weather_offset = 0;
|
|
bool change_weather = false;
|
|
|
|
// check to see if it is time to change the weather according to weather_frequency (time between changes)
|
|
if( weather_last_changed_time <= (Timer::GetUnixTimeStamp() - weather_frequency) )
|
|
{
|
|
LogWrite(ZONE__DEBUG, 2, "Zone", "%s: Checking for weather changes", zone_name);
|
|
// reset last changed time (frequency check)
|
|
weather_last_changed_time = Timer::GetUnixTimeStamp();
|
|
|
|
// this is the chance a weather change occurs at all at the expired interval
|
|
int8 weather_random = MakeRandomInt(1, 100);
|
|
LogWrite(ZONE__DEBUG, 2, "Zone", "%s: Chance to change weather: %i%%, rolled: %i%% - Change weather: %s", zone_name, weather_change_chance, weather_random, weather_random <= weather_change_chance ? "True" : "False");
|
|
|
|
if( weather_random <= weather_change_chance )
|
|
{
|
|
change_weather = true;
|
|
weather_offset = weather_change_amount;
|
|
|
|
if( weather_type == 3 ) // chaotic weather patterns, random weather between min/max
|
|
{
|
|
new_weather = MakeRandomFloat(weather_min_severity, weather_max_severity);
|
|
LogWrite(ZONE__DEBUG, 3, "Zone", "%s: Chaotic weather severity changed to %.2f", zone_name, new_weather);
|
|
weather_pattern = 2;
|
|
}
|
|
else if( weather_type == 2 ) // random weather patterns, combination of normal + dynamic + max_offset
|
|
{
|
|
weather_offset = MakeRandomFloat(weather_change_amount, weather_dynamic_offset);
|
|
LogWrite(ZONE__DEBUG, 3, "Zone", "%s: Random weather severity changed by %.2f", zone_name, weather_offset);
|
|
|
|
int8 weather_alter = weather_change_chance / 10; // the divide is to prevent too many direction changes in a cycle
|
|
weather_random = MakeRandomInt(1, 100); // chance that the weather changes direction (weather_pattern)
|
|
|
|
if( weather_random <= weather_alter )
|
|
weather_pattern = ( weather_pattern == 0 ) ? 1 : 0;
|
|
}
|
|
else if( weather_type == 1 ) // dynamic weather patterns, weather may not reach min/max
|
|
{
|
|
int8 weather_alter = weather_change_chance / 10; // the divide is to prevent too many direction changes in a cycle
|
|
weather_random = MakeRandomInt(1, 100); // chance that the weather changes direction (weather_pattern)
|
|
|
|
if( weather_random <= weather_alter )
|
|
{
|
|
weather_pattern = ( weather_pattern == 0 ) ? 1 : 0;
|
|
LogWrite(ZONE__DEBUG, 3, "Zone", "%s: Dynamic weather pattern changed to %i", zone_name, weather_pattern);
|
|
}
|
|
}
|
|
else // normal weather patterns, weather starts at min, goes to max, then back down again
|
|
{
|
|
// do nothing (processed below)
|
|
LogWrite(ZONE__DEBUG, 3, "Zone", "%s: Normal weather severity changed by %.2f", zone_name, weather_offset);
|
|
}
|
|
|
|
// when all done, change the weather
|
|
if( change_weather )
|
|
{
|
|
if( weather_pattern == 1 )
|
|
{
|
|
// weather is getting worse, til it reaches weather_max_severity
|
|
new_weather = ( weather_current_severity <= weather_max_severity ) ? weather_current_severity + weather_offset : weather_max_severity;
|
|
LogWrite(ZONE__DEBUG, 3, "Zone", "%s: Increased weather severity by %.2f", zone_name, weather_offset);
|
|
|
|
if(new_weather > weather_max_severity)
|
|
{
|
|
new_weather = weather_max_severity - weather_offset;
|
|
weather_pattern = 0;
|
|
}
|
|
}
|
|
else if( weather_pattern == 0 )
|
|
{
|
|
// weather is clearing up, til it reaches weather_min_severity
|
|
new_weather = ( weather_current_severity >= weather_min_severity ) ? weather_current_severity - weather_offset : weather_min_severity;
|
|
LogWrite(ZONE__DEBUG, 3, "Zone", "%s: Decreased weather severity by %.2f", zone_name, weather_offset);
|
|
|
|
if(new_weather < weather_min_severity)
|
|
{
|
|
new_weather = weather_min_severity + weather_offset;
|
|
weather_pattern = 1;
|
|
}
|
|
}
|
|
|
|
LogWrite(ZONE__DEBUG, 1, "Zone", "%s: Weather change triggered from %.2f to %.2f", zone_name, weather_current_severity, new_weather);
|
|
this->SetRain(new_weather);
|
|
weather_current_severity = new_weather;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
LogWrite(ZONE__DEBUG, 1, "Zone", "%s: Not time to change weather yet", zone_name);
|
|
}
|
|
|
|
void ZoneServer::HidePrivateSpawn(Spawn* spawn) {
|
|
if (!spawn->IsPrivateSpawn())
|
|
return;
|
|
|
|
Client* client = 0;
|
|
Player* player = 0;
|
|
PacketStruct* packet = 0;
|
|
int32 packet_version = 0;
|
|
MutexList<Client*>::iterator itr = connected_clients.begin();
|
|
while (itr->Next()) {
|
|
client = itr->value;
|
|
player = client->GetPlayer();
|
|
if (player->WasSentSpawn(spawn->GetID()) || client->GetPlayer()->IsSendingSpawn(spawn->GetID())) {
|
|
if (!packet || packet_version != client->GetVersion()) {
|
|
safe_delete(packet);
|
|
packet_version = client->GetVersion();
|
|
packet = configReader.getStruct("WS_DestroyGhostCmd", packet_version);
|
|
}
|
|
|
|
SendRemoveSpawn(client, spawn, packet);
|
|
if(spawn_range_map.count(client) > 0)
|
|
spawn_range_map.Get(client)->erase(spawn->GetID());
|
|
|
|
if(player->GetTarget() == spawn)
|
|
player->SetTarget(0);
|
|
}
|
|
}
|
|
|
|
safe_delete(packet);
|
|
}
|
|
|
|
SpawnLocation* ZoneServer::GetSpawnLocation(int32 id) {
|
|
SpawnLocation* ret = 0;
|
|
MSpawnLocationList.readlock(__FUNCTION__, __LINE__);
|
|
if (spawn_location_list.count(id) > 0)
|
|
ret = spawn_location_list[id];
|
|
MSpawnLocationList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::PlayAnimation(Spawn* spawn, int32 visual_state, Spawn* spawn2, int8 hide_type){
|
|
Client* client = 0;
|
|
PacketStruct* packet = 0;
|
|
Spawn* exclude_spawn = 0;
|
|
if (!spawn)
|
|
return;
|
|
if (spawn2){
|
|
if(hide_type == 1){
|
|
if(spawn2->IsPlayer()) {
|
|
client = ((Player*)spawn2)->GetClient();
|
|
if(client){
|
|
packet = configReader.getStruct("WS_CannedEmote", client->GetVersion());
|
|
packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(spawn));
|
|
packet->setDataByName("anim_type", visual_state);
|
|
client->QueuePacket(packet->serialize());
|
|
}
|
|
safe_delete(packet);
|
|
return;
|
|
}
|
|
}
|
|
if(hide_type == 2)
|
|
exclude_spawn = spawn2;
|
|
}
|
|
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
|
|
client = *client_itr;
|
|
if(spawn->GetDistance(client->GetPlayer()) > 50)
|
|
continue;
|
|
if(exclude_spawn == client->GetPlayer())
|
|
continue;
|
|
if(!client->IsReadyForUpdates()) // client is not in world yet so we shouldn't be sending animations of spawns yet
|
|
continue;
|
|
|
|
if(!packet || packet->GetVersion() != client->GetVersion()) {
|
|
safe_delete(packet);
|
|
packet = configReader.getStruct("WS_CannedEmote", client->GetVersion());
|
|
}
|
|
if (packet) {
|
|
int32 spawn_id = client->GetPlayer()->GetIDWithPlayerSpawn(spawn);
|
|
if(spawn_id) {
|
|
packet->setDataByName("spawn_id", spawn_id);
|
|
packet->setDataByName("anim_type", visual_state);
|
|
client->QueuePacket(packet->serialize());
|
|
}
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
safe_delete(packet);
|
|
}
|
|
|
|
vector<Spawn*> ZoneServer::GetSpawnsByID(int32 id) {
|
|
vector<Spawn*> tmp_list;
|
|
Spawn* spawn;
|
|
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if (spawn && (spawn->GetDatabaseID() == id))
|
|
tmp_list.push_back(spawn);
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
return tmp_list;
|
|
}
|
|
|
|
|
|
vector<Spawn*> ZoneServer::GetSpawnsByRailID(sint64 rail_id) {
|
|
vector<Spawn*> tmp_list;
|
|
Spawn* spawn;
|
|
MTransportSpawns.readlock(__FUNCTION__, __LINE__);
|
|
vector<int32>::iterator itr = transport_spawns.begin();
|
|
while(itr != transport_spawns.end()){
|
|
spawn = GetSpawnByID(*itr);
|
|
if(spawn && spawn->GetRailID() == rail_id){
|
|
tmp_list.push_back(spawn);
|
|
}
|
|
itr++;
|
|
}
|
|
MTransportSpawns.releasereadlock(__FUNCTION__, __LINE__);
|
|
return tmp_list;
|
|
}
|
|
|
|
void ZoneServer::RemovePlayerPassenger(int32 char_id) {
|
|
vector<Spawn*> tmp_list;
|
|
Spawn* spawn;
|
|
MTransportSpawns.readlock(__FUNCTION__, __LINE__);
|
|
vector<int32>::iterator itr = transport_spawns.begin();
|
|
while(itr != transport_spawns.end()){
|
|
spawn = GetSpawnByID(*itr);
|
|
if(spawn) {
|
|
spawn->RemoveRailPassenger(char_id);
|
|
}
|
|
itr++;
|
|
}
|
|
MTransportSpawns.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
bool ZoneServer::SetPlayerTargetByName(Client* originator, char* targetName, float distance) {
|
|
if(!targetName || !originator->GetPlayer()) {
|
|
return false;
|
|
}
|
|
int32 name_size = strlen(targetName);
|
|
if(name_size < 1 || name_size > 127) {
|
|
return false;
|
|
}
|
|
|
|
auto loc = glm::vec3(originator->GetPlayer()->GetX(), originator->GetPlayer()->GetZ(), originator->GetPlayer()->GetY());
|
|
std::vector<int32> grids_by_radius;
|
|
if(originator->GetPlayer()->GetMap()) {
|
|
grids_by_radius = GetGridsByLocation(originator->GetPlayer(), loc, distance);
|
|
}
|
|
else {
|
|
grids_by_radius.push_back(originator->GetPlayer()->GetLocation());
|
|
}
|
|
|
|
bool found_target = false;
|
|
float spawn_dist = 999999.0f;
|
|
Spawn* target_spawn = nullptr;
|
|
float tmp_dist = 0.0f;
|
|
MGridMaps.lock_shared();
|
|
std::vector<int32>::iterator grid_radius_itr;
|
|
for(grid_radius_itr = grids_by_radius.begin(); grid_radius_itr != grids_by_radius.end(); grid_radius_itr++) {
|
|
std::map<int32, GridMap*>::iterator grids = grid_maps.find((*grid_radius_itr));
|
|
if(grids != grid_maps.end()) {
|
|
grids->second->MSpawns.lock_shared();
|
|
typedef map <int32, Spawn*> SpawnMapType;
|
|
for( SpawnMapType::iterator it = grids->second->spawns.begin(); it != grids->second->spawns.end(); ++it ) {
|
|
Spawn* spawn = it->second;
|
|
|
|
bool inSameGroup = false;
|
|
if(spawn->IsEntity()) {
|
|
GroupMemberInfo* gmi = originator->GetPlayer()->GetGroupMemberInfo();
|
|
inSameGroup = (((Entity*)spawn)->GetGroupMemberInfo() && originator->GetPlayer()->GetGroupMemberInfo() && ((Entity*)spawn)->GetGroupMemberInfo()->group_id == originator->GetPlayer()->GetGroupMemberInfo()->group_id);
|
|
}
|
|
if (spawn && spawn->appearance.targetable > 0 && !strncasecmp(spawn->GetName(), targetName, name_size) && (inSameGroup || (((tmp_dist = spawn->GetDistance(originator->GetPlayer(), true)) <= distance) && tmp_dist < spawn_dist && originator->GetPlayer()->CheckLoS(spawn)))) {
|
|
spawn_dist = tmp_dist;
|
|
target_spawn = spawn;
|
|
}
|
|
}
|
|
grids->second->MSpawns.unlock_shared();
|
|
}
|
|
}
|
|
MGridMaps.unlock_shared();
|
|
|
|
if(target_spawn != nullptr) {
|
|
originator->TargetSpawn(target_spawn);
|
|
}
|
|
|
|
return (target_spawn != nullptr);
|
|
}
|
|
|
|
std::vector<int32> ZoneServer::GetGridsByLocation(Spawn* originator, glm::vec3 loc, float distance) {
|
|
std::vector<int32> grids_by_radius;
|
|
if(originator == nullptr)
|
|
return grids_by_radius;
|
|
|
|
if(originator->GetMap()) {
|
|
grids_by_radius = originator->GetMap()->GetGridsByPoint(loc, distance);
|
|
if(default_zone_map && default_zone_map != originator->GetMap()) {
|
|
std::vector<int32> default_grids = default_zone_map->GetGridsByPoint(loc, distance);
|
|
|
|
std::unordered_set<int32> elements(grids_by_radius.begin(), grids_by_radius.end());
|
|
|
|
for (const auto& elem : default_grids) {
|
|
if (elements.find(elem) == elements.end()) {
|
|
grids_by_radius.push_back(elem);
|
|
elements.insert(elem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return grids_by_radius;
|
|
}
|
|
|
|
std::vector<std::pair<int32, float>> ZoneServer::GetAttackableSpawnsByDistance(Spawn* caster, float distance) {
|
|
std::vector<std::pair<int32, float>> spawns_by_distance;
|
|
Spawn* spawn = 0;
|
|
auto loc = glm::vec3(caster->GetX(), caster->GetZ(), caster->GetY());
|
|
std::vector<int32> grids_by_radius;
|
|
if(caster->GetMap()) {
|
|
grids_by_radius = GetGridsByLocation(caster, loc, distance);
|
|
}
|
|
else {
|
|
grids_by_radius.push_back(caster->GetLocation());
|
|
}
|
|
|
|
float tmp_dist = 0.0f;
|
|
MGridMaps.lock_shared();
|
|
std::vector<int32>::iterator grid_radius_itr;
|
|
for(grid_radius_itr = grids_by_radius.begin(); grid_radius_itr != grids_by_radius.end(); grid_radius_itr++) {
|
|
std::map<int32, GridMap*>::iterator grids = grid_maps.find((*grid_radius_itr));
|
|
if(grids != grid_maps.end()) {
|
|
grids->second->MSpawns.lock_shared();
|
|
typedef map <int32, Spawn*> SpawnMapType;
|
|
for( SpawnMapType::iterator it = grids->second->spawns.begin(); it != grids->second->spawns.end(); ++it ) {
|
|
Spawn* spawn = it->second;
|
|
if (spawn && spawn->IsNPC() && spawn->appearance.attackable > 0 && spawn->GetID() > 0 && spawn->GetID() != caster->GetID() &&
|
|
spawn->Alive() && ((tmp_dist = spawn->GetDistance(caster, true)) <= distance)) {
|
|
spawns_by_distance.push_back({spawn->GetID(), tmp_dist});
|
|
}
|
|
}
|
|
grids->second->MSpawns.unlock_shared();
|
|
}
|
|
}
|
|
MGridMaps.unlock_shared();
|
|
std::sort(spawns_by_distance.begin(), spawns_by_distance.end(), compareByValue);
|
|
|
|
return spawns_by_distance;
|
|
}
|
|
|
|
void ZoneServer::ResurrectSpawn(Spawn* spawn, Client* client) {
|
|
if(!client || !spawn)
|
|
return;
|
|
PendingResurrection* rez = client->GetCurrentRez();
|
|
if(!rez)
|
|
return;
|
|
|
|
PacketStruct* packet = 0;
|
|
float power_perc = rez->mp_perc;
|
|
float health_perc = rez->hp_perc;
|
|
Spawn* caster_spawn = GetSpawnByID(rez->caster);
|
|
|
|
if(!caster_spawn)
|
|
return;
|
|
|
|
sint32 heal_amt = 0;
|
|
sint32 power_amt = 0;
|
|
bool no_calcs = rez->no_calcs;
|
|
int8 crit_mod = rez->crit_mod;
|
|
Entity* caster = 0;
|
|
InfoStruct* info = 0;
|
|
bool crit = false;
|
|
string heal_spell = rez->heal_name;
|
|
int16 heal_packet_type = 0;
|
|
int16 power_packet_type = 0;
|
|
|
|
//Calculations for how much to heal the spawn
|
|
if(health_perc > 0)
|
|
heal_amt = (spawn->GetTotalHP() * (health_perc / 100));
|
|
if(power_perc > 0)
|
|
power_amt = (spawn->GetTotalPower() * (power_perc / 100));
|
|
|
|
if(caster_spawn->IsEntity()){
|
|
caster = ((Entity*)caster_spawn);
|
|
info = caster->GetInfoStruct();
|
|
}
|
|
|
|
if(!no_calcs && caster){
|
|
heal_amt = caster->CalculateHealAmount(spawn, heal_amt, crit_mod, &crit);
|
|
power_amt = caster->CalculateHealAmount(spawn, power_amt, crit_mod, &crit);
|
|
}
|
|
|
|
//Set this rez as a crit to be passed to subspell (not yet used)
|
|
rez->crit = true;
|
|
|
|
//Set Heal amt to 1 if 0 now so the player has health
|
|
if(heal_amt == 0)
|
|
heal_amt = 1;
|
|
|
|
if(heal_amt > spawn->GetTotalHP())
|
|
heal_amt = spawn->GetTotalHP();
|
|
if(power_amt > spawn->GetTotalPower())
|
|
power_amt = spawn->GetTotalPower();
|
|
|
|
spawn->SetAlive(true);
|
|
spawn->SetHP(heal_amt);
|
|
if(power_amt > 0)
|
|
spawn->SetPower(power_amt);
|
|
|
|
if(client && caster){
|
|
EQ2Packet* move = ((Player*)spawn)->Move(caster->GetX(), caster->GetY(), caster->GetZ(), client->GetVersion());
|
|
if(move)
|
|
client->QueuePacket(move);
|
|
}
|
|
|
|
if(crit){
|
|
power_packet_type = HEAL_PACKET_TYPE_CRIT_MANA;
|
|
heal_packet_type = HEAL_PACKET_TYPE_CRIT_HEAL;
|
|
}
|
|
else {
|
|
power_packet_type = HEAL_PACKET_TYPE_SIMPLE_MANA;
|
|
heal_packet_type = HEAL_PACKET_TYPE_SIMPLE_HEAL;
|
|
}
|
|
|
|
SendHealPacket(caster, spawn, heal_packet_type, heal_amt, heal_spell.c_str());
|
|
if(power_amt > 0)
|
|
SendHealPacket(caster, spawn, power_packet_type, power_amt, heal_spell.c_str());
|
|
|
|
//The following code sets the spawn as alive
|
|
if(dead_spawns.count(spawn->GetID()) > 0)
|
|
dead_spawns.erase(spawn->GetID());
|
|
|
|
if(spawn->IsPlayer()){
|
|
spawn->SetSpawnType(4);
|
|
client = ((Player*)spawn)->GetClient();
|
|
if(client){
|
|
packet = configReader.getStruct("WS_Resurrected", client->GetVersion());
|
|
if(packet){
|
|
client->QueuePacket(packet->serialize());
|
|
}
|
|
safe_delete(packet);
|
|
|
|
if(client->GetVersion() <= 561) {
|
|
ClientPacketFunctions::SendServerControlFlagsClassic(client, 8, 0);
|
|
ClientPacketFunctions::SendServerControlFlagsClassic(client, 16, 0);
|
|
}
|
|
else {
|
|
ClientPacketFunctions::SendServerControlFlags(client, 1, 8, 0);
|
|
ClientPacketFunctions::SendServerControlFlags(client, 1, 16, 0);
|
|
}
|
|
client->SimpleMessage(CHANNEL_NARRATIVE, "You regain consciousness!");
|
|
}
|
|
}
|
|
spawn->SendSpawnChanges(true);
|
|
spawn->SetTempActionState(-1);
|
|
spawn->appearance.attackable = 1;
|
|
|
|
if(rez->revive_sickness_spell_id) {
|
|
Spell* spell = master_spell_list.GetSpell(rez->revive_sickness_spell_id, rez->revive_sickness_spell_tier);
|
|
|
|
if (spell)
|
|
{
|
|
GetSpellProcess()->CastInstant(spell, caster ? caster : (Entity*)client->GetPlayer(), (Entity*)client->GetPlayer());
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SendDispellPacket(Entity* caster, Spawn* target, string dispell_name, string spell_name, int8 dispell_type){
|
|
if(!caster || !target)
|
|
return;
|
|
|
|
Client* client = 0;
|
|
Player* player = 0;
|
|
PacketStruct* packet = 0;
|
|
vector<Client*>::iterator client_itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++){
|
|
client = *client_itr;
|
|
if(!client || !(player = client->GetPlayer()) || (player != caster && ((caster && player->WasSentSpawn(caster->GetID()) == false) || (target && player->WasSentSpawn(target->GetID()) == false))))
|
|
continue;
|
|
if(caster && caster->GetDistance(player) > 50)
|
|
continue;
|
|
if(target && target->GetDistance(player) > 50)
|
|
continue;
|
|
|
|
packet = configReader.getStruct("WS_HearDispell", client->GetVersion());
|
|
if(packet){
|
|
packet->setDataByName("spell_name", spell_name.c_str());
|
|
packet->setDataByName("dispell_name", dispell_name.c_str());
|
|
packet->setDataByName("caster", player->GetIDWithPlayerSpawn(caster));
|
|
packet->setDataByName("target", player->GetIDWithPlayerSpawn(target));
|
|
packet->setDataByName("type", dispell_type);
|
|
client->QueuePacket(packet->serialize());
|
|
}
|
|
safe_delete(packet);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::DismissAllPets() {
|
|
Spawn* spawn = 0;
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if (spawn && spawn->IsEntity())
|
|
((Entity*)spawn)->DismissAllPets();
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::RemoveTargetFromSpell(LuaSpell* spell, Spawn* target, bool remove_caster){
|
|
if (spellProcess)
|
|
spellProcess->RemoveTargetFromSpell(spell, target, remove_caster);
|
|
}
|
|
|
|
void ZoneServer::ClearHate(Entity* entity) {
|
|
Spawn* spawn = 0;
|
|
map<int32, Spawn*>::iterator itr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if (spawn && spawn->IsNPC() && ((NPC*)spawn)->Brain())
|
|
((NPC*)spawn)->Brain()->ClearHate(entity);
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
ThreadReturnType ZoneLoop(void* tmp) {
|
|
#ifdef WIN32
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
|
#endif
|
|
if (tmp == 0) {
|
|
ThrowError("ZoneLoop(): tmp = 0!");
|
|
THREAD_RETURN(NULL);
|
|
}
|
|
ZoneServer* zs = (ZoneServer*) tmp;
|
|
while (zs->Process()) {
|
|
if(zs->GetClientCount() == 0)
|
|
Sleep(1000);
|
|
else
|
|
Sleep(10);
|
|
}
|
|
// we failed, time to disappear, no more processing period
|
|
safe_delete(zs);
|
|
THREAD_RETURN(NULL);
|
|
}
|
|
|
|
ThreadReturnType SpawnLoop(void* tmp) {
|
|
#ifdef WIN32
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
|
#endif
|
|
if (tmp == 0) {
|
|
ThrowError("SpawnLoop(): tmp = 0!");
|
|
THREAD_RETURN(NULL);
|
|
}
|
|
ZoneServer* zs = (ZoneServer*) tmp;
|
|
#ifndef NO_CATCH
|
|
try {
|
|
#endif
|
|
zs->spawnthread_active = true;
|
|
while (zs->SpawnProcess()) {
|
|
if(zs->GetClientCount() == 0)
|
|
Sleep(1000);
|
|
else
|
|
Sleep(20);
|
|
}
|
|
zs->spawnthread_active = false;
|
|
#ifndef NO_CATCH
|
|
}
|
|
catch(...) {
|
|
zs->spawnthread_active = false;
|
|
zs->initial_spawn_threads_active = 0;
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "Error Processing SpawnLoop, shutting down zone '%s'...", zs->GetZoneName());
|
|
try{
|
|
zs->Shutdown();
|
|
}
|
|
catch(...){
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "Error Processing SpawnLoop while shutting down zone '%s'...", zs->GetZoneName());
|
|
throw;
|
|
}
|
|
throw;
|
|
}
|
|
#endif
|
|
THREAD_RETURN(NULL);
|
|
}
|
|
|
|
ThreadReturnType SendInitialSpawns(void* tmp) {
|
|
#ifdef WIN32
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
|
#endif
|
|
if (tmp == 0) {
|
|
ThrowError("SendInitialSpawns(): tmp = 0!");
|
|
THREAD_RETURN(NULL);
|
|
}
|
|
Client* client = (Client*) tmp;
|
|
client->GetCurrentZone()->SendZoneSpawns(client);
|
|
THREAD_RETURN(NULL);
|
|
}
|
|
|
|
ThreadReturnType SendLevelChangedSpawns(void* tmp) {
|
|
#ifdef WIN32
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
|
|
#endif
|
|
if (tmp == 0) {
|
|
ThrowError("SendLevelChangedSpawns(): tmp = 0!");
|
|
THREAD_RETURN(NULL);
|
|
}
|
|
Client* client = (Client*)tmp;
|
|
client->GetCurrentZone()->SendAllSpawnsForLevelChange(client);
|
|
THREAD_RETURN(NULL);
|
|
}
|
|
|
|
void ZoneServer::SetSpawnStructs(Client* client) {
|
|
int16 client_ver = client->GetVersion();
|
|
Player* player = client->GetPlayer();
|
|
|
|
//Save a copy of the correct spawn substructs for the client's player, save here a copy if we don't have one
|
|
PacketStruct* pos = configReader.getStruct("Substruct_SpawnPositionStruct", client_ver);
|
|
player->SetSpawnPosStruct(pos);
|
|
if (versioned_pos_structs.count(pos->GetVersion()) == 0)
|
|
versioned_pos_structs[pos->GetVersion()] = new PacketStruct(pos, true);
|
|
|
|
PacketStruct* vis = configReader.getStruct("Substruct_SpawnVisualizationInfoStruct", client_ver);
|
|
player->SetSpawnVisStruct(vis);
|
|
if (versioned_vis_structs.count(vis->GetVersion()) == 0)
|
|
versioned_vis_structs[vis->GetVersion()] = new PacketStruct(vis, true);
|
|
|
|
PacketStruct* info = configReader.getStruct("Substruct_SpawnInfoStruct", client_ver);
|
|
player->SetSpawnInfoStruct(info);
|
|
if (versioned_info_structs.count(info->GetVersion()) == 0)
|
|
versioned_info_structs[info->GetVersion()] = new PacketStruct(info, true);
|
|
|
|
PacketStruct* header = configReader.getStruct("WS_SpawnStruct_Header", client_ver);
|
|
player->SetSpawnHeaderStruct(header);
|
|
|
|
PacketStruct* footer = configReader.getStruct("WS_SpawnStruct_Footer", client_ver);
|
|
player->SetSpawnFooterStruct(footer);
|
|
|
|
PacketStruct* sfooter = configReader.getStruct("WS_SignWidgetSpawnStruct_Footer", client_ver);
|
|
player->SetSignFooterStruct(sfooter);
|
|
|
|
PacketStruct* wfooter = configReader.getStruct("WS_WidgetSpawnStruct_Footer", client_ver);
|
|
player->SetWidgetFooterStruct(wfooter);
|
|
}
|
|
|
|
Spawn* ZoneServer::GetSpawn(int32 id){
|
|
Spawn* ret = 0;
|
|
|
|
if(GetNPC(id))
|
|
ret = GetNewNPC(id);
|
|
else if(this->GetObject(id))
|
|
ret = GetNewObject(id);
|
|
else if(GetWidget(id))
|
|
ret = GetNewWidget(id);
|
|
else if(GetSign(id))
|
|
ret = GetNewSign(id);
|
|
else if(GetGroundSpawn(id))
|
|
ret = GetNewGroundSpawn(id);
|
|
// Unable to find the spawn in the list lets attempt to add it if we are not currently reloading
|
|
else if (!reloading && database.LoadNPC(this, id)) {
|
|
if (GetNPC(id))
|
|
ret = GetNewNPC(id);
|
|
else
|
|
LogWrite(NPC__ERROR, 0, "NPC", "Database inserted npc (%u) but was still unable to retrieve it!", id);
|
|
}
|
|
else if (!reloading && database.LoadObject(this, id)) {
|
|
if (this->GetObject(id))
|
|
ret = GetNewObject(id);
|
|
else
|
|
LogWrite(OBJECT__ERROR, 0, "Object", "Database inserted object (%u) but was still unable to retrieve it!", id);
|
|
}
|
|
else if (!reloading && database.LoadWidget(this, id)) {
|
|
if (GetWidget(id))
|
|
ret = GetNewWidget(id);
|
|
else
|
|
LogWrite(WIDGET__ERROR, 0, "Widget", "Database inserted widget (%u) but was still unable to retrieve it!", id);
|
|
}
|
|
else if (!reloading && database.LoadSign(this, id)) {
|
|
if (GetSign(id))
|
|
ret = GetNewSign(id);
|
|
else
|
|
LogWrite(SIGN__ERROR, 0, "Sign", "Database inserted sign (%u) but was still unable to retrieve it!", id);
|
|
}
|
|
else if (!reloading && database.LoadGroundSpawn(this, id)) {
|
|
if (GetGroundSpawn(id))
|
|
ret = GetNewGroundSpawn(id);
|
|
else
|
|
LogWrite(GROUNDSPAWN__ERROR, 0, "GSpawn", "Database inserted ground spawn (%u) but was still unable to retrieve it!", id);
|
|
}
|
|
|
|
if(ret && ret->IsOmittedByDBFlag())
|
|
{
|
|
LogWrite(SPAWN__WARNING, 0, "Spawn", "Spawn (%u) was skipped due to a missing expansion / holiday flag being met.", id);
|
|
safe_delete(ret);
|
|
ret = 0;
|
|
}
|
|
|
|
if(ret)
|
|
ret->SetID(Spawn::NextID());
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
|
|
vector<EntityCommand*>* ZoneServer::GetEntityCommandList(int32 id){
|
|
if(entity_command_list.count(id) > 0)
|
|
return entity_command_list[id];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void ZoneServer::SetEntityCommandList(int32 id, EntityCommand* command) {
|
|
if (entity_command_list.count(id) == 0)
|
|
entity_command_list[id] = new vector<EntityCommand*>;
|
|
|
|
entity_command_list[id]->push_back(command);
|
|
}
|
|
|
|
EntityCommand* ZoneServer::GetEntityCommand(int32 id, string name) {
|
|
EntityCommand* ret = 0;
|
|
if (entity_command_list.count(id) == 0)
|
|
return ret;
|
|
|
|
vector<EntityCommand*>::iterator itr;
|
|
for (itr = entity_command_list[id]->begin(); itr != entity_command_list[id]->end(); itr++) {
|
|
if ((*itr)->name == name) {
|
|
ret = (*itr);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::ClearEntityCommands() {
|
|
if (entity_command_list.size() > 0) {
|
|
map<int32, vector<EntityCommand*>* >::iterator itr;
|
|
for (itr = entity_command_list.begin(); itr != entity_command_list.end(); itr++) {
|
|
vector<EntityCommand*>* entity_commands = itr->second;
|
|
if (entity_commands && entity_commands->size() > 0) {
|
|
vector<EntityCommand*>::iterator v_itr;
|
|
for (v_itr = entity_commands->begin(); v_itr != entity_commands->end(); v_itr++)
|
|
safe_delete(*v_itr);
|
|
entity_commands->clear();
|
|
}
|
|
safe_delete(entity_commands);
|
|
}
|
|
entity_command_list.clear();
|
|
}
|
|
}
|
|
|
|
void ZoneServer::AddNPCSkill(int32 list_id, int32 skill_id, int16 value){
|
|
npc_skill_list[list_id][skill_id] = value;
|
|
}
|
|
|
|
map<string, Skill*>* ZoneServer::GetNPCSkills(int32 primary_list, int32 secondary_list){
|
|
map<string, Skill*>* ret = 0;
|
|
if(npc_skill_list.count(primary_list) > 0){
|
|
ret = new map<string, Skill*>();
|
|
map<int32, int16>::iterator itr;
|
|
Skill* tmpSkill = 0;
|
|
for(itr = npc_skill_list[primary_list].begin(); itr != npc_skill_list[primary_list].end(); itr++){
|
|
tmpSkill = master_skill_list.GetSkill(itr->first);
|
|
if(tmpSkill){
|
|
tmpSkill = new Skill(tmpSkill);
|
|
tmpSkill->current_val = itr->second;
|
|
tmpSkill->max_val = tmpSkill->current_val+5;
|
|
(*ret)[tmpSkill->name.data] = tmpSkill;
|
|
}
|
|
}
|
|
}
|
|
if(npc_skill_list.count(secondary_list) > 0){
|
|
if(!ret)
|
|
ret = new map<string, Skill*>();
|
|
map<int32, int16>::iterator itr;
|
|
Skill* tmpSkill = 0;
|
|
for(itr = npc_skill_list[secondary_list].begin(); itr != npc_skill_list[secondary_list].end(); itr++){
|
|
tmpSkill = master_skill_list.GetSkill(itr->first);
|
|
if(tmpSkill){
|
|
tmpSkill = new Skill(tmpSkill);
|
|
tmpSkill->current_val = itr->second;
|
|
tmpSkill->max_val = tmpSkill->current_val+5;
|
|
(*ret)[tmpSkill->name.data] = tmpSkill;
|
|
}
|
|
}
|
|
}
|
|
if(ret && ret->size() == 0){
|
|
safe_delete(ret);
|
|
ret = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::AddNPCEquipment(int32 list_id, int32 item_id){
|
|
npc_equipment_list[list_id].push_back(item_id);
|
|
}
|
|
|
|
void ZoneServer::SetNPCEquipment(NPC* npc) {
|
|
if(npc_equipment_list.count(npc->GetEquipmentListID()) > 0){
|
|
Item* tmpItem = 0;
|
|
int8 slot = 0;
|
|
vector<int32>::iterator itr;
|
|
for(itr = npc_equipment_list[npc->GetEquipmentListID()].begin(); itr != npc_equipment_list[npc->GetEquipmentListID()].end(); itr++){
|
|
tmpItem = master_item_list.GetItem(*itr);
|
|
if(tmpItem){
|
|
slot = npc->GetEquipmentList()->GetFreeSlot(tmpItem);
|
|
if(slot < 255){
|
|
tmpItem = new Item(tmpItem);
|
|
npc->GetEquipmentList()->SetItem(slot, tmpItem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::AddNPC(int32 id, NPC* npc) {
|
|
npc_list[id] = npc;
|
|
}
|
|
|
|
void ZoneServer::AddWidget(int32 id, Widget* widget) {
|
|
widget_list[id] = widget;
|
|
}
|
|
|
|
Widget* ZoneServer::GetWidget(int32 id, bool override_loading) {
|
|
if((!reloading || override_loading) && widget_list.count(id) > 0)
|
|
return widget_list[id];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
Widget* ZoneServer::GetNewWidget(int32 id) {
|
|
if(!reloading && widget_list.count(id) > 0)
|
|
return widget_list[id]->Copy();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
|
|
void ZoneServer::LoadGroundSpawnEntries(){
|
|
MGroundSpawnItems.lock();
|
|
database.LoadGroundSpawnEntries(this);
|
|
MGroundSpawnItems.unlock();
|
|
}
|
|
|
|
void ZoneServer::LoadGroundSpawnItems() {
|
|
}
|
|
|
|
void ZoneServer::AddGroundSpawnEntry(int32 groundspawn_id, int16 min_skill_level, int16 min_adventure_level, int8 bonus_table, float harvest1, float harvest3, float harvest5, float harvest_imbue, float harvest_rare, float harvest10, int32 harvest_coin) {
|
|
GroundSpawnEntry* entry = new GroundSpawnEntry;
|
|
entry->min_skill_level = min_skill_level;
|
|
entry->min_adventure_level = min_adventure_level;
|
|
entry->bonus_table = bonus_table;
|
|
entry->harvest1 = harvest1;
|
|
entry->harvest3 = harvest3;
|
|
entry->harvest5 = harvest5;
|
|
entry->harvest_imbue = harvest_imbue;
|
|
entry->harvest_rare = harvest_rare;
|
|
entry->harvest10 = harvest10;
|
|
entry->harvest_coin = harvest_coin;
|
|
groundspawn_entries[groundspawn_id].push_back(entry);
|
|
}
|
|
|
|
void ZoneServer::AddGroundSpawnItem(int32 groundspawn_id, int32 item_id, int8 is_rare, int32 grid_id) {
|
|
GroundSpawnEntryItem* entry = new GroundSpawnEntryItem;
|
|
entry->item_id = item_id;
|
|
entry->is_rare = is_rare;
|
|
entry->grid_id = grid_id;
|
|
groundspawn_items[groundspawn_id].push_back(entry);
|
|
|
|
}
|
|
|
|
vector<GroundSpawnEntry*>* ZoneServer::GetGroundSpawnEntries(int32 id){
|
|
vector<GroundSpawnEntry*>* ret = 0;
|
|
MGroundSpawnItems.lock();
|
|
if(groundspawn_entries.count(id) > 0)
|
|
ret = &groundspawn_entries[id];
|
|
MGroundSpawnItems.unlock();
|
|
return ret;
|
|
}
|
|
|
|
vector<GroundSpawnEntryItem*>* ZoneServer::GetGroundSpawnEntryItems(int32 id){
|
|
vector<GroundSpawnEntryItem*>* ret = 0;
|
|
if(groundspawn_items.count(id) > 0)
|
|
ret = &groundspawn_items[id];
|
|
return ret;
|
|
}
|
|
|
|
// TODO - mis-named, should be DeleteGroundSpawnEntries() but this is ok for now :)
|
|
void ZoneServer::DeleteGroundSpawnItems()
|
|
{
|
|
MGroundSpawnItems.lock();
|
|
|
|
map<int32, vector<GroundSpawnEntry*> >::iterator groundspawnentry_map_itr;
|
|
vector<GroundSpawnEntry*>::iterator groundspawnentry_itr;
|
|
for(groundspawnentry_map_itr = groundspawn_entries.begin(); groundspawnentry_map_itr != groundspawn_entries.end(); groundspawnentry_map_itr++)
|
|
{
|
|
for(groundspawnentry_itr = groundspawnentry_map_itr->second.begin(); groundspawnentry_itr != groundspawnentry_map_itr->second.end(); groundspawnentry_itr++)
|
|
{
|
|
safe_delete(*groundspawnentry_itr);
|
|
}
|
|
}
|
|
groundspawn_entries.clear();
|
|
|
|
map<int32, vector<GroundSpawnEntryItem*> >::iterator groundspawnitem_map_itr;
|
|
vector<GroundSpawnEntryItem*>::iterator groundspawnitem_itr;
|
|
for(groundspawnitem_map_itr = groundspawn_items.begin(); groundspawnitem_map_itr != groundspawn_items.end(); groundspawnitem_map_itr++)
|
|
{
|
|
for(groundspawnitem_itr = groundspawnitem_map_itr->second.begin(); groundspawnitem_itr != groundspawnitem_map_itr->second.end(); groundspawnitem_itr++)
|
|
{
|
|
safe_delete(*groundspawnitem_itr);
|
|
}
|
|
}
|
|
groundspawn_items.clear();
|
|
|
|
MGroundSpawnItems.unlock();
|
|
}
|
|
|
|
void ZoneServer::AddGroundSpawn(int32 id, GroundSpawn* spawn) {
|
|
groundspawn_list[id] = spawn;
|
|
}
|
|
|
|
GroundSpawn* ZoneServer::GetGroundSpawn(int32 id, bool override_loading) {
|
|
if((!reloading || override_loading) && groundspawn_list.count(id) > 0)
|
|
return groundspawn_list[id];
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
GroundSpawn* ZoneServer::GetNewGroundSpawn(int32 id) {
|
|
if(!reloading && groundspawn_list.count(id) > 0)
|
|
return groundspawn_list[id]->Copy();
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
void ZoneServer::AddLootTable(int32 id, LootTable* table){
|
|
loot_tables[id] = table;
|
|
}
|
|
|
|
void ZoneServer::AddLootDrop(int32 id, LootDrop* drop){
|
|
loot_drops[id].push_back(drop);
|
|
}
|
|
|
|
void ZoneServer::AddSpawnLootList(int32 spawn_id, int32 id) {
|
|
spawn_loot_list[spawn_id].push_back(id);
|
|
}
|
|
|
|
void ZoneServer::ClearSpawnLootList(int32 spawn_id) {
|
|
spawn_loot_list[spawn_id].clear();
|
|
}
|
|
|
|
void ZoneServer::AddLevelLootList(GlobalLoot* loot) {
|
|
level_loot_list.push_back(loot);
|
|
}
|
|
|
|
void ZoneServer::AddRacialLootList(int16 racial_id, GlobalLoot* loot) {
|
|
racial_loot_list[racial_id].push_back(loot);
|
|
}
|
|
|
|
void ZoneServer::AddZoneLootList(int32 zone, GlobalLoot* loot) {
|
|
zone_loot_list[zone].push_back(loot);
|
|
}
|
|
|
|
void ZoneServer::ClearLootTables(){
|
|
map<int32,LootTable*>::iterator table_itr;
|
|
for(table_itr = loot_tables.begin(); table_itr != loot_tables.end(); table_itr++){
|
|
safe_delete(table_itr->second);
|
|
}
|
|
|
|
map<int32, vector<LootDrop*> >::iterator drop_itr;
|
|
vector<LootDrop*>::iterator drop_itr2;
|
|
for(drop_itr = loot_drops.begin(); drop_itr != loot_drops.end(); drop_itr++){
|
|
for(drop_itr2 = drop_itr->second.begin(); drop_itr2 != drop_itr->second.end(); drop_itr2++){
|
|
safe_delete(*drop_itr2);
|
|
}
|
|
}
|
|
|
|
vector<GlobalLoot*>::iterator level_itr;
|
|
for (level_itr = level_loot_list.begin(); level_itr != level_loot_list.end(); level_itr++) {
|
|
safe_delete(*level_itr);
|
|
}
|
|
|
|
|
|
map<int16, vector<GlobalLoot*> >::iterator race_itr;
|
|
vector<GlobalLoot*>::iterator race_itr2;
|
|
for (race_itr = racial_loot_list.begin(); race_itr != racial_loot_list.end(); race_itr++) {
|
|
for (race_itr2 = race_itr->second.begin(); race_itr2 != race_itr->second.end(); race_itr2++) {
|
|
safe_delete(*race_itr2);
|
|
}
|
|
}
|
|
|
|
map<int32, vector<GlobalLoot*> >::iterator zone_itr;
|
|
vector<GlobalLoot*>::iterator zone_itr2;
|
|
for(zone_itr = zone_loot_list.begin(); zone_itr != zone_loot_list.end(); zone_itr++) {
|
|
for (zone_itr2 = zone_itr->second.begin(); zone_itr2 != zone_itr->second.end(); zone_itr2++) {
|
|
safe_delete(*zone_itr2);
|
|
}
|
|
}
|
|
|
|
loot_tables.clear();
|
|
loot_drops.clear();
|
|
spawn_loot_list.clear();
|
|
level_loot_list.clear();
|
|
racial_loot_list.clear();
|
|
zone_loot_list.clear();
|
|
}
|
|
|
|
vector<int32> ZoneServer::GetSpawnLootList(int32 spawn_id, int32 zone_id, int8 spawn_level, int16 racial_id, Spawn* spawn) {
|
|
vector<int32> ret;
|
|
int32 returnValue = 0;
|
|
|
|
if(reloading)
|
|
return ret;
|
|
|
|
if (spawn_loot_list.count(spawn_id) > 0)
|
|
ret.insert(ret.end(), spawn_loot_list[spawn_id].begin(), spawn_loot_list[spawn_id].end());
|
|
|
|
if (level_loot_list.size() > 0) {
|
|
vector<GlobalLoot*>::iterator itr;
|
|
for (itr = level_loot_list.begin(); itr != level_loot_list.end(); itr++) {
|
|
GlobalLoot* loot = *itr;
|
|
const char* zone_script = world.GetZoneScript(this->GetZoneID());
|
|
returnValue = 0; // reset since this can override the database setting
|
|
if(zone_script)
|
|
{
|
|
if(lua_interface->RunZoneScriptWithReturn(zone_script, "loot_criteria_level", spawn->GetZone(), spawn, loot->table_id, loot->minLevel, loot->maxLevel, &returnValue) && returnValue == 0)
|
|
continue;
|
|
}
|
|
bool entryAdded = false;
|
|
if (loot->minLevel == 0 && loot->maxLevel == 0 && (!loot->loot_tier || spawn->GetLootTier() >= loot->loot_tier) && (entryAdded = true)) // successful plan to add set entryAdded to true
|
|
ret.push_back(loot->table_id);
|
|
else {
|
|
if (spawn_level >= loot->minLevel && spawn_level <= loot->maxLevel && (!loot->loot_tier || spawn->GetLootTier() >= loot->loot_tier) && (entryAdded = true)) // successful plan to add set entryAdded to true
|
|
ret.push_back(loot->table_id);
|
|
}
|
|
|
|
if(!entryAdded && returnValue) // DB override via LUA scripting
|
|
ret.push_back(loot->table_id);
|
|
}
|
|
}
|
|
|
|
if (racial_loot_list.count(racial_id) > 0) {
|
|
vector<GlobalLoot*>::iterator itr;
|
|
for (itr = racial_loot_list[racial_id].begin(); itr != racial_loot_list[racial_id].end(); itr++) {
|
|
GlobalLoot* loot = *itr;
|
|
const char* zone_script = world.GetZoneScript(this->GetZoneID());
|
|
returnValue = 0; // reset since this can override the database setting
|
|
if(zone_script)
|
|
{
|
|
if(lua_interface->RunZoneScriptWithReturn(zone_script, "loot_criteria_racial", spawn->GetZone(), spawn, loot->table_id, loot->minLevel, loot->maxLevel, &returnValue) && returnValue == 0)
|
|
continue;
|
|
}
|
|
bool entryAdded = false;
|
|
if (loot->minLevel == 0 && loot->maxLevel == 0 && (!loot->loot_tier || spawn->GetLootTier() >= loot->loot_tier) && (entryAdded = true)) // successful plan to add set entryAdded to true
|
|
ret.push_back(loot->table_id);
|
|
else {
|
|
if (spawn_level >= loot->minLevel && spawn_level <= loot->maxLevel && (!loot->loot_tier || spawn->GetLootTier() >= loot->loot_tier) && (entryAdded = true)) // successful plan to add set entryAdded to true
|
|
ret.push_back(loot->table_id);
|
|
}
|
|
|
|
if(!entryAdded && returnValue) // DB override via LUA scripting
|
|
ret.push_back(loot->table_id);
|
|
}
|
|
}
|
|
|
|
if (zone_loot_list.count(zone_id) > 0) {
|
|
vector<GlobalLoot*>::iterator itr;
|
|
for (itr = zone_loot_list[zone_id].begin(); itr != zone_loot_list[zone_id].end(); itr++) {
|
|
GlobalLoot* loot = *itr;
|
|
const char* zone_script = world.GetZoneScript(this->GetZoneID());
|
|
returnValue = 0; // reset since this can override the database setting
|
|
if(zone_script)
|
|
{
|
|
if(lua_interface->RunZoneScriptWithReturn(zone_script, "loot_criteria_zone", spawn->GetZone(), spawn, loot->table_id, loot->minLevel, loot->maxLevel, &returnValue) && returnValue == 0)
|
|
continue;
|
|
}
|
|
bool entryAdded = false;
|
|
if (loot->minLevel == 0 && loot->maxLevel == 0 && (!loot->loot_tier || spawn->GetLootTier() >= loot->loot_tier) && (entryAdded = true)) // successful plan to add set entryAdded to true
|
|
ret.push_back(loot->table_id);
|
|
else {
|
|
if (spawn_level >= loot->minLevel && spawn_level <= loot->maxLevel && (!loot->loot_tier || spawn->GetLootTier() >= loot->loot_tier) && (entryAdded = true)) // successful plan to add set entryAdded to true
|
|
ret.push_back(loot->table_id);
|
|
}
|
|
|
|
if(!entryAdded && returnValue) // DB override via LUA scripting
|
|
ret.push_back(loot->table_id);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
vector<LootDrop*>* ZoneServer::GetLootDrops(int32 table_id){
|
|
if(!reloading && loot_drops.count(table_id) > 0)
|
|
return &(loot_drops[table_id]);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
LootTable* ZoneServer::GetLootTable(int32 table_id){
|
|
return loot_tables[table_id];
|
|
}
|
|
|
|
void ZoneServer::AddLocationTransporter(int32 zone_id, string message, float trigger_x, float trigger_y, float trigger_z, float trigger_radius, int32 destination_zone_id, float destination_x, float destination_y, float destination_z, float destination_heading, int32 cost, int32 unique_id){
|
|
LocationTransportDestination* loc = new LocationTransportDestination;
|
|
loc->message = message;
|
|
loc->trigger_x = trigger_x;
|
|
loc->trigger_y = trigger_y;
|
|
loc->trigger_z = trigger_z;
|
|
loc->trigger_radius = trigger_radius;
|
|
loc->destination_zone_id = destination_zone_id;
|
|
loc->destination_x = destination_x;
|
|
loc->destination_y = destination_y;
|
|
loc->destination_z = destination_z;
|
|
loc->destination_heading = destination_heading;
|
|
loc->cost = cost;
|
|
loc->unique_id = unique_id;
|
|
MTransporters.lock();
|
|
if(location_transporters.count(zone_id) == 0)
|
|
location_transporters[zone_id] = new MutexList<LocationTransportDestination*>();
|
|
location_transporters[zone_id]->Add(loc);
|
|
MTransporters.unlock();
|
|
}
|
|
|
|
void ZoneServer::AddTransporter(int32 transport_id, int8 type, string name, string message, int32 destination_zone_id, float destination_x, float destination_y, float destination_z, float destination_heading,
|
|
int32 cost, int32 unique_id, int8 min_level, int8 max_level, int32 quest_req, int16 quest_step_req, int32 quest_complete, int32 map_x, int32 map_y, int32 expansion_flag, int32 holiday_flag, int32 min_client_version,
|
|
int32 max_client_version, int32 flight_path_id, int16 mount_id, int8 mount_red_color, int8 mount_green_color, int8 mount_blue_color){
|
|
TransportDestination* transport = new TransportDestination;
|
|
transport->type = type;
|
|
transport->display_name = name;
|
|
transport->message = message;
|
|
transport->destination_zone_id = destination_zone_id;
|
|
transport->destination_x = destination_x;
|
|
transport->destination_y = destination_y;
|
|
transport->destination_z = destination_z;
|
|
transport->destination_heading = destination_heading;
|
|
transport->cost = cost;
|
|
transport->unique_id = unique_id;
|
|
|
|
transport->min_level = min_level;
|
|
transport->max_level = max_level;
|
|
transport->req_quest = quest_req;
|
|
transport->req_quest_step = quest_step_req;
|
|
transport->req_quest_complete = quest_complete;
|
|
|
|
transport->map_x = map_x;
|
|
transport->map_y = map_y;
|
|
|
|
transport->expansion_flag = expansion_flag;
|
|
transport->holiday_flag = holiday_flag;
|
|
|
|
transport->min_client_version = min_client_version;
|
|
transport->max_client_version = max_client_version;
|
|
|
|
transport->flight_path_id = flight_path_id;
|
|
|
|
transport->mount_id = mount_id;
|
|
transport->mount_red_color = mount_red_color;
|
|
transport->mount_green_color = mount_green_color;
|
|
transport->mount_blue_color = mount_blue_color;
|
|
|
|
MTransporters.lock();
|
|
transporters[transport_id].push_back(transport);
|
|
MTransporters.unlock();
|
|
}
|
|
|
|
void ZoneServer::GetTransporters(vector<TransportDestination*>* returnList, Client* client, int32 transport_id){
|
|
if (!returnList)
|
|
return;
|
|
|
|
MTransporters.lock();
|
|
if (transporters.count(transport_id) > 0)
|
|
{
|
|
vector<TransportDestination*> list;
|
|
for (int i = 0; i < transporters[transport_id].size(); i++)
|
|
{
|
|
if (transporters[transport_id][i]->min_client_version && client->GetVersion() < transporters[transport_id][i]->min_client_version)
|
|
continue;
|
|
else if (transporters[transport_id][i]->max_client_version && client->GetVersion() > transporters[transport_id][i]->max_client_version)
|
|
continue;
|
|
|
|
if (database.CheckExpansionFlags(this, transporters[transport_id][i]->expansion_flag) && database.CheckHolidayFlags(this, transporters[transport_id][i]->holiday_flag))
|
|
{
|
|
returnList->push_back(transporters[transport_id][i]);
|
|
}
|
|
}
|
|
}
|
|
MTransporters.unlock();
|
|
}
|
|
|
|
MutexList<LocationTransportDestination*>* ZoneServer::GetLocationTransporters(int32 zone_id){
|
|
MutexList<LocationTransportDestination*>* ret = 0;
|
|
MTransporters.lock();
|
|
if(location_transporters.count(zone_id) > 0)
|
|
ret = location_transporters[zone_id];
|
|
MTransporters.unlock();
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::DeleteGlobalTransporters(){
|
|
MTransporters.lock();
|
|
map<int32, vector<TransportDestination*> >::iterator itr;
|
|
vector<TransportDestination*>::iterator transport_vector_itr;
|
|
for(itr = transporters.begin(); itr != transporters.end(); itr++){
|
|
for(transport_vector_itr = itr->second.begin(); transport_vector_itr != itr->second.end(); transport_vector_itr++){
|
|
safe_delete(*transport_vector_itr);
|
|
}
|
|
}
|
|
map<int32, MutexList<LocationTransportDestination*>* >::iterator itr2;
|
|
for(itr2 = location_transporters.begin(); itr2 != location_transporters.end(); itr2++){
|
|
itr2->second->clear(true);
|
|
delete itr2->second;
|
|
}
|
|
transporters.clear();
|
|
location_transporters.clear();
|
|
MTransporters.unlock();
|
|
}
|
|
|
|
void ZoneServer::DeleteGlobalSpawns() {
|
|
ClearLootTables();
|
|
|
|
map<int32, NPC*>::iterator npc_list_iter;
|
|
for(npc_list_iter=npc_list.begin();npc_list_iter!=npc_list.end();npc_list_iter++) {
|
|
safe_delete(npc_list_iter->second);
|
|
}
|
|
npc_list.clear();
|
|
map<int32, Object*>::iterator object_list_iter;
|
|
for(object_list_iter=object_list.begin();object_list_iter!=object_list.end();object_list_iter++) {
|
|
safe_delete(object_list_iter->second);
|
|
}
|
|
object_list.clear();
|
|
map<int32, GroundSpawn*>::iterator groundspawn_list_iter;
|
|
for(groundspawn_list_iter=groundspawn_list.begin();groundspawn_list_iter!=groundspawn_list.end();groundspawn_list_iter++) {
|
|
safe_delete(groundspawn_list_iter->second);
|
|
}
|
|
groundspawn_list.clear();
|
|
map<int32, Widget*>::iterator widget_list_iter;
|
|
for(widget_list_iter=widget_list.begin();widget_list_iter!=widget_list.end();widget_list_iter++) {
|
|
safe_delete(widget_list_iter->second);
|
|
}
|
|
widget_list.clear();
|
|
map<int32, Sign*>::iterator sign_list_iter;
|
|
for(sign_list_iter=sign_list.begin();sign_list_iter!=sign_list.end();sign_list_iter++) {
|
|
safe_delete(sign_list_iter->second);
|
|
}
|
|
sign_list.clear();
|
|
|
|
/*map<int32, AppearanceData*>::iterator appearance_list_iter;
|
|
for(appearance_list_iter=npc_appearance_list.begin();appearance_list_iter!=npc_appearance_list.end();appearance_list_iter++) {
|
|
safe_delete(appearance_list_iter->second);
|
|
}
|
|
npc_appearance_list.clear();*/
|
|
|
|
|
|
ClearEntityCommands();
|
|
|
|
DeleteGroundSpawnItems();
|
|
DeleteGlobalTransporters();
|
|
DeleteTransporterMaps();
|
|
}
|
|
|
|
void ZoneServer::AddTransportMap(int32 id, string name) {
|
|
MTransportMaps.writelock(__FUNCTION__, __LINE__);
|
|
m_transportMaps[id] = name;
|
|
MTransportMaps.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
bool ZoneServer::TransportHasMap(int32 id) {
|
|
bool ret = false;
|
|
|
|
MTransportMaps.readlock(__FUNCTION__, __LINE__);
|
|
ret = m_transportMaps.count(id) > 0;
|
|
MTransportMaps.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
string ZoneServer::GetTransportMap(int32 id) {
|
|
string ret;
|
|
|
|
MTransportMaps.readlock(__FUNCTION__, __LINE__);
|
|
if (m_transportMaps.count(id) > 0)
|
|
ret = m_transportMaps[id];
|
|
MTransportMaps.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ZoneServer::DeleteTransporterMaps() {
|
|
MTransportMaps.writelock(__FUNCTION__, __LINE__);
|
|
m_transportMaps.clear();
|
|
MTransportMaps.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::ReloadSpawns() {
|
|
if (reloading)
|
|
return;
|
|
|
|
reloading = true;
|
|
world.SetReloadingSubsystem("Spawns");
|
|
// Let every one in the zone know what is happening
|
|
HandleBroadcast("Reloading all spawns for this zone.");
|
|
DeleteGlobalSpawns();
|
|
Depop(false, true);
|
|
}
|
|
|
|
void ZoneServer::SendStateCommand(Spawn* spawn, int32 state) {
|
|
vector<Client*>::iterator itr;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
Client* client = *itr;
|
|
if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()))
|
|
ClientPacketFunctions::SendStateCommand(client, client->GetPlayer()->GetIDWithPlayerSpawn(spawn), state);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::AddFlightPath(int32 id, FlightPathInfo* info) {
|
|
if (m_flightPaths.count(id) > 0) {
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "Duplicate flight path (%u)", id);
|
|
safe_delete(info);
|
|
return;
|
|
}
|
|
|
|
m_flightPaths[id] = info;
|
|
}
|
|
|
|
void ZoneServer::AddFlightPathLocation(int32 id, FlightPathLocation* location) {
|
|
if (m_flightPaths.count(id) == 0) {
|
|
LogWrite(ZONE__ERROR, 0, "Zone", "There is no flight info for this route (%u)", id);
|
|
safe_delete(location);
|
|
return;
|
|
}
|
|
|
|
m_flightPathRoutes[id].push_back(location);
|
|
}
|
|
|
|
void ZoneServer::DeleteFlightPaths() {
|
|
map<int32, vector<FlightPathLocation*> >::iterator itr;
|
|
vector<FlightPathLocation*>::iterator itr2;
|
|
map<int32, FlightPathInfo*>::iterator itr3;
|
|
|
|
for (itr = m_flightPathRoutes.begin(); itr != m_flightPathRoutes.end(); itr++) {
|
|
for (itr2 = itr->second.begin(); itr2 != itr->second.end(); itr2++) {
|
|
safe_delete(*itr2);
|
|
}
|
|
|
|
itr->second.clear();
|
|
}
|
|
m_flightPathRoutes.clear();
|
|
|
|
for (itr3 = m_flightPaths.begin(); itr3 != m_flightPaths.end(); itr3++) {
|
|
safe_delete(itr3->second);
|
|
}
|
|
m_flightPaths.clear();
|
|
}
|
|
|
|
void ZoneServer::SendFlightPathsPackets(Client* client) {
|
|
// Only send a packet if there are flight paths
|
|
if (m_flightPathRoutes.size() > 0) {
|
|
PacketStruct* packet = configReader.getStruct("WS_FlightPathsMsg", client->GetVersion());
|
|
if (packet) {
|
|
int32 num_routes = m_flightPaths.size();
|
|
packet->setArrayLengthByName("number_of_routes", num_routes);
|
|
packet->setArrayLengthByName("number_of_routes2", num_routes);
|
|
packet->setArrayLengthByName("number_of_routes3", num_routes);
|
|
packet->setArrayLengthByName("number_of_routes4", num_routes);
|
|
map<int32, FlightPathInfo*>::iterator itr;
|
|
int32 i = 0;
|
|
for (itr = m_flightPaths.begin(); itr != m_flightPaths.end(); itr++, i++) {
|
|
packet->setArrayDataByName("route_length", m_flightPathRoutes[itr->first].size(), i);
|
|
packet->setArrayDataByName("ground_mount", itr->second->flying ? 0 : 1, i);
|
|
packet->setArrayDataByName("allow_dismount", itr->second->dismount ? 1 : 0, i);
|
|
packet->setSubArrayLengthByName("route_length2", m_flightPathRoutes[itr->first].size(), i);
|
|
vector<FlightPathLocation*>::iterator itr2;
|
|
int32 j = 0;
|
|
for (itr2 = m_flightPathRoutes[itr->first].begin(); itr2 != m_flightPathRoutes[itr->first].end(); itr2++, j++) {
|
|
packet->setSubArrayDataByName("x", (*itr2)->X, i, j);
|
|
packet->setSubArrayDataByName("y", (*itr2)->Y, i, j);
|
|
packet->setSubArrayDataByName("z", (*itr2)->Z, i, j);
|
|
}
|
|
}
|
|
client->QueuePacket(packet->serialize());
|
|
safe_delete(packet);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 ZoneServer::GetFlightPathIndex(int32 id) {
|
|
int32 index = 0;
|
|
map<int32, FlightPathInfo*>::iterator itr;
|
|
for (itr = m_flightPaths.begin(); itr != m_flightPaths.end(); itr++, index++) {
|
|
if (itr->first == id)
|
|
return index;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
float ZoneServer::GetFlightPathSpeed(int32 id) {
|
|
float speed = 1;
|
|
|
|
if (m_flightPaths.count(id) > 0)
|
|
speed = m_flightPaths[id]->speed;
|
|
|
|
return speed;
|
|
}
|
|
|
|
void ZoneServer::ProcessSpawnConditional(int8 condition) {
|
|
MSpawnLocationList.readlock(__FUNCTION__, __LINE__);
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
map<int32, Spawn*>::iterator itr;
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
if (itr->second != NULL) // null itr->second still coming into ProcessSpawnConditional
|
|
{
|
|
SpawnLocation* loc = spawn_location_list[itr->second->GetSpawnLocationID()];
|
|
if (loc && loc->conditional > 0) {
|
|
if ((loc->conditional & condition) != condition) {
|
|
Despawn(itr->second, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
map<int32, SpawnLocation*>::iterator itr2;
|
|
for (itr2 = spawn_location_list.begin(); itr2 != spawn_location_list.end(); itr2++) {
|
|
SpawnLocation* loc = itr2->second;
|
|
if (loc && loc->conditional > 0 && ((loc->conditional & condition) == condition))
|
|
if (GetSpawnByLocationID(loc->placement_id) == NULL)
|
|
ProcessSpawnLocation(loc, nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
}
|
|
|
|
MSpawnLocationList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::AddSpawnProximities(Spawn* newSpawn) {
|
|
Spawn* spawn = 0;
|
|
map<int32, Spawn*>::iterator itr;
|
|
MPendingSpawnListAdd.readlock(__FUNCTION__, __LINE__);
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if (spawn && spawn != newSpawn) {
|
|
if (newSpawn->GetDatabaseID())
|
|
spawn->AddSpawnToProximity(newSpawn->GetDatabaseID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_DATABASE_ID);
|
|
if (newSpawn->GetSpawnLocationID())
|
|
spawn->AddSpawnToProximity(newSpawn->GetSpawnLocationID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_LOCATION_ID);
|
|
|
|
if (spawn->GetDatabaseID())
|
|
newSpawn->AddSpawnToProximity(spawn->GetDatabaseID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_DATABASE_ID);
|
|
if (spawn->GetSpawnLocationID())
|
|
newSpawn->AddSpawnToProximity(spawn->GetSpawnLocationID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_LOCATION_ID);
|
|
}
|
|
}
|
|
|
|
list<Spawn*>::iterator itr2;
|
|
for (itr2 = pending_spawn_list_add.begin(); itr2 != pending_spawn_list_add.end(); itr2++) {
|
|
spawn = *itr2;
|
|
if (spawn && spawn != newSpawn) {
|
|
if (newSpawn->GetDatabaseID())
|
|
spawn->AddSpawnToProximity(newSpawn->GetDatabaseID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_DATABASE_ID);
|
|
if (newSpawn->GetSpawnLocationID())
|
|
spawn->AddSpawnToProximity(newSpawn->GetSpawnLocationID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_LOCATION_ID);
|
|
|
|
if (spawn->GetDatabaseID())
|
|
newSpawn->AddSpawnToProximity(spawn->GetDatabaseID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_DATABASE_ID);
|
|
if (spawn->GetSpawnLocationID())
|
|
newSpawn->AddSpawnToProximity(spawn->GetSpawnLocationID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_LOCATION_ID);
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
MPendingSpawnListAdd.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
// we only call this inside a write lock
|
|
void ZoneServer::RemoveSpawnProximities(Spawn* oldSpawn) {
|
|
Spawn* spawn = 0;
|
|
map<int32, Spawn*>::iterator itr;
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if (spawn && spawn != oldSpawn) {
|
|
if (oldSpawn->GetDatabaseID())
|
|
spawn->RemoveSpawnFromProximity(oldSpawn->GetDatabaseID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_DATABASE_ID);
|
|
if (oldSpawn->GetSpawnLocationID())
|
|
spawn->RemoveSpawnFromProximity(oldSpawn->GetSpawnLocationID(), Spawn::SpawnProximityType::SPAWNPROXIMITY_LOCATION_ID);
|
|
|
|
// don't need to remove oldSpawn proximities, we clear them all out
|
|
}
|
|
}
|
|
}
|
|
|
|
void ZoneServer::SetSpawnScript(SpawnEntry* entry, Spawn* spawn)
|
|
{
|
|
if (!entry || !spawn)
|
|
return;
|
|
|
|
const char* script = 0;
|
|
|
|
for (int x = 0; x < 3; x++)
|
|
{
|
|
switch (x)
|
|
{
|
|
case 0:
|
|
script = world.GetSpawnEntryScript(entry->spawn_entry_id);
|
|
break;
|
|
case 1:
|
|
script = world.GetSpawnLocationScript(entry->spawn_location_id);
|
|
break;
|
|
case 2:
|
|
script = world.GetSpawnScript(entry->spawn_id);
|
|
break;
|
|
}
|
|
|
|
if (script && lua_interface && lua_interface->GetSpawnScript(script) != 0)
|
|
{
|
|
spawn->SetSpawnScript(string(script));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
vector<HouseItem> ZoneServer::GetHouseItems(Client* client)
|
|
{
|
|
if (!client->GetCurrentZone()->GetInstanceID() || !client->HasOwnerOrEditAccess())
|
|
return std::vector<HouseItem>();
|
|
|
|
PacketStruct* packet = configReader.getStruct("WS_HouseItemsList", client->GetVersion());
|
|
|
|
std::vector<HouseItem> items;
|
|
map<int32, Spawn*>::iterator itr;
|
|
Spawn* spawn = 0;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if (spawn && spawn->IsObject() && spawn->GetPickupItemID())
|
|
{
|
|
HouseItem tmpItem;
|
|
tmpItem.item_id = spawn->GetPickupItemID();
|
|
tmpItem.unique_id = spawn->GetPickupUniqueItemID();
|
|
tmpItem.spawn_id = spawn->GetID();
|
|
tmpItem.item = master_item_list.GetItem(spawn->GetPickupItemID());
|
|
|
|
if (!tmpItem.item)
|
|
continue;
|
|
|
|
items.push_back(tmpItem);
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
|
|
return items;
|
|
}
|
|
|
|
void ZoneServer::SendHouseItems(Client* client)
|
|
{
|
|
if (!client->GetCurrentZone()->GetInstanceID() || !client->HasOwnerOrEditAccess())
|
|
return;
|
|
|
|
PacketStruct* packet = configReader.getStruct("WS_HouseItemsList", client->GetVersion());
|
|
|
|
if(!packet) {
|
|
return;
|
|
}
|
|
|
|
std::vector<HouseItem> items = GetHouseItems(client);
|
|
|
|
// setting this to 1 puts it on the door widget
|
|
packet->setDataByName("is_widget_door", 1);
|
|
packet->setArrayLengthByName("num_items", items.size());
|
|
for (int i = 0; i < items.size(); i++)
|
|
{
|
|
HouseItem tmpItem = items[i];
|
|
packet->setArrayDataByName("unique_id", tmpItem.unique_id, i); // unique_id is in fact the item_id...
|
|
packet->setArrayDataByName("item_name", tmpItem.item->name.c_str(), i);
|
|
packet->setArrayDataByName("status_reduction", tmpItem.item->houseitem_info->status_rent_reduction, i);
|
|
|
|
// location, 0 = floor, 1 = ceiling
|
|
//packet->setArrayDataByName("location", 1, i, 0);
|
|
|
|
// item_state int8
|
|
// 0 = normal (cannot pick up item / move item / toggle visibility)
|
|
// 1 = virtual (toggle visibility available, no move item)
|
|
// 2 = hidden (cannot pick up item / move item / toggle visibility)
|
|
// 3 = virtual/hidden/toggle visibility
|
|
// 4 = none (cannot pick up item / move item / toggle visibility)
|
|
// 5 = none, toggle visibility (cannot pick up item / move item)
|
|
// 8 = none (cannot pick up item / move item / toggle visibility)
|
|
//packet->setArrayDataByName("item_state", tmpvalue, i, 0);
|
|
|
|
// makes it so we don't have access to move item/retrieve item
|
|
// cannot use in conjunction with ui_tab_flag1/ui_tab_flag2
|
|
//packet->setArrayDataByName("tradeable", 1, i);
|
|
//packet->setArrayDataByName("item_description", "failboat", i);
|
|
|
|
// access to move item/retrieve item, do not use in conjunction with tradeable
|
|
packet->setArrayDataByName("ui_tab_flag1", 1, i, 0);
|
|
packet->setArrayDataByName("ui_tab_flag2", 1, i, 0);
|
|
|
|
// both of these can serve as description fields (only one should be used they populate the same area below the item name)
|
|
//packet->setArrayDataByName("first_item_description", "test", i);
|
|
//packet->setArrayDataByName("second_item_description", "Description here!", i);
|
|
|
|
packet->setArrayDataByName("icon", tmpItem.item->GetIcon(client->GetVersion()), i);
|
|
}
|
|
|
|
EQ2Packet* pack = packet->serialize();
|
|
client->QueuePacket(pack);
|
|
safe_delete(packet);
|
|
}
|
|
|
|
Spawn* ZoneServer::GetSpawnFromUniqueItemID(int32 unique_id)
|
|
{
|
|
if (!GetInstanceID() || GetInstanceType() != Instance_Type::PERSONAL_HOUSE_INSTANCE)
|
|
return nullptr;
|
|
|
|
map<int32, Spawn*>::iterator itr;
|
|
Spawn* spawn = 0;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = spawn_list.begin(); itr != spawn_list.end(); itr++) {
|
|
spawn = itr->second;
|
|
if (spawn && spawn->IsObject() && spawn->GetPickupUniqueItemID() == unique_id)
|
|
{
|
|
Spawn* tmpSpawn = spawn;
|
|
MSpawnList.releasereadlock();
|
|
return tmpSpawn;
|
|
}
|
|
}
|
|
MSpawnList.releasereadlock();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void ZoneServer::AddPendingSpawnRemove(int32 id)
|
|
{
|
|
MPendingSpawnRemoval.writelock(__FUNCTION__, __LINE__);
|
|
m_pendingSpawnRemove.insert(make_pair(id,true));
|
|
MPendingSpawnRemoval.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::ProcessSpawnRemovals()
|
|
{
|
|
MSpawnList.writelock(__FUNCTION__, __LINE__);
|
|
MPendingSpawnRemoval.writelock(__FUNCTION__, __LINE__);
|
|
if (m_pendingSpawnRemove.size() > 0) {
|
|
map<int32,bool>::iterator itr2;
|
|
for (itr2 = m_pendingSpawnRemove.begin(); itr2 != m_pendingSpawnRemove.end(); itr2++) {
|
|
spawn_list.erase(itr2->first);
|
|
subspawn_list[SUBSPAWN_TYPES::COLLECTOR].erase(itr2->first);
|
|
|
|
std::map<int32,int32>::iterator hsmitr = housing_spawn_map.find(itr2->first);
|
|
if(hsmitr != housing_spawn_map.end()) {
|
|
subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].erase(hsmitr->second);
|
|
housing_spawn_map.erase(hsmitr);
|
|
}
|
|
}
|
|
|
|
m_pendingSpawnRemove.clear();
|
|
}
|
|
MPendingSpawnRemoval.releasewritelock(__FUNCTION__, __LINE__);
|
|
MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::AddSpawnToGroup(Spawn* spawn, int32 group_id)
|
|
{
|
|
if( spawn->GetSpawnGroupID() > 0 )
|
|
spawn->RemoveSpawnFromGroup();
|
|
MutexList<int32>* groupList = &spawn_group_map.Get(group_id);
|
|
MutexList<int32>::iterator itr2 = groupList->begin();
|
|
|
|
while(itr2.Next())
|
|
{
|
|
Spawn* groupSpawn = GetSpawnByID(itr2.value);
|
|
if(groupSpawn)
|
|
{
|
|
// found existing group member to add it in
|
|
spawn->AddSpawnToGroup(groupSpawn);
|
|
break;
|
|
}
|
|
}
|
|
groupList->Add(spawn->GetID());
|
|
spawn->SetSpawnGroupID(group_id);
|
|
}
|
|
|
|
void ZoneServer::QueueStateCommandToClients(int32 spawn_id, int32 state)
|
|
{
|
|
if(spawn_id < 1)
|
|
return;
|
|
|
|
MLuaQueueStateCmd.lock();
|
|
lua_queued_state_commands.insert(make_pair(spawn_id, state));
|
|
MLuaQueueStateCmd.unlock();
|
|
}
|
|
|
|
void ZoneServer::QueueDefaultCommand(int32 spawn_id, std::string command, float distance)
|
|
{
|
|
if(spawn_id < 1)
|
|
return;
|
|
|
|
MLuaQueueStateCmd.lock();
|
|
lua_spawn_update_command[spawn_id].insert(make_pair(command,distance));
|
|
MLuaQueueStateCmd.unlock();
|
|
}
|
|
|
|
void ZoneServer::ProcessQueuedStateCommands() // in a client list lock only
|
|
{
|
|
vector<Client*>::iterator itr;
|
|
|
|
MLuaQueueStateCmd.lock();
|
|
|
|
if(lua_queued_state_commands.size() > 0)
|
|
{
|
|
std::map<int32, int32>::iterator statecmds;
|
|
for(statecmds = lua_queued_state_commands.begin(); statecmds != lua_queued_state_commands.end(); statecmds++)
|
|
{
|
|
Spawn* spawn = GetSpawnByID(statecmds->first, false);
|
|
if(!spawn)
|
|
continue;
|
|
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
Client* client = *itr;
|
|
if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()))
|
|
ClientPacketFunctions::SendStateCommand(client, client->GetPlayer()->GetIDWithPlayerSpawn(spawn), statecmds->second);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
lua_queued_state_commands.clear();
|
|
}
|
|
|
|
if(lua_spawn_update_command.size() > 0)
|
|
{
|
|
std::map<int32, std::map<std::string,float>>::iterator updatecmds;
|
|
for(updatecmds = lua_spawn_update_command.begin(); updatecmds != lua_spawn_update_command.end(); updatecmds++)
|
|
{
|
|
Spawn* spawn = GetSpawnByID(updatecmds->first, false);
|
|
if(!spawn)
|
|
continue;
|
|
|
|
std::map<std::string,float>::iterator innermap;
|
|
for(innermap = lua_spawn_update_command[updatecmds->first].begin(); innermap != lua_spawn_update_command[updatecmds->first].end(); innermap++)
|
|
{
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
Client* client = *itr;
|
|
if (client && client->GetPlayer()->WasSentSpawn(spawn->GetID()))
|
|
client->SendDefaultCommand(spawn, innermap->first.c_str(), innermap->second);
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
lua_spawn_update_command[updatecmds->first].clear();
|
|
}
|
|
lua_spawn_update_command.clear();
|
|
}
|
|
MLuaQueueStateCmd.unlock();
|
|
}
|
|
|
|
void ZoneServer::RemoveClientsFromZone(ZoneServer* zone) {
|
|
vector<Client*>::iterator itr;
|
|
MClientList.readlock(__FUNCTION__, __LINE__);
|
|
for (itr = clients.begin(); itr != clients.end(); itr++) {
|
|
Client* client = *itr;
|
|
if(client->GetCurrentZone() == zone) {
|
|
client->SetCurrentZone(nullptr);
|
|
}
|
|
if(client->GetZoningDestination() == zone) {
|
|
client->SetZoningDestination(nullptr);
|
|
}
|
|
}
|
|
MClientList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
void ZoneServer::SendSubSpawnUpdates(SUBSPAWN_TYPES subtype) {
|
|
std::map<int32, Spawn*>::iterator subitr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
for(subitr = subspawn_list[subtype].begin(); subitr != subspawn_list[subtype].end(); subitr++) {
|
|
subitr->second->changed = true;
|
|
subitr->second->info_changed = true;
|
|
AddChangedSpawn(subitr->second);
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
}
|
|
|
|
bool ZoneServer::HouseItemSpawnExists(int32 item_id) {
|
|
bool exists = false;
|
|
std::map<int32, Spawn*>::iterator subitr;
|
|
MSpawnList.readlock(__FUNCTION__, __LINE__);
|
|
subitr = subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].find(item_id);
|
|
if(subitr != subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].end()) {
|
|
exists = true;
|
|
}
|
|
MSpawnList.releasereadlock(__FUNCTION__, __LINE__);
|
|
return exists;
|
|
}
|
|
|
|
void ZoneServer::ProcessPendingSpawns() {
|
|
MPendingSpawnListAdd.writelock(__FUNCTION__, __LINE__);
|
|
list<Spawn*>::iterator itr2;
|
|
for (itr2 = pending_spawn_list_add.begin(); itr2 != pending_spawn_list_add.end(); itr2++) {
|
|
Spawn* spawn = *itr2;
|
|
|
|
MSpawnList.writelock(__FUNCTION__, __LINE__);
|
|
if (spawn)
|
|
spawn_list[spawn->GetID()] = spawn;
|
|
|
|
if(spawn->IsCollector()) {
|
|
subspawn_list[SUBSPAWN_TYPES::COLLECTOR].insert(make_pair(spawn->GetID(),spawn));
|
|
}
|
|
if(spawn->GetPickupItemID()) {
|
|
subspawn_list[SUBSPAWN_TYPES::HOUSE_ITEM_SPAWN].insert(make_pair(spawn->GetPickupItemID(),spawn));
|
|
housing_spawn_map.insert(make_pair(spawn->GetID(), spawn->GetPickupItemID()));
|
|
}
|
|
MSpawnList.releasewritelock(__FUNCTION__, __LINE__);
|
|
|
|
CheckSpawnRange(spawn);
|
|
}
|
|
|
|
pending_spawn_list_add.clear();
|
|
MPendingSpawnListAdd.releasewritelock(__FUNCTION__, __LINE__);
|
|
spawn_check_add.Trigger();
|
|
}
|
|
|
|
void ZoneServer::AddSpawnToGrid(Spawn* spawn, int32 grid_id) {
|
|
if(spawn->GetID() == 0 || spawn->IsDeletedSpawn())
|
|
return;
|
|
|
|
MGridMaps.lock_shared();
|
|
std::map<int32, GridMap*>::iterator grids = grid_maps.find(grid_id);
|
|
if(grids != grid_maps.end()) {
|
|
grids->second->MSpawns.lock();
|
|
grids->second->spawns.insert(make_pair(spawn->GetID(), spawn));
|
|
grids->second->MSpawns.unlock();
|
|
}
|
|
else {
|
|
MGridMaps.unlock_shared();
|
|
MGridMaps.lock();
|
|
GridMap* gm = new GridMap;
|
|
gm->grid_id = grid_id;
|
|
gm->spawns.insert(make_pair(spawn->GetID(), spawn));
|
|
grid_maps.insert(make_pair(grid_id, gm));
|
|
MGridMaps.unlock();
|
|
return;
|
|
}
|
|
|
|
MGridMaps.unlock_shared();
|
|
}
|
|
|
|
void ZoneServer::RemoveSpawnFromGrid(Spawn* spawn, int32 grid_id) {
|
|
std::shared_lock lock(MGridMaps);
|
|
std::map<int32, GridMap*>::iterator grids = grid_maps.find(grid_id);
|
|
if(grids != grid_maps.end()) {
|
|
grids->second->MSpawns.lock();
|
|
if(grids->second->spawns.count(spawn->GetID()) > 0) {
|
|
grids->second->spawns.erase(spawn->GetID());
|
|
}
|
|
grids->second->MSpawns.unlock();
|
|
}
|
|
}
|
|
|
|
int32 ZoneServer::GetSpawnCountInGrid(int32 grid_id) {
|
|
int32 count = 0;
|
|
std::shared_lock lock(MGridMaps);
|
|
std::map<int32, GridMap*>::iterator grids = grid_maps.find(grid_id);
|
|
if(grids != grid_maps.end()) {
|
|
grids->second->MSpawns.lock_shared();
|
|
count = grids->second->spawns.size();
|
|
grids->second->MSpawns.unlock_shared();
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
void ZoneServer::SendClientSpawnListInGrid(Client* client, int32 grid_id){
|
|
std::shared_lock lock(MGridMaps);
|
|
|
|
Spawn* spawn = nullptr;
|
|
client->Message(CHANNEL_COLOR_RED, "Grid ID %u has %u spawns.", grid_id, GetSpawnCountInGrid(grid_id));
|
|
std::map<int32, GridMap*>::iterator grids = grid_maps.find(grid_id);
|
|
if(grids != grid_maps.end()) {
|
|
grids->second->MSpawns.lock_shared();
|
|
typedef map <int32, Spawn*> SpawnMapType;
|
|
for( SpawnMapType::iterator it = grids->second->spawns.begin(); it != grids->second->spawns.end(); ++it ) {
|
|
spawn = it->second;
|
|
client->Message(CHANNEL_COLOR_YELLOW, "Spawn %s (%u), Loc X/Y/Z: %f/%f/%f.", spawn->GetName(), spawn->GetID(), spawn->GetX(), spawn->GetY(), spawn->GetZ());
|
|
}
|
|
grids->second->MSpawns.unlock_shared();
|
|
}
|
|
}
|
|
|
|
void ZoneServer::AddIgnoredWidget(int32 id) {
|
|
std::unique_lock lock(MIgnoredWidgets);
|
|
if(ignored_widgets.find(id) == ignored_widgets.end()) {
|
|
ignored_widgets.insert(make_pair(id,true));
|
|
}
|
|
}
|
|
|
|
void ZoneServer::AddRespawn(Spawn* spawn) {
|
|
int32 respawn_time = spawn->GetRespawnTime();
|
|
if(spawn->GetRespawnOffsetLow() != 0 || spawn->GetRespawnOffsetHigh() != 0) {
|
|
int random_offset = MakeRandomInt(spawn->GetRespawnOffsetLow(), spawn->GetRespawnOffsetHigh());
|
|
int result_time = static_cast<int>(respawn_time) + random_offset;
|
|
respawn_time = static_cast<unsigned>(std::max(result_time, 0));
|
|
}
|
|
AddRespawn(spawn->GetSpawnLocationID(), respawn_time);
|
|
}
|
|
|
|
void ZoneServer::AddRespawn(int32 locationID, int32 respawnTime) {
|
|
if(locationID > 0 && respawnTime > 0) {
|
|
respawn_timers.Put(locationID, Timer::GetCurrentTime2() + respawnTime * 1000);
|
|
}
|
|
} |