zaela-Server/zone/entity.cpp

5184 lines
117 KiB
C++
Raw Permalink Normal View History

2013-05-09 10:44:08 -04:00
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net)
2013-02-16 16:14:39 -08:00
2013-05-09 10:44:08 -04:00
This program 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; version 2 of the License.
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
2013-02-16 16:14:39 -08:00
are required to give you total support for your newly bought product;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
2013-05-09 10:44:08 -04:00
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
2013-05-06 13:07:41 -04:00
2013-05-09 10:44:08 -04:00
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2013-02-16 16:14:39 -08:00
*/
#include "../common/global_define.h"
2013-02-16 16:14:39 -08:00
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <iostream>
#ifdef _WINDOWS
#include <process.h>
#else
#include <pthread.h>
#include "../common/unix.h"
#endif
#include "../common/features.h"
2013-02-16 16:14:39 -08:00
#include "../common/guilds.h"
2013-02-16 16:14:39 -08:00
#include "guild_mgr.h"
#include "petitions.h"
2014-08-21 23:46:01 -07:00
#include "quest_parser_collection.h"
#include "raids.h"
#include "string_ids.h"
#include "worldserver.h"
#include "water_map.h"
#include "npc_scale_manager.h"
#include "../common/say_link.h"
2013-02-16 16:14:39 -08:00
#ifdef _WINDOWS
#define snprintf _snprintf
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
2013-02-16 16:14:39 -08:00
#endif
2014-12-15 20:08:46 -05:00
#ifdef BOTS
#include "bot.h"
#endif
extern Zone *zone;
extern volatile bool is_zone_loaded;
2013-02-16 16:14:39 -08:00
extern WorldServer worldserver;
extern uint32 numclients;
extern PetitionList petition_list;
2013-05-09 10:44:08 -04:00
extern char errorname[32];
2013-02-16 16:14:39 -08:00
Entity::Entity()
{
id = 0;
initial_id = 0;
spawn_timestamp = time(nullptr);
2013-02-16 16:14:39 -08:00
}
Entity::~Entity()
{
2013-02-16 16:14:39 -08:00
}
Client *Entity::CastToClient()
{
if (this == 0x00) {
2019-09-01 22:05:44 -05:00
LogError("CastToClient error (nullptr)");
2013-02-16 16:14:39 -08:00
return 0;
}
#ifdef _EQDEBUG
if (!IsClient()) {
2019-09-01 22:05:44 -05:00
LogError("CastToClient error (not client)");
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<Client *>(this);
2013-02-16 16:14:39 -08:00
}
NPC *Entity::CastToNPC()
{
#ifdef _EQDEBUG
if (!IsNPC()) {
2019-09-01 22:05:44 -05:00
LogError("CastToNPC error (Not NPC)");
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<NPC *>(this);
2013-02-16 16:14:39 -08:00
}
Mob *Entity::CastToMob()
{
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
if (!IsMob()) {
std::cout << "CastToMob error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<Mob *>(this);
2013-02-16 16:14:39 -08:00
}
Merc *Entity::CastToMerc()
{
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
if (!IsMerc()) {
std::cout << "CastToMerc error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<Merc *>(this);
2013-02-16 16:14:39 -08:00
}
Trap *Entity::CastToTrap()
2013-02-16 16:14:39 -08:00
{
#ifdef DEBUG
if (!IsTrap()) {
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<Trap *>(this);
2013-02-16 16:14:39 -08:00
}
Corpse *Entity::CastToCorpse()
{
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
if (!IsCorpse()) {
std::cout << "CastToCorpse error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<Corpse *>(this);
2013-02-16 16:14:39 -08:00
}
Object *Entity::CastToObject()
{
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
if (!IsObject()) {
std::cout << "CastToObject error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<Object *>(this);
2013-02-16 16:14:39 -08:00
}
/*Group* Entity::CastToGroup() {
#ifdef _EQDEBUG
2013-05-06 13:07:41 -04:00
if(!IsGroup()) {
std::cout << "CastToGroup error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<Group*>(this);
}*/
Doors *Entity::CastToDoors()
{
return static_cast<Doors *>(this);
2013-02-16 16:14:39 -08:00
}
Beacon *Entity::CastToBeacon()
{
return static_cast<Beacon *>(this);
2013-02-16 16:14:39 -08:00
}
Encounter *Entity::CastToEncounter()
{
return static_cast<Encounter *>(this);
}
const Client *Entity::CastToClient() const
{
if (this == 0x00) {
std::cout << "CastToClient error (nullptr)" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#ifdef _EQDEBUG
if (!IsClient()) {
std::cout << "CastToClient error (not client?)" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<const Client *>(this);
2013-02-16 16:14:39 -08:00
}
const NPC *Entity::CastToNPC() const
{
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
if (!IsNPC()) {
std::cout << "CastToNPC error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<const NPC *>(this);
2013-02-16 16:14:39 -08:00
}
const Mob *Entity::CastToMob() const
{
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
if (!IsMob()) {
std::cout << "CastToMob error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<const Mob *>(this);
2013-02-16 16:14:39 -08:00
}
const Merc *Entity::CastToMerc() const
{
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
if (!IsMerc()) {
std::cout << "CastToMerc error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<const Merc *>(this);
2013-02-16 16:14:39 -08:00
}
const Trap *Entity::CastToTrap() const
{
2013-02-16 16:14:39 -08:00
#ifdef DEBUG
if (!IsTrap()) {
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<const Trap *>(this);
2013-02-16 16:14:39 -08:00
}
const Corpse *Entity::CastToCorpse() const
{
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
if (!IsCorpse()) {
std::cout << "CastToCorpse error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<const Corpse *>(this);
2013-02-16 16:14:39 -08:00
}
const Object *Entity::CastToObject() const
{
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
if (!IsObject()) {
std::cout << "CastToObject error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<const Object *>(this);
2013-02-16 16:14:39 -08:00
}
const Doors *Entity::CastToDoors() const
{
return static_cast<const Doors *>(this);
2013-02-16 16:14:39 -08:00
}
const Beacon* Entity::CastToBeacon() const
{
return static_cast<const Beacon *>(this);
2013-02-16 16:14:39 -08:00
}
const Encounter* Entity::CastToEncounter() const
{
return static_cast<const Encounter *>(this);
}
2013-02-16 16:14:39 -08:00
#ifdef BOTS
Bot *Entity::CastToBot()
{
2013-02-16 16:14:39 -08:00
#ifdef _EQDEBUG
if (!IsBot()) {
std::cout << "CastToBot error" << std::endl;
2013-02-16 16:14:39 -08:00
return 0;
}
#endif
return static_cast<Bot *>(this);
2013-02-16 16:14:39 -08:00
}
2019-06-21 06:00:58 -04:00
const Bot *Entity::CastToBot() const
{
#ifdef _EQDEBUG
if (!IsBot()) {
std::cout << "CastToBot error" << std::endl;
return 0;
}
#endif
return static_cast<const Bot *>(this);
}
2013-02-16 16:14:39 -08:00
#endif
EntityList::EntityList()
2019-10-13 18:49:16 -07:00
:
object_timer(5000),
door_timer(5000),
corpse_timer(2000),
group_timer(1000),
raid_timer(1000),
trap_timer(1000)
{
// set up ids between 1 and 1500
// neither client or server performs well if you have
// enough entities to exhaust this list
for (uint16 i = 1; i <= 1500; i++)
free_ids.push(i);
2013-02-16 16:14:39 -08:00
}
EntityList::~EntityList()
{
2013-02-16 16:14:39 -08:00
//must call this before the list is destroyed, or else it will try to
//delete the NPCs in the list, which it cannot do.
RemoveAllLocalities();
}
bool EntityList::CanAddHateForMob(Mob *p)
{
2013-05-09 10:44:08 -04:00
int count = 0;
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC *npc = it->second;
2013-05-09 10:44:08 -04:00
if (npc->IsOnHatelist(p))
count++;
// no need to continue if we already hit the limit
if (count > 3)
return false;
++it;
2013-05-09 10:44:08 -04:00
}
if (count <= 2)
return true;
return false;
2013-02-16 16:14:39 -08:00
}
void EntityList::AddClient(Client *client)
{
2013-02-16 16:14:39 -08:00
client->SetID(GetFreeID());
client_list.insert(std::pair<uint16, Client *>(client->GetID(), client));
mob_list.insert(std::pair<uint16, Mob *>(client->GetID(), client));
2013-02-16 16:14:39 -08:00
}
void EntityList::TrapProcess()
{
if (numclients < 1)
2013-02-16 16:14:39 -08:00
return;
if (trap_list.empty()) {
2019-10-13 18:49:16 -07:00
trap_timer.Disable();
return;
}
auto it = trap_list.begin();
while (it != trap_list.end()) {
if (!it->second->Process()) {
safe_delete(it->second);
free_ids.push(it->first);
it = trap_list.erase(it);
} else {
++it;
2013-02-16 16:14:39 -08:00
}
}
}
2013-05-04 18:06:58 -07:00
// Debug function -- checks to see if group_list has any nullptr entries.
2013-02-16 16:14:39 -08:00
// Meant to be called after each group-related function, in order
// to track down bugs.
void EntityList::CheckGroupList (const char *fname, const int fline)
{
std::list<Group *>::iterator it;
2013-02-16 16:14:39 -08:00
for (it = group_list.begin(); it != group_list.end(); ++it)
2013-02-16 16:14:39 -08:00
{
2013-05-04 18:06:58 -07:00
if (*it == nullptr)
2013-02-16 16:14:39 -08:00
{
2019-09-01 22:05:44 -05:00
LogError("nullptr group, [{}]:[{}]", fname, fline);
2013-02-16 16:14:39 -08:00
}
}
}
void EntityList::GroupProcess()
{
if (numclients < 1)
return;
2013-02-16 16:14:39 -08:00
if (group_list.empty()) {
2019-10-13 18:49:16 -07:00
group_timer.Disable();
2013-02-16 16:14:39 -08:00
return;
}
2013-02-16 16:14:39 -08:00
2015-01-09 12:59:13 -05:00
for (auto &group : group_list)
group->Process();
2013-02-16 16:14:39 -08:00
#if EQDEBUG >= 5
CheckGroupList (__FILE__, __LINE__);
#endif
}
void EntityList::QueueToGroupsForNPCHealthAA(Mob *sender, const EQApplicationPacket *app)
2013-02-16 16:14:39 -08:00
{
2015-01-09 12:59:13 -05:00
for (auto &group : group_list)
group->QueueHPPacketsForNPCHealthAA(sender, app);
2013-02-16 16:14:39 -08:00
}
void EntityList::RaidProcess()
{
if (numclients < 1)
return;
2013-02-16 16:14:39 -08:00
if (raid_list.empty()) {
2019-10-13 18:49:16 -07:00
raid_timer.Disable();
2013-02-16 16:14:39 -08:00
return;
}
2013-02-16 16:14:39 -08:00
2015-01-09 12:59:13 -05:00
for (auto &raid : raid_list)
raid->Process();
2013-02-16 16:14:39 -08:00
}
void EntityList::DoorProcess()
{
2013-02-16 16:14:39 -08:00
#ifdef IDLE_WHEN_EMPTY
if (numclients < 1)
2013-02-16 16:14:39 -08:00
return;
#endif
if (door_list.empty()) {
2019-10-13 18:49:16 -07:00
door_timer.Disable();
return;
}
auto it = door_list.begin();
while (it != door_list.end()) {
if (!it->second->Process()) {
safe_delete(it->second);
free_ids.push(it->first);
it = door_list.erase(it);
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::ObjectProcess()
{
if (object_list.empty()) {
2019-10-13 18:49:16 -07:00
object_timer.Disable();
return;
}
auto it = object_list.begin();
while (it != object_list.end()) {
if (!it->second->Process()) {
safe_delete(it->second);
free_ids.push(it->first);
it = object_list.erase(it);
} else {
++it;
2013-02-16 16:14:39 -08:00
}
}
}
void EntityList::CorpseProcess()
{
if (corpse_list.empty()) {
2019-10-13 18:49:16 -07:00
corpse_timer.Disable(); // No corpses in list
return;
}
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (!it->second->Process()) {
safe_delete(it->second);
free_ids.push(it->first);
it = corpse_list.erase(it);
} else {
++it;
2013-02-16 16:14:39 -08:00
}
}
}
void EntityList::MobProcess()
{
bool mob_dead;
auto it = mob_list.begin();
while (it != mob_list.end()) {
uint16 id = it->first;
Mob *mob = it->second;
size_t sz = mob_list.size();
#ifdef IDLE_WHEN_EMPTY
static int old_client_count=0;
static Timer *mob_settle_timer = new Timer();
if (numclients == 0 && old_client_count > 0 &&
RuleI(Zone, SecondsBeforeIdle) > 0) {
// Start Timer to allow any mobs that chased chars from zone
// to return home.
mob_settle_timer->Start(RuleI(Zone, SecondsBeforeIdle) * 1000);
}
old_client_count = numclients;
// Disable settle timer if someone zones into empty zone
if (numclients > 0 || mob_settle_timer->Check()) {
mob_settle_timer->Disable();
}
if (numclients > 0 ||
mob->GetWanderType() == 4 || mob->GetWanderType() == 6 ||
mob_settle_timer->Enabled()) {
// Normal processing, or assuring that spawns that should
// path and depop do that. Otherwise all of these type mobs
// will be up and at starting positions, or waiting at the zoneline
// if they chased the PCs when idle zone wakes up.
mob_dead = !mob->Process();
}
else {
// spawn_events can cause spawns and deaths while zone empty.
// At the very least, process that.
mob_dead = mob->CastToNPC()->GetDepop();
}
#else
mob_dead = !mob->Process();
#endif
size_t a_sz = mob_list.size();
if(a_sz > sz) {
//increased size can potentially screw with iterators so reset it to current value
//if buckets are re-orderered we may skip a process here and there but since
//process happens so often it shouldn't matter much
it = mob_list.find(id);
++it;
} else {
++it;
2013-02-16 16:14:39 -08:00
}
if(mob_dead) {
if(mob->IsMerc()) {
entity_list.RemoveMerc(id);
}
#ifdef BOTS
else if(mob->IsBot()) {
entity_list.RemoveBot(id);
}
2013-02-16 16:14:39 -08:00
#endif
else if(mob->IsNPC()) {
entity_list.RemoveNPC(id);
}
else {
2013-02-16 16:14:39 -08:00
#ifdef _WINDOWS
struct in_addr in;
in.s_addr = mob->CastToClient()->GetIP();
2019-09-02 01:39:52 -05:00
LogInfo("Dropping client: Process=false, ip=[{}] port=[{}]", inet_ntoa(in), mob->CastToClient()->GetPort());
2013-02-16 16:14:39 -08:00
#endif
zone->StartShutdownTimer();
Group *g = GetGroupByMob(mob);
if(g) {
2019-09-01 22:05:44 -05:00
LogError("About to delete a client still in a group");
g->DelMember(mob);
}
Raid *r = entity_list.GetRaidByClient(mob->CastToClient());
if(r) {
2019-09-01 22:05:44 -05:00
LogError("About to delete a client still in a raid");
r->MemberZoned(mob->CastToClient());
}
entity_list.RemoveClient(id);
2013-02-16 16:14:39 -08:00
}
entity_list.RemoveMob(id);
2013-02-16 16:14:39 -08:00
}
}
}
void EntityList::BeaconProcess()
{
auto it = beacon_list.begin();
while (it != beacon_list.end()) {
if (!it->second->Process()) {
safe_delete(it->second);
free_ids.push(it->first);
it = beacon_list.erase(it);
} else {
++it;
}
2013-02-16 16:14:39 -08:00
}
}
void EntityList::EncounterProcess()
{
auto it = encounter_list.begin();
while (it != encounter_list.end()) {
if (!it->second->Process()) {
safe_delete(it->second);
free_ids.push(it->first);
it = encounter_list.erase(it);
}
else {
++it;
}
}
}
void EntityList::AddGroup(Group *group)
{
if (group == nullptr) //this seems to be happening somehow...
2013-02-16 16:14:39 -08:00
return;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
uint32 gid = worldserver.NextGroupID();
if (gid == 0) {
LogError("Unable to get new group ID from world server. group is going to be broken");
2013-02-16 16:14:39 -08:00
return;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
AddGroup(group, gid);
#if EQDEBUG >= 5
CheckGroupList (__FILE__, __LINE__);
#endif
}
void EntityList::AddGroup(Group *group, uint32 gid)
{
2013-02-16 16:14:39 -08:00
group->SetID(gid);
group_list.push_back(group);
2019-10-13 18:49:16 -07:00
if (!group_timer.Enabled())
group_timer.Start();
2013-02-16 16:14:39 -08:00
#if EQDEBUG >= 5
CheckGroupList(__FILE__, __LINE__);
2013-02-16 16:14:39 -08:00
#endif
}
void EntityList::AddRaid(Raid *raid)
{
if (raid == nullptr)
2013-02-16 16:14:39 -08:00
return;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
uint32 gid = worldserver.NextGroupID();
if (gid == 0) {
LogError("Unable to get new group ID from world server. group is going to be broken");
2013-02-16 16:14:39 -08:00
return;
}
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
AddRaid(raid, gid);
}
void EntityList::AddRaid(Raid *raid, uint32 gid)
{
2013-02-16 16:14:39 -08:00
raid->SetID(gid);
raid_list.push_back(raid);
2019-10-13 18:49:16 -07:00
if (!raid_timer.Enabled())
raid_timer.Start();
2013-02-16 16:14:39 -08:00
}
void EntityList::AddCorpse(Corpse *corpse, uint32 in_id)
{
2013-02-16 16:14:39 -08:00
if (corpse == 0)
return;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if (in_id == 0xFFFFFFFF)
corpse->SetID(GetFreeID());
else
corpse->SetID(in_id);
2013-02-16 16:14:39 -08:00
corpse->CalcCorpseName();
corpse_list.insert(std::pair<uint16, Corpse *>(corpse->GetID(), corpse));
2019-10-13 18:49:16 -07:00
if (!corpse_timer.Enabled())
corpse_timer.Start();
2013-02-16 16:14:39 -08:00
}
void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
{
2013-02-16 16:14:39 -08:00
npc->SetID(GetFreeID());
2013-05-09 10:44:08 -04:00
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
2013-02-16 16:14:39 -08:00
uint16 emoteid = npc->GetEmoteID();
if (emoteid != 0)
npc->DoNPCEmote(ONSPAWN, emoteid);
npc->SetSpawned();
2013-02-16 16:14:39 -08:00
if (SendSpawnPacket) {
if (dontqueue) { // aka, SEND IT NOW BITCH!
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket;
npc->CreateSpawnPacket(app, npc);
2013-02-16 16:14:39 -08:00
QueueClients(npc, app);
npc->SendArmorAppearance();
npc->SetAppearance(npc->GetGuardPointAnim(),false);
if (!npc->IsTargetable())
npc->SendTargetable(false);
2013-02-16 16:14:39 -08:00
safe_delete(app);
} else {
2016-05-25 16:10:28 -04:00
auto ns = new NewSpawn_Struct;
2013-02-16 16:14:39 -08:00
memset(ns, 0, sizeof(NewSpawn_Struct));
npc->FillSpawnStruct(ns, nullptr); // Not working on player newspawns, so it's safe to use a ForWho of 0
2013-02-16 16:14:39 -08:00
AddToSpawnQueue(npc->GetID(), &ns);
safe_delete(ns);
}
if (npc->IsFindable())
2013-02-16 16:14:39 -08:00
UpdateFindableNPCState(npc, false);
}
2013-05-06 13:07:41 -04:00
npc_list.insert(std::pair<uint16, NPC *>(npc->GetID(), npc));
mob_list.insert(std::pair<uint16, Mob *>(npc->GetID(), npc));
/* Zone controller process EVENT_SPAWN_ZONE */
if (RuleB(Zone, UseZoneController)) {
if (entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID) && npc->GetNPCTypeID() != ZONE_CONTROLLER_NPC_ID){
char data_pass[100] = { 0 };
snprintf(data_pass, 99, "%d %d", npc->GetID(), npc->GetNPCTypeID());
parse->EventNPC(EVENT_SPAWN_ZONE, entity_list.GetNPCByNPCTypeID(ZONE_CONTROLLER_NPC_ID)->CastToNPC(), nullptr, data_pass, 0);
}
}
/**
* Set whether NPC was spawned in or out of water
*/
if (zone->HasMap() && zone->HasWaterMap()) {
npc->SetSpawnedInWater(false);
if (zone->watermap->InLiquid(npc->GetPosition())) {
npc->SetSpawnedInWater(true);
}
}
2013-02-16 16:14:39 -08:00
}
void EntityList::AddMerc(Merc *merc, bool SendSpawnPacket, bool dontqueue)
{
if (merc)
{
2013-02-16 16:14:39 -08:00
merc->SetID(GetFreeID());
merc->SetSpawned();
if (SendSpawnPacket)
{
if (dontqueue) {
2013-02-16 16:14:39 -08:00
// Send immediately
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket();
2013-02-16 16:14:39 -08:00
merc->CreateSpawnPacket(outapp);
outapp->priority = 6;
QueueClients(merc, outapp, true);
safe_delete(outapp);
} else {
2013-02-16 16:14:39 -08:00
// Queue the packet
2016-05-25 16:10:28 -04:00
auto ns = new NewSpawn_Struct;
2013-02-16 16:14:39 -08:00
memset(ns, 0, sizeof(NewSpawn_Struct));
merc->FillSpawnStruct(ns, 0);
2013-02-16 16:14:39 -08:00
AddToSpawnQueue(merc->GetID(), &ns);
safe_delete(ns);
}
}
merc_list.insert(std::pair<uint16, Merc *>(merc->GetID(), merc));
mob_list.insert(std::pair<uint16, Mob *>(merc->GetID(), merc));
2013-02-16 16:14:39 -08:00
}
}
void EntityList::AddObject(Object *obj, bool SendSpawnPacket)
{
2013-05-06 13:07:41 -04:00
obj->SetID(GetFreeID());
2013-02-16 16:14:39 -08:00
if (SendSpawnPacket) {
EQApplicationPacket app;
obj->CreateSpawnPacket(&app);
#if (EQDEBUG >= 6)
DumpPacket(&app);
#endif
QueueClients(0, &app,false);
}
object_list.insert(std::pair<uint16, Object *>(obj->GetID(), obj));
2019-10-13 18:49:16 -07:00
if (!object_timer.Enabled())
object_timer.Start();
2013-02-16 16:14:39 -08:00
}
void EntityList::AddDoor(Doors *door)
{
2013-02-16 16:14:39 -08:00
door->SetEntityID(GetFreeID());
door_list.insert(std::pair<uint16, Doors *>(door->GetEntityID(), door));
2019-10-13 18:49:16 -07:00
if (!door_timer.Enabled())
door_timer.Start();
2013-02-16 16:14:39 -08:00
}
void EntityList::AddTrap(Trap *trap)
{
2013-02-16 16:14:39 -08:00
trap->SetID(GetFreeID());
trap_list.insert(std::pair<uint16, Trap *>(trap->GetID(), trap));
2019-10-13 18:49:16 -07:00
if (!trap_timer.Enabled())
trap_timer.Start();
2013-02-16 16:14:39 -08:00
}
void EntityList::AddBeacon(Beacon *beacon)
{
beacon->SetID(GetFreeID());
beacon_list.insert(std::pair<uint16, Beacon *>(beacon->GetID(), beacon));
2013-02-16 16:14:39 -08:00
}
void EntityList::AddEncounter(Encounter *encounter)
{
encounter->SetID(GetFreeID());
encounter_list.insert(std::pair<uint16, Encounter *>(encounter->GetID(), encounter));
}
void EntityList::AddToSpawnQueue(uint16 entityid, NewSpawn_Struct **ns)
{
2013-02-16 16:14:39 -08:00
uint32 count;
if ((count = (client_list.size())) == 0)
2013-02-16 16:14:39 -08:00
return;
SpawnQueue.Append(*ns);
NumSpawnsOnQueue++;
if (tsFirstSpawnOnQueue == 0xFFFFFFFF)
tsFirstSpawnOnQueue = Timer::GetCurrentTime();
*ns = nullptr;
2013-02-16 16:14:39 -08:00
}
void EntityList::CheckSpawnQueue()
{
2013-02-16 16:14:39 -08:00
// Send the stuff if the oldest packet on the queue is older than 50ms -Quagmire
if (tsFirstSpawnOnQueue != 0xFFFFFFFF && (Timer::GetCurrentTime() - tsFirstSpawnOnQueue) > 50) {
LinkedListIterator<NewSpawn_Struct *> iterator(SpawnQueue);
EQApplicationPacket *outapp = 0;
iterator.Reset();
NewSpawn_Struct *ns;
while(iterator.MoreElements()) {
outapp = new EQApplicationPacket;
ns = iterator.GetData();
Mob::CreateSpawnPacket(outapp, ns);
QueueClients(0, outapp);
auto it = npc_list.find(ns->spawn.spawnId);
if (it == npc_list.end()) {
// We must of despawned, hope that's the reason!
2019-09-01 22:05:44 -05:00
LogError("Error in EntityList::CheckSpawnQueue: Unable to find NPC for spawnId [{}]", ns->spawn.spawnId);
}
else {
NPC *pnpc = it->second;
pnpc->SendArmorAppearance();
pnpc->SetAppearance(pnpc->GetGuardPointAnim(), false);
if (!pnpc->IsTargetable())
pnpc->SendTargetable(false);
}
safe_delete(outapp);
iterator.RemoveCurrent();
}
2013-02-16 16:14:39 -08:00
tsFirstSpawnOnQueue = 0xFFFFFFFF;
NumSpawnsOnQueue = 0;
}
}
Doors *EntityList::FindDoor(uint8 door_id)
2013-02-16 16:14:39 -08:00
{
if (door_id == 0 || door_list.empty())
return nullptr;
2013-02-16 16:14:39 -08:00
auto it = door_list.begin();
while (it != door_list.end()) {
if (it->second->GetDoorID() == door_id)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Object *EntityList::FindObject(uint32 object_id)
2013-02-16 16:14:39 -08:00
{
if (object_id == 0 || object_list.empty())
return nullptr;
2013-02-16 16:14:39 -08:00
auto it = object_list.begin();
while (it != object_list.end()) {
if (it->second->GetDBID() == object_id)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
}
Object *EntityList::FindNearbyObject(float x, float y, float z, float radius)
2013-02-16 16:14:39 -08:00
{
if (object_list.empty())
return nullptr;
2013-02-16 16:14:39 -08:00
float ox;
float oy;
float oz;
auto it = object_list.begin();
while (it != object_list.end()) {
Object *object = it->second;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
object->GetLocation(&ox, &oy, &oz);
ox = (x < ox) ? (ox - x) : (x - ox);
oy = (y < oy) ? (oy - y) : (y - oy);
oz = (z < oz) ? (oz - z) : (z - oz);
if ((ox <= radius) && (oy <= radius) && (oz <= radius))
return object;
++it;
}
2013-02-16 16:14:39 -08:00
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
}
bool EntityList::MakeDoorSpawnPacket(EQApplicationPacket *app, Client *client)
2013-02-16 16:14:39 -08:00
{
if (door_list.empty())
return false;
uint32 mask_test = client->ClientVersionBit();
2013-02-16 16:14:39 -08:00
int count = 0;
auto it = door_list.begin();
while (it != door_list.end()) {
if ((it->second->GetClientVersionMask() & mask_test) &&
strlen(it->second->GetDoorName()) > 3)
2013-02-16 16:14:39 -08:00
count++;
++it;
2013-02-16 16:14:39 -08:00
}
if (count == 0 || count > 500)
2013-02-16 16:14:39 -08:00
return false;
2013-02-16 16:14:39 -08:00
uint32 length = count * sizeof(Door_Struct);
2016-05-25 16:10:28 -04:00
auto packet_buffer = new uchar[length];
2013-02-16 16:14:39 -08:00
memset(packet_buffer, 0, length);
uchar *ptr = packet_buffer;
2013-02-16 16:14:39 -08:00
Doors *door;
Door_Struct new_door;
2013-02-16 16:14:39 -08:00
it = door_list.begin();
while (it != door_list.end()) {
door = it->second;
if (door && (door->GetClientVersionMask() & mask_test) &&
strlen(door->GetDoorName()) > 3) {
memset(&new_door, 0, sizeof(new_door));
memcpy(new_door.name, door->GetDoorName(), 32);
auto position = door->GetPosition();
new_door.xPos = position.x;
new_door.yPos = position.y;
new_door.zPos = position.z;
new_door.heading = position.w;
new_door.incline = door->GetIncline();
new_door.size = door->GetSize();
new_door.doorId = door->GetDoorID();
new_door.opentype = door->GetOpenType();
Log(Logs::General, Logs::Doors, "Door timer_disable: %s door_id: %u is_open: %s invert_state: %i",
(door->GetDisableTimer() ? "true" : "false"),
door->GetDoorID(),
(door->IsDoorOpen() ? "true" : "false"),
door->GetInvertState()
);
new_door.state_at_spawn = (door->GetInvertState() ? !door->IsDoorOpen() : door->IsDoorOpen());
new_door.invert_state = door->GetInvertState();
new_door.door_param = door->GetDoorParam();
memcpy(ptr, &new_door, sizeof(new_door));
ptr += sizeof(new_door);
*(ptr - 1) = 0x01;
*(ptr - 3) = 0x01;
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
app->SetOpcode(OP_SpawnDoor);
app->size = length;
app->pBuffer = packet_buffer;
2013-05-06 13:07:41 -04:00
return true;
2013-02-16 16:14:39 -08:00
}
Entity *EntityList::GetEntityMob(uint16 id)
{
auto it = mob_list.find(id);
if (it != mob_list.end())
return it->second;
return nullptr;
2013-02-16 16:14:39 -08:00
}
Entity *EntityList::GetEntityMerc(uint16 id)
{
auto it = merc_list.find(id);
if (it != merc_list.end())
return it->second;
return nullptr;
2013-02-16 16:14:39 -08:00
}
Entity *EntityList::GetEntityMob(const char *name)
2013-02-16 16:14:39 -08:00
{
if (name == 0 || mob_list.empty())
2013-02-16 16:14:39 -08:00
return 0;
auto it = mob_list.begin();
while (it != mob_list.end()) {
if (strcasecmp(it->second->GetName(), name) == 0)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Entity *EntityList::GetEntityDoor(uint16 id)
{
auto it = door_list.find(id);
if (it != door_list.end())
return it->second;
return nullptr;
2013-02-16 16:14:39 -08:00
}
Entity *EntityList::GetEntityCorpse(uint16 id)
2013-02-16 16:14:39 -08:00
{
auto it = corpse_list.find(id);
if (it != corpse_list.end())
return it->second;
return nullptr;
}
2013-02-16 16:14:39 -08:00
Entity *EntityList::GetEntityCorpse(const char *name)
{
if (name == 0 || corpse_list.empty())
return nullptr;
2013-05-06 13:07:41 -04:00
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (strcasecmp(it->second->GetName(), name) == 0)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Entity *EntityList::GetEntityTrap(uint16 id)
{
auto it = trap_list.find(id);
if (it != trap_list.end())
return it->second;
return nullptr;
2013-02-16 16:14:39 -08:00
}
Entity *EntityList::GetEntityObject(uint16 id)
{
auto it = object_list.find(id);
if (it != object_list.end())
return it->second;
return nullptr;
2013-02-16 16:14:39 -08:00
}
Entity *EntityList::GetEntityBeacon(uint16 id)
{
auto it = beacon_list.find(id);
if (it != beacon_list.end())
return it->second;
return nullptr;
2013-02-16 16:14:39 -08:00
}
Entity *EntityList::GetEntityEncounter(uint16 id)
{
auto it = encounter_list.find(id);
if (it != encounter_list.end())
return it->second;
return nullptr;
}
Entity *EntityList::GetID(uint16 get_id)
2013-02-16 16:14:39 -08:00
{
Entity *ent = 0;
if ((ent = entity_list.GetEntityMob(get_id)) != 0)
2013-02-16 16:14:39 -08:00
return ent;
else if ((ent=entity_list.GetEntityDoor(get_id)) != 0)
2013-02-16 16:14:39 -08:00
return ent;
else if ((ent=entity_list.GetEntityCorpse(get_id)) != 0)
2013-02-16 16:14:39 -08:00
return ent;
else if ((ent=entity_list.GetEntityObject(get_id)) != 0)
2013-02-16 16:14:39 -08:00
return ent;
else if ((ent=entity_list.GetEntityTrap(get_id)) != 0)
2013-02-16 16:14:39 -08:00
return ent;
else if ((ent=entity_list.GetEntityBeacon(get_id)) != 0)
2013-02-16 16:14:39 -08:00
return ent;
else if ((ent = entity_list.GetEntityEncounter(get_id)) != 0)
return ent;
2013-02-16 16:14:39 -08:00
else
return 0;
}
NPC *EntityList::GetNPCByNPCTypeID(uint32 npc_id)
2013-02-16 16:14:39 -08:00
{
if (npc_id == 0 || npc_list.empty())
return nullptr;
2013-05-06 13:07:41 -04:00
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second->GetNPCTypeID() == npc_id)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
2020-05-07 23:05:07 -04:00
NPC *EntityList::GetNPCBySpawnID(uint32 spawn_id)
{
if (spawn_id == 0 || npc_list.empty()) {
return nullptr;
}
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second->GetSpawnGroupId() == spawn_id) {
return it->second;
}
++it;
}
return nullptr;
}
Mob *EntityList::GetMob(uint16 get_id)
2013-02-16 16:14:39 -08:00
{
Entity *ent = nullptr;
2013-02-16 16:14:39 -08:00
if (get_id == 0)
return nullptr;
2013-02-16 16:14:39 -08:00
if ((ent = entity_list.GetEntityMob(get_id)))
2013-02-16 16:14:39 -08:00
return ent->CastToMob();
else if ((ent = entity_list.GetEntityCorpse(get_id)))
2013-02-16 16:14:39 -08:00
return ent->CastToMob();
return nullptr;
2013-02-16 16:14:39 -08:00
}
Mob *EntityList::GetMob(const char *name)
2013-02-16 16:14:39 -08:00
{
Entity* ent = nullptr;
2013-02-16 16:14:39 -08:00
if (name == 0)
return nullptr;
2013-02-16 16:14:39 -08:00
if ((ent = entity_list.GetEntityMob(name)))
2013-02-16 16:14:39 -08:00
return ent->CastToMob();
else if ((ent = entity_list.GetEntityCorpse(name)))
2013-02-16 16:14:39 -08:00
return ent->CastToMob();
return nullptr;
2013-02-16 16:14:39 -08:00
}
Mob *EntityList::GetMobByNpcTypeID(uint32 get_id)
2013-02-16 16:14:39 -08:00
{
if (get_id == 0 || mob_list.empty())
2013-02-16 16:14:39 -08:00
return 0;
2013-05-06 13:07:41 -04:00
auto it = mob_list.begin();
while (it != mob_list.end()) {
if (it->second->GetNPCTypeID() == get_id)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
bool EntityList::IsMobSpawnedByNpcTypeID(uint32 get_id)
{
if (get_id == 0 || npc_list.empty())
return false;
auto it = npc_list.begin();
while (it != npc_list.end()) {
// Mobs will have a 0 as their GetID() if they're dead
if (it->second->GetNPCTypeID() == get_id && it->second->GetID() != 0)
return true;
++it;
}
return false;
}
Object *EntityList::GetObjectByDBID(uint32 id)
2013-02-16 16:14:39 -08:00
{
if (id == 0 || object_list.empty())
return nullptr;
2013-02-16 16:14:39 -08:00
auto it = object_list.begin();
while (it != object_list.end()) {
if (it->second->GetDBID() == id)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Doors *EntityList::GetDoorsByDBID(uint32 id)
2013-02-16 16:14:39 -08:00
{
if (id == 0 || door_list.empty())
return nullptr;
2013-02-16 16:14:39 -08:00
auto it = door_list.begin();
while (it != door_list.end()) {
if (it->second->GetDoorDBID() == id)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Doors *EntityList::GetDoorsByDoorID(uint32 id)
2013-02-16 16:14:39 -08:00
{
if (id == 0 || door_list.empty())
return nullptr;
2013-02-16 16:14:39 -08:00
auto it = door_list.begin();
while (it != door_list.end()) {
if (it->second->CastToDoors()->GetDoorID() == id)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
uint16 EntityList::GetFreeID()
{
if (free_ids.empty()) { // hopefully this will never be true
// The client has a hard cap on entity count some where
// Neither the client or server performs well with a lot entities either
uint16 newid = 1500;
while (true) {
newid++;
if (GetID(newid) == nullptr)
return newid;
2013-02-16 16:14:39 -08:00
}
}
uint16 newid = free_ids.front();
free_ids.pop();
return newid;
2013-02-16 16:14:39 -08:00
}
// if no language skill is specified, sent with 100 skill
void EntityList::ChannelMessage(Mob *from, uint8 chan_num, uint8 language, const char *message, ...)
{
2013-02-16 16:14:39 -08:00
ChannelMessage(from, chan_num, language, 100, message);
}
void EntityList::ChannelMessage(Mob *from, uint8 chan_num, uint8 language,
uint8 lang_skill, const char *message, ...)
{
2013-02-16 16:14:39 -08:00
va_list argptr;
char buffer[4096];
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
va_start(argptr, message);
vsnprintf(buffer, 4096, message, argptr);
va_end(argptr);
2013-05-06 13:07:41 -04:00
auto it = client_list.begin();
while(it != client_list.end()) {
Client *client = it->second;
2013-02-16 16:14:39 -08:00
eqFilterType filter = FilterNone;
if (chan_num == ChatChannel_Shout) //shout
filter = FilterShouts;
else if (chan_num == ChatChannel_Auction) //auction
filter = FilterAuctions;
//
// Only say is limited in range
if (chan_num != ChatChannel_Say || Distance(client->GetPosition(), from->GetPosition()) < 200)
if (filter == FilterNone || client->GetFilter(filter) != FilterHide)
2013-02-16 16:14:39 -08:00
client->ChannelMessageSend(from->GetName(), 0, chan_num, language, lang_skill, buffer);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::SendZoneSpawns(Client *client)
2013-02-16 16:14:39 -08:00
{
EQApplicationPacket *app;
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *ent = it->second;
2017-07-19 14:52:04 -04:00
if (!ent->InZone() || !ent->ShouldISpawnFor(client)) {
++it;
continue;
2013-02-16 16:14:39 -08:00
}
2013-02-16 16:14:39 -08:00
app = new EQApplicationPacket;
it->second->CastToMob()->CreateSpawnPacket(app); // TODO: Use zonespawns opcode instead
2013-02-16 16:14:39 -08:00
client->QueuePacket(app, true, Client::CLIENT_CONNECTED);
safe_delete(app);
++it;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::SendZoneSpawnsBulk(Client *client)
2013-02-16 16:14:39 -08:00
{
2018-12-31 03:18:59 -06:00
NewSpawn_Struct ns{};
Mob *spawn;
EQApplicationPacket *app;
2018-12-31 03:18:59 -06:00
uint32 max_spawns = 100;
if (max_spawns > mob_list.size()) {
max_spawns = static_cast<uint32>(mob_list.size());
}
auto bulk_zone_spawn_packet = new BulkZoneSpawnPacket(client, max_spawns);
const glm::vec4 &client_position = client->GetPosition();
const float distance_max = (600.0 * 600.0);
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
spawn = it->second;
if (spawn && spawn->GetID() > 0 && spawn->Spawned()) {
2018-12-31 03:18:59 -06:00
if (!spawn->ShouldISpawnFor(client)) {
2013-02-16 16:14:39 -08:00
continue;
2018-12-31 03:18:59 -06:00
}
2018-12-31 03:18:59 -06:00
const glm::vec4 &spawn_position = spawn->GetPosition();
2017-07-19 14:52:04 -04:00
2018-12-31 03:18:59 -06:00
bool is_delayed_packet = (
DistanceSquared(client_position, spawn_position) > distance_max ||
(spawn->IsClient() && (spawn->GetRace() == MINOR_ILL_OBJ || spawn->GetRace() == TREE))
);
2017-07-19 14:52:04 -04:00
2018-12-31 03:18:59 -06:00
if (is_delayed_packet) {
app = new EQApplicationPacket;
spawn->CreateSpawnPacket(app);
client->QueuePacket(app, true, Client::CLIENT_CONNECTED);
safe_delete(app);
}
else {
memset(&ns, 0, sizeof(NewSpawn_Struct));
spawn->FillSpawnStruct(&ns, client);
2018-12-31 03:18:59 -06:00
bulk_zone_spawn_packet->AddSpawn(&ns);
}
spawn->SendArmorAppearance(client);
2018-12-31 03:18:59 -06:00
/**
* Original code kept for spawn packet research
*
* int32 race = spawn->GetRace();
*
* Illusion races on PCs don't work as a mass spawn
* But they will work as an add_spawn AFTER CLIENT_CONNECTED.
* if (spawn->IsClient() && (race == MINOR_ILL_OBJ || race == TREE)) {
* app = new EQApplicationPacket;
* spawn->CreateSpawnPacket(app);
* client->QueuePacket(app, true, Client::CLIENT_CONNECTED);
* safe_delete(app);
* }
* else {
* memset(&ns, 0, sizeof(NewSpawn_Struct));
* spawn->FillSpawnStruct(&ns, client);
* bzsp->AddSpawn(&ns);
* }
*
* Despite being sent in the OP_ZoneSpawns packet, the client
* does not display worn armor correctly so display it.
* spawn->SendArmorAppearance(client);
*/
}
}
safe_delete(bulk_zone_spawn_packet);
2013-02-16 16:14:39 -08:00
}
//this is a hack to handle a broken spawn struct
void EntityList::SendZonePVPUpdates(Client *to)
{
auto it = client_list.begin();
while (it != client_list.end()) {
Client *c = it->second;
2013-02-16 16:14:39 -08:00
if(c->GetPVP())
c->SendAppearancePacket(AT_PVP, c->GetPVP(), true, false, to);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::SendZoneCorpses(Client *client)
2013-02-16 16:14:39 -08:00
{
EQApplicationPacket *app;
2013-05-06 13:07:41 -04:00
for (auto it = corpse_list.begin(); it != corpse_list.end(); ++it) {
Corpse *ent = it->second;
2013-02-16 16:14:39 -08:00
app = new EQApplicationPacket;
ent->CreateSpawnPacket(app);
client->QueuePacket(app, true, Client::CLIENT_CONNECTED);
safe_delete(app);
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::SendZoneCorpsesBulk(Client *client)
{
2013-02-16 16:14:39 -08:00
NewSpawn_Struct ns;
Corpse *spawn;
uint32 maxspawns = 100;
2013-02-16 16:14:39 -08:00
2016-05-25 16:10:28 -04:00
auto bzsp = new BulkZoneSpawnPacket(client, maxspawns);
2013-05-06 13:07:41 -04:00
for (auto it = corpse_list.begin(); it != corpse_list.end(); ++it) {
spawn = it->second;
if (spawn && spawn->InZone()) {
2013-02-16 16:14:39 -08:00
memset(&ns, 0, sizeof(NewSpawn_Struct));
spawn->FillSpawnStruct(&ns, client);
bzsp->AddSpawn(&ns);
}
}
safe_delete(bzsp);
}
void EntityList::SendZoneObjects(Client *client)
2013-02-16 16:14:39 -08:00
{
auto it = object_list.begin();
while (it != object_list.end()) {
2016-05-25 16:10:28 -04:00
auto app = new EQApplicationPacket;
it->second->CreateSpawnPacket(app);
2013-02-16 16:14:39 -08:00
client->FastQueuePacket(&app);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::Save()
{
auto it = client_list.begin();
while (it != client_list.end()) {
it->second->Save();
++it;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::ReplaceWithTarget(Mob *pOldMob, Mob *pNewTarget)
2013-02-16 16:14:39 -08:00
{
if (!pNewTarget)
2013-02-16 16:14:39 -08:00
return;
2013-05-06 13:07:41 -04:00
auto it = mob_list.begin();
while (it != mob_list.end()) {
if (it->second->IsAIControlled()) {
2013-05-09 10:44:08 -04:00
// replace the old mob with the new one
if (it->second->RemoveFromHateList(pOldMob))
it->second->AddToHateList(pNewTarget, 1, 0);
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets)
2013-02-16 16:14:39 -08:00
{
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *m = it->second;
++it;
2013-05-06 13:07:41 -04:00
if (!m)
2013-02-16 16:14:39 -08:00
continue;
if (RemoveFromXTargets) {
2017-10-16 01:25:14 -04:00
if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m->CastToClient())))
2013-02-16 16:14:39 -08:00
m->CastToClient()->RemoveXTarget(mob, false);
// FadingMemories calls this function passing the client.
2017-10-16 01:25:14 -04:00
else if (mob->IsClient() && (m->CheckAggro(mob) || m->IsOnFeignMemory(mob->CastToClient())))
2013-02-16 16:14:39 -08:00
mob->CastToClient()->RemoveXTarget(m, false);
2013-05-23 20:22:42 -04:00
}
m->RemoveFromHateList(mob);
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::RemoveFromXTargets(Mob *mob)
2013-02-16 16:14:39 -08:00
{
auto it = client_list.begin();
while (it != client_list.end()) {
it->second->RemoveXTarget(mob, false);
++it;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::RemoveFromAutoXTargets(Mob *mob)
2013-02-16 16:14:39 -08:00
{
auto it = client_list.begin();
while (it != client_list.end()) {
it->second->RemoveXTarget(mob, true);
++it;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::RefreshAutoXTargets(Client *c)
{
if (!c)
2013-02-16 16:14:39 -08:00
return;
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *m = it->second;
++it;
2013-05-06 13:07:41 -04:00
if (!m || m->GetHP() <= 0)
2013-02-16 16:14:39 -08:00
continue;
2017-10-16 01:25:14 -04:00
if ((m->CheckAggro(c) || m->IsOnFeignMemory(c)) && !c->IsXTarget(m)) {
c->AddAutoXTarget(m, false); // we only call this before a bulk, so lets not send right away
2013-02-16 16:14:39 -08:00
break;
}
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::RefreshClientXTargets(Client *c)
{
if (!c)
2013-02-16 16:14:39 -08:00
return;
auto it = client_list.begin();
while (it != client_list.end()) {
Client *c2 = it->second;
++it;
2013-05-06 13:07:41 -04:00
if (!c2)
2013-02-16 16:14:39 -08:00
continue;
if (c2->IsClientXTarget(c))
2013-02-16 16:14:39 -08:00
c2->UpdateClientXTarget(c);
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *app,
bool iSendToSender, Mob *SkipThisMob, bool ackreq, bool HoTT, uint32 ClientVersionBits, bool inspect_buffs)
2013-02-16 16:14:39 -08:00
{
auto it = client_list.begin();
while (it != client_list.end()) {
Client *c = it->second;
++it;
2013-02-16 16:14:39 -08:00
Mob *Target = c->GetTarget();
if (!Target)
2013-02-16 16:14:39 -08:00
continue;
2013-05-04 18:06:58 -07:00
Mob *TargetsTarget = nullptr;
2013-02-16 16:14:39 -08:00
TargetsTarget = Target->GetTarget();
2013-02-16 16:14:39 -08:00
bool Send = false;
if (c == SkipThisMob)
2013-02-16 16:14:39 -08:00
continue;
if (iSendToSender)
if (c == sender)
2013-02-16 16:14:39 -08:00
Send = true;
2013-05-06 13:07:41 -04:00
if (c != sender) {
if (Target == sender) {
if (inspect_buffs) { // if inspect_buffs is true we're sending a mob's buffs to those with the LAA
if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) {
Send = true;
} else if (c->IsRaidGrouped()) {
Raid *raid = c->GetRaid();
if (!raid)
continue;
uint32 gid = raid->GetGroup(c);
if (gid > 11 || raid->GroupCount(gid) < 3)
continue;
if (raid->GetLeadershipAA(groupAAInspectBuffs, gid))
Send = true;
} else {
Group *group = c->GetGroup();
if (!group || group->GroupCount() < 3)
continue;
if (group->GetLeadershipAA(groupAAInspectBuffs))
Send = true;
}
} else {
2013-02-16 16:14:39 -08:00
Send = true;
}
} else if (HoTT && TargetsTarget == sender) {
Send = true;
}
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
if (Send && (c->ClientVersionBit() & ClientVersionBits))
2013-02-16 16:14:39 -08:00
c->QueuePacket(app, ackreq);
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::QueueClientsByXTarget(Mob *sender, const EQApplicationPacket *app, bool iSendToSender, EQ::versions::ClientVersionBitmask client_version_bits)
2013-02-16 16:14:39 -08:00
{
auto it = client_list.begin();
while (it != client_list.end()) {
Client *c = it->second;
++it;
2013-05-06 13:07:41 -04:00
if (!c || ((c == sender) && !iSendToSender))
2013-05-06 13:07:41 -04:00
continue;
2013-02-16 16:14:39 -08:00
if ((c->ClientVersionBit() & client_version_bits) == 0)
continue;
if (!c->IsXTarget(sender))
2013-02-16 16:14:39 -08:00
continue;
c->QueuePacket(app);
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
/**
* @param sender
* @param app
* @param ignore_sender
* @param distance
* @param skipped_mob
* @param is_ack_required
* @param filter
*/
void EntityList::QueueCloseClients(
Mob *sender,
const EQApplicationPacket *app,
bool ignore_sender,
float distance,
Mob *skipped_mob,
bool is_ack_required,
eqFilterType filter
)
{
2013-05-04 18:06:58 -07:00
if (sender == nullptr) {
2013-02-16 16:14:39 -08:00
QueueClients(sender, app, ignore_sender);
return;
}
if (distance <= 0) {
distance = 600;
}
2013-05-06 13:07:41 -04:00
float distance_squared = distance * distance;
for (auto &e : GetCloseMobList(sender, distance)) {
Mob *mob = e.second;
if (!mob->IsClient()) {
continue;
}
Client *client = mob->CastToClient();
2013-02-16 16:14:39 -08:00
if ((!ignore_sender || client != sender) && (client != skipped_mob)) {
2019-12-30 20:18:44 -06:00
if (DistanceSquared(client->GetPosition(), sender->GetPosition()) >= distance_squared) {
continue;
}
if (!client->Connected()) {
continue;
}
eqFilterMode client_filter = client->GetFilter(filter);
if (
filter == FilterNone || client_filter == FilterShow ||
(client_filter == FilterShowGroupOnly &&
(sender == client || (client->GetGroup() && client->GetGroup()->IsGroupMember(sender)))) ||
(client_filter == FilterShowSelfOnly && client == sender)
) {
client->QueuePacket(app, is_ack_required, Client::CLIENT_CONNECTED);
2013-02-16 16:14:39 -08:00
}
}
}
}
//sender can be null
void EntityList::QueueClients(
Mob *sender, const EQApplicationPacket *app,
bool ignore_sender, bool ackreq
)
{
auto it = client_list.begin();
while (it != client_list.end()) {
Client *ent = it->second;
2013-02-16 16:14:39 -08:00
if ((!ignore_sender || ent != sender))
ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::QueueManaged(Mob *sender, const EQApplicationPacket *app,
bool ignore_sender, bool ackreq)
{
auto it = client_list.begin();
while (it != client_list.end()) {
Client *ent = it->second;
2013-02-16 16:14:39 -08:00
if ((!ignore_sender || ent != sender))
ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::QueueClientsStatus(Mob *sender, const EQApplicationPacket *app,
bool ignore_sender, uint8 minstatus, uint8 maxstatus)
2013-02-16 16:14:39 -08:00
{
auto it = client_list.begin();
while (it != client_list.end()) {
if ((!ignore_sender || it->second != sender) &&
(it->second->Admin() >= minstatus && it->second->Admin() <= maxstatus))
it->second->QueuePacket(app);
2013-05-06 13:07:41 -04:00
++it;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::DuelMessage(Mob *winner, Mob *loser, bool flee)
{
if (winner->GetLevelCon(winner->GetLevel(), loser->GetLevel()) > 2) {
std::vector<EQ::Any> args;
args.push_back(winner);
args.push_back(loser);
parse->EventPlayer(EVENT_DUEL_WIN, winner->CastToClient(), loser->GetName(), loser->CastToClient()->CharacterID(), &args);
parse->EventPlayer(EVENT_DUEL_LOSE, loser->CastToClient(), winner->GetName(), winner->CastToClient()->CharacterID(), &args);
}
auto it = client_list.begin();
while (it != client_list.end()) {
Client *cur = it->second;
2013-02-16 16:14:39 -08:00
//might want some sort of distance check in here?
if (cur != winner && cur != loser) {
2013-02-16 16:14:39 -08:00
if (flee)
2019-08-11 00:14:02 -05:00
cur->MessageString(Chat::Yellow, DUEL_FLED, winner->GetName(),loser->GetName(),loser->GetName());
2013-02-16 16:14:39 -08:00
else
2019-08-11 00:14:02 -05:00
cur->MessageString(Chat::Yellow, DUEL_FINISHED, winner->GetName(),loser->GetName());
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
}
Client *EntityList::GetClientByName(const char *checkname)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (strcasecmp(it->second->GetName(), checkname) == 0)
return it->second;
++it;
2013-05-06 13:07:41 -04:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Client *EntityList::GetClientByCharID(uint32 iCharID)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->CharacterID() == iCharID)
return it->second;
++it;
2013-05-06 13:07:41 -04:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Client *EntityList::GetClientByWID(uint32 iWID)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->GetWID() == iWID) {
return it->second;
2013-05-06 13:07:41 -04:00
}
++it;
2013-05-06 13:07:41 -04:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Client *EntityList::GetClientByLSID(uint32 iLSID)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->LSAccountID() == iLSID) {
return it->second;
}
++it;
}
return nullptr;
}
Client *EntityList::GetRandomClient(const glm::vec3& location, float Distance, Client *ExcludeClient)
2013-02-16 16:14:39 -08:00
{
std::vector<Client *> ClientsInRange;
2013-02-16 16:14:39 -08:00
for (auto it = client_list.begin();it != client_list.end(); ++it)
if ((it->second != ExcludeClient) && (DistanceSquared(static_cast<glm::vec3>(it->second->GetPosition()), location) <= Distance))
ClientsInRange.push_back(it->second);
2013-02-16 16:14:39 -08:00
if (ClientsInRange.empty())
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
return ClientsInRange[zone->random.Int(0, ClientsInRange.size() - 1)];
2013-02-16 16:14:39 -08:00
}
Corpse *EntityList::GetCorpseByOwner(Client *client)
{
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (it->second->IsPlayerCorpse())
if (strcasecmp(it->second->GetOwnerName(), client->GetName()) == 0)
return it->second;
++it;
}
return nullptr;
}
Corpse *EntityList::GetCorpseByOwnerWithinRange(Client *client, Mob *center, int range)
{
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (it->second->IsPlayerCorpse())
if (DistanceSquaredNoZ(center->GetPosition(), it->second->GetPosition()) < range &&
strcasecmp(it->second->GetOwnerName(), client->GetName()) == 0)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Corpse *EntityList::GetCorpseByDBID(uint32 dbid)
{
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (it->second->GetCorpseDBID() == dbid)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Corpse *EntityList::GetCorpseByName(const char *name)
{
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (strcmp(it->second->GetName(), name) == 0)
return it->second;
++it;
2013-02-16 16:14:39 -08:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Spawn2 *EntityList::GetSpawnByID(uint32 id)
{
if (!zone || !zone->IsLoaded())
return nullptr;
LinkedListIterator<Spawn2 *> iterator(zone->spawn2_list);
iterator.Reset();
while(iterator.MoreElements())
{
if(iterator.GetData()->GetID() == id) {
return iterator.GetData();
}
iterator.Advance();
}
return nullptr;
}
2013-02-16 16:14:39 -08:00
void EntityList::RemoveAllCorpsesByCharID(uint32 charid)
{
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (it->second->GetCharID() == charid) {
safe_delete(it->second);
free_ids.push(it->first);
it = corpse_list.erase(it);
} else {
++it;
}
2013-02-16 16:14:39 -08:00
}
}
void EntityList::RemoveCorpseByDBID(uint32 dbid)
{
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (it->second->GetCorpseDBID() == dbid) {
safe_delete(it->second);
free_ids.push(it->first);
it = corpse_list.erase(it);
} else {
++it;
}
2013-02-16 16:14:39 -08:00
}
}
int EntityList::RezzAllCorpsesByCharID(uint32 charid)
{
int RezzExp = 0;
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (it->second->GetCharID() == charid) {
RezzExp += it->second->GetRezExp();
it->second->IsRezzed(true);
it->second->CompleteResurrection();
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
return RezzExp;
}
Group *EntityList::GetGroupByMob(Mob *mob)
2013-05-06 13:07:41 -04:00
{
std::list<Group *>::iterator iterator;
2013-02-16 16:14:39 -08:00
iterator = group_list.begin();
while (iterator != group_list.end()) {
if ((*iterator)->IsGroupMember(mob))
2013-02-16 16:14:39 -08:00
return *iterator;
++iterator;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
#if EQDEBUG >= 5
CheckGroupList (__FILE__, __LINE__);
#endif
return nullptr;
2013-02-16 16:14:39 -08:00
}
Group *EntityList::GetGroupByLeaderName(const char *leader)
{
std::list<Group *>::iterator iterator;
2013-02-16 16:14:39 -08:00
iterator = group_list.begin();
while (iterator != group_list.end()) {
if (!strcmp((*iterator)->GetLeaderName(), leader))
2013-02-16 16:14:39 -08:00
return *iterator;
++iterator;
2013-02-16 16:14:39 -08:00
}
#if EQDEBUG >= 5
CheckGroupList (__FILE__, __LINE__);
#endif
return nullptr;
2013-02-16 16:14:39 -08:00
}
Group *EntityList::GetGroupByID(uint32 group_id)
{
std::list<Group *>::iterator iterator;
2013-02-16 16:14:39 -08:00
iterator = group_list.begin();
while (iterator != group_list.end()) {
if ((*iterator)->GetID() == group_id)
2013-02-16 16:14:39 -08:00
return *iterator;
++iterator;
2013-02-16 16:14:39 -08:00
}
#if EQDEBUG >= 5
CheckGroupList (__FILE__, __LINE__);
#endif
return nullptr;
2013-02-16 16:14:39 -08:00
}
Group *EntityList::GetGroupByClient(Client *client)
2013-05-06 13:07:41 -04:00
{
std::list <Group *>::iterator iterator;
2013-02-16 16:14:39 -08:00
iterator = group_list.begin();
while (iterator != group_list.end()) {
if ((*iterator)->IsGroupMember(client->CastToMob()))
2013-02-16 16:14:39 -08:00
return *iterator;
++iterator;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
#if EQDEBUG >= 5
CheckGroupList (__FILE__, __LINE__);
#endif
return nullptr;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
Raid *EntityList::GetRaidByLeaderName(const char *leader)
{
std::list<Raid *>::iterator iterator;
2013-02-16 16:14:39 -08:00
iterator = raid_list.begin();
while (iterator != raid_list.end()) {
if ((*iterator)->GetLeader())
if(strcmp((*iterator)->GetLeader()->GetName(), leader) == 0)
2013-02-16 16:14:39 -08:00
return *iterator;
++iterator;
2013-05-06 13:07:41 -04:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Raid *EntityList::GetRaidByID(uint32 id)
{
std::list<Raid *>::iterator iterator;
2013-02-16 16:14:39 -08:00
iterator = raid_list.begin();
while (iterator != raid_list.end()) {
if ((*iterator)->GetID() == id)
2013-02-16 16:14:39 -08:00
return *iterator;
++iterator;
2013-05-06 13:07:41 -04:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Raid *EntityList::GetRaidByClient(Client* client)
2013-05-06 13:07:41 -04:00
{
if (client->p_raid_instance) {
return client->p_raid_instance;
}
2013-02-16 16:14:39 -08:00
std::list<Raid *>::iterator iterator;
2013-02-16 16:14:39 -08:00
iterator = raid_list.begin();
while (iterator != raid_list.end()) {
for (auto &member : (*iterator)->members) {
if (member.member) {
if (member.member == client) {
client->p_raid_instance = *iterator;
2013-02-16 16:14:39 -08:00
return *iterator;
}
}
}
++iterator;
2013-05-06 13:07:41 -04:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Raid *EntityList::GetRaidByMob(Mob *mob)
{
std::list<Raid *>::iterator iterator;
2013-02-16 16:14:39 -08:00
iterator = raid_list.begin();
while (iterator != raid_list.end()) {
for(int x = 0; x < MAX_RAID_MEMBERS; x++) {
// TODO: Implement support for Mob objects in Raid class
/*if((*iterator)->members[x].member){
if((*iterator)->members[x].member == mob)
return *iterator;
}*/
2013-02-16 16:14:39 -08:00
}
++iterator;
2013-05-06 13:07:41 -04:00
}
return nullptr;
2013-05-06 13:07:41 -04:00
}
Client *EntityList::GetClientByAccID(uint32 accid)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->AccountID() == accid)
return it->second;
++it;
2013-05-06 13:07:41 -04:00
}
return nullptr;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
void EntityList::ChannelMessageFromWorld(const char *from, const char *to,
uint8 chan_num, uint32 guild_id, uint8 language, uint8 lang_skill, const char *message)
{
for (auto it = client_list.begin(); it != client_list.end(); ++it) {
Client *client = it->second;
if (chan_num == ChatChannel_Guild) {
if (!client->IsInGuild(guild_id))
2013-02-16 16:14:39 -08:00
continue;
if (!guild_mgr.CheckPermission(guild_id, client->GuildRank(), GUILD_HEAR))
2013-02-16 16:14:39 -08:00
continue;
if (client->GetFilter(FilterGuildChat) == FilterHide)
2013-02-16 16:14:39 -08:00
continue;
} else if (chan_num == ChatChannel_OOC) {
if (client->GetFilter(FilterOOC) == FilterHide)
2013-02-16 16:14:39 -08:00
continue;
}
client->ChannelMessageSend(from, to, chan_num, language, lang_skill, message);
2013-02-16 16:14:39 -08:00
}
}
void EntityList::Message(uint32 to_guilddbid, uint32 type, const char *message, ...)
{
2013-02-16 16:14:39 -08:00
va_list argptr;
char buffer[4096];
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
va_start(argptr, message);
vsnprintf(buffer, 4096, message, argptr);
va_end(argptr);
2013-05-06 13:07:41 -04:00
auto it = client_list.begin();
while (it != client_list.end()) {
Client *client = it->second;
2013-02-16 16:14:39 -08:00
if (to_guilddbid == 0 || client->IsInGuild(to_guilddbid))
client->Message(type, buffer);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::QueueClientsGuild(Mob *sender, const EQApplicationPacket *app,
bool ignore_sender, uint32 guild_id)
{
auto it = client_list.begin();
while (it != client_list.end()) {
Client *client = it->second;
2013-02-16 16:14:39 -08:00
if (client->IsInGuild(guild_id))
client->QueuePacket(app);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::QueueClientsGuildBankItemUpdate(const GuildBankItemUpdate_Struct *gbius, uint32 GuildID)
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankItemUpdate_Struct));
2013-02-16 16:14:39 -08:00
GuildBankItemUpdate_Struct *outgbius = (GuildBankItemUpdate_Struct*)outapp->pBuffer;
memcpy(outgbius, gbius, sizeof(GuildBankItemUpdate_Struct));
const EQ::ItemData *Item = database.GetItem(gbius->ItemID);
2013-02-16 16:14:39 -08:00
auto it = client_list.begin();
while (it != client_list.end()) {
Client *client = it->second;
2013-02-16 16:14:39 -08:00
if (client->IsInGuild(GuildID)) {
if (Item && (gbius->Permissions == GuildBankPublicIfUsable))
2013-02-16 16:14:39 -08:00
outgbius->Useable = Item->IsEquipable(client->GetBaseRace(), client->GetBaseClass());
client->QueuePacket(outapp);
}
++it;
2013-02-16 16:14:39 -08:00
}
safe_delete(outapp);
}
void EntityList::MessageStatus(uint32 to_guild_id, int to_minstatus, uint32 type, const char *message, ...)
{
2013-02-16 16:14:39 -08:00
va_list argptr;
2019-08-11 00:14:02 -05:00
char buffer[4096];
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
va_start(argptr, message);
vsnprintf(buffer, 4096, message, argptr);
va_end(argptr);
2013-05-06 13:07:41 -04:00
auto it = client_list.begin();
while (it != client_list.end()) {
Client *client = it->second;
2019-08-11 00:14:02 -05:00
if ((to_guild_id == 0 || client->IsInGuild(to_guild_id)) && client->Admin() >= to_minstatus) {
2013-02-16 16:14:39 -08:00
client->Message(type, buffer);
2019-08-11 00:14:02 -05:00
}
++it;
2013-02-16 16:14:39 -08:00
}
}
2019-08-11 00:14:02 -05:00
/**
* @param sender
* @param skipsender
* @param dist
* @param type
* @param string_id
* @param message1
* @param message2
* @param message3
* @param message4
* @param message5
* @param message6
* @param message7
* @param message8
* @param message9
*/
void EntityList::MessageCloseString(
Mob *sender,
bool skipsender,
float dist,
uint32 type,
uint32 string_id,
const char *message1,
const char *message2,
const char *message3,
const char *message4,
const char *message5,
const char *message6,
const char *message7,
const char *message8,
const char *message9
)
2013-02-16 16:14:39 -08:00
{
Client *c;
2019-08-11 00:14:02 -05:00
float dist2 = dist * dist;
for (auto & it : client_list) {
c = it.second;
if (c && DistanceSquared(c->GetPosition(), sender->GetPosition()) <= dist2 && (!skipsender || c != sender)) {
c->MessageString(
type,
string_id,
message1,
message2,
message3,
message4,
message5,
message6,
message7,
message8,
message9
);
}
2013-02-16 16:14:39 -08:00
}
}
2019-08-11 00:00:55 -05:00
/**
* @param sender
* @param skipsender
* @param dist
* @param type
* @param filter
* @param string_id
* @param message1
* @param message2
* @param message3
* @param message4
* @param message5
* @param message6
* @param message7
* @param message8
* @param message9
*/
2019-08-11 00:14:02 -05:00
void EntityList::FilteredMessageCloseString(
Mob *sender, bool skipsender,
float dist,
uint32 type,
eqFilterType filter,
uint32 string_id,
const char *message1,
const char *message2,
const char *message3,
const char *message4,
const char *message5,
const char *message6,
const char *message7,
const char *message8,
const char *message9
)
{
Client *c;
2019-08-11 00:14:02 -05:00
float dist2 = dist * dist;
for (auto & it : client_list) {
c = it.second;
if (c && DistanceSquared(c->GetPosition(), sender->GetPosition()) <= dist2 && (!skipsender || c != sender)) {
c->FilteredMessageString(
sender, type, filter, string_id,
message1, message2, message3, message4, message5,
message6, message7, message8, message9
);
}
}
}
2019-08-11 00:14:02 -05:00
/**
*
* @param sender
* @param skipsender
* @param type
* @param string_id
* @param message1
* @param message2
* @param message3
* @param message4
* @param message5
* @param message6
* @param message7
* @param message8
* @param message9
*/
void EntityList::MessageString(
Mob *sender,
bool skipsender,
uint32 type,
uint32 string_id,
const char *message1,
const char *message2,
const char *message3,
const char *message4,
const char *message5,
const char *message6,
const char *message7,
const char *message8,
const char *message9
)
2013-02-16 16:14:39 -08:00
{
Client *c;
2013-05-06 13:07:41 -04:00
2019-08-11 00:14:02 -05:00
for (auto & it : client_list) {
c = it.second;
if (c && (!skipsender || c != sender)) {
c->MessageString(
type,
string_id,
message1,
message2,
message3,
message4,
message5,
message6,
message7,
message8,
message9
);
}
2013-02-16 16:14:39 -08:00
}
}
2019-08-11 00:14:02 -05:00
/**
*
* @param sender
* @param skipsender
* @param type
* @param filter
* @param string_id
* @param message1
* @param message2
* @param message3
* @param message4
* @param message5
* @param message6
* @param message7
* @param message8
* @param message9
*/
void EntityList::FilteredMessageString(
Mob *sender,
bool skipsender,
uint32 type,
eqFilterType filter,
uint32 string_id,
const char *message1,
const char *message2,
const char *message3,
const char *message4,
const char *message5,
const char *message6,
const char *message7,
const char *message8,
const char *message9
)
{
Client *c;
2019-08-11 00:14:02 -05:00
for (auto & it : client_list) {
c = it.second;
if (c && (!skipsender || c != sender)) {
c->FilteredMessageString(
sender,
type,
filter,
string_id,
message1,
message2,
message3,
message4,
message5,
message6,
message7,
message8,
message9
);
}
}
}
2019-08-11 00:14:02 -05:00
/**
* @param sender
* @param skipsender
* @param dist
* @param type
* @param message
* @param ...
*/
void EntityList::MessageClose(Mob *sender, bool skipsender, float dist, uint32 type, const char *message, ...)
{
2013-02-16 16:14:39 -08:00
va_list argptr;
2019-08-11 00:14:02 -05:00
char buffer[4096];
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
va_start(argptr, message);
vsnprintf(buffer, 4095, message, argptr);
va_end(argptr);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
float dist2 = dist * dist;
2013-05-06 13:07:41 -04:00
auto it = client_list.begin();
while (it != client_list.end()) {
2019-08-11 00:14:02 -05:00
if (DistanceSquared(it->second->GetPosition(), sender->GetPosition()) <= dist2 &&
(!skipsender || it->second != sender)) {
it->second->Message(type, buffer);
2019-08-11 00:14:02 -05:00
}
++it;
2013-02-16 16:14:39 -08:00
}
}
2019-08-11 00:14:02 -05:00
void EntityList::FilteredMessageClose(
Mob *sender,
bool skipsender,
float dist,
uint32 type,
eqFilterType filter,
const char *message,
...
)
2017-05-19 22:50:08 -07:00
{
va_list argptr;
2019-08-11 00:14:02 -05:00
char buffer[4096];
2017-05-19 22:50:08 -07:00
va_start(argptr, message);
vsnprintf(buffer, 4095, message, argptr);
va_end(argptr);
float dist2 = dist * dist;
auto it = client_list.begin();
while (it != client_list.end()) {
2019-08-11 00:14:02 -05:00
if (DistanceSquared(it->second->GetPosition(), sender->GetPosition()) <= dist2 &&
(!skipsender || it->second != sender)) {
it->second->FilteredMessage(sender, type, filter, buffer);
}
2017-05-19 22:50:08 -07:00
++it;
}
}
void EntityList::RemoveAllMobs()
{
auto it = mob_list.begin();
while (it != mob_list.end()) {
safe_delete(it->second);
free_ids.push(it->first);
it = mob_list.erase(it);
}
2013-02-16 16:14:39 -08:00
}
void EntityList::RemoveAllClients()
{
// doesn't clear the data
client_list.clear();
2013-02-16 16:14:39 -08:00
}
void EntityList::RemoveAllNPCs()
{
// doesn't clear the data
npc_list.clear();
2013-02-16 16:14:39 -08:00
npc_limit_list.clear();
}
void EntityList::RemoveAllMercs()
{
2015-01-03 02:57:55 -05:00
// doesn't clear the data
merc_list.clear();
2013-02-16 16:14:39 -08:00
}
void EntityList::RemoveAllGroups()
{
2015-01-03 02:57:55 -05:00
while (group_list.size()) {
auto group = group_list.front();
2013-02-16 16:14:39 -08:00
group_list.pop_front();
delete group;
2015-01-03 02:57:55 -05:00
}
2013-02-16 16:14:39 -08:00
#if EQDEBUG >= 5
CheckGroupList (__FILE__, __LINE__);
#endif
}
void EntityList::RemoveAllRaids()
{
2015-01-03 02:57:55 -05:00
while (raid_list.size()) {
auto raid = raid_list.front();
2013-02-16 16:14:39 -08:00
raid_list.pop_front();
delete raid;
2015-01-03 02:57:55 -05:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::RemoveAllDoors()
{
auto it = door_list.begin();
while (it != door_list.end()) {
safe_delete(it->second);
free_ids.push(it->first);
it = door_list.erase(it);
}
2013-02-16 16:14:39 -08:00
DespawnAllDoors();
}
void EntityList::DespawnAllDoors()
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_RemoveAllDoors, 0);
2013-02-16 16:14:39 -08:00
this->QueueClients(0,outapp);
safe_delete(outapp);
}
void EntityList::RespawnAllDoors()
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second) {
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket();
MakeDoorSpawnPacket(outapp, it->second);
it->second->FastQueuePacket(&outapp);
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::RemoveAllCorpses()
{
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
safe_delete(it->second);
free_ids.push(it->first);
it = corpse_list.erase(it);
}
2013-02-16 16:14:39 -08:00
}
void EntityList::RemoveAllObjects()
{
auto it = object_list.begin();
while (it != object_list.end()) {
safe_delete(it->second);
free_ids.push(it->first);
it = object_list.erase(it);
}
2013-02-16 16:14:39 -08:00
}
void EntityList::RemoveAllTraps()
{
auto it = trap_list.begin();
while (it != trap_list.end()) {
safe_delete(it->second);
free_ids.push(it->first);
it = trap_list.erase(it);
}
2013-02-16 16:14:39 -08:00
}
void EntityList::RemoveAllEncounters()
{
auto it = encounter_list.begin();
while (it != encounter_list.end()) {
safe_delete(it->second);
free_ids.push(it->first);
it = encounter_list.erase(it);
}
}
2019-12-28 17:08:34 -06:00
/**
* @param delete_id
* @return
*/
bool EntityList::RemoveMob(uint16 delete_id)
{
2019-12-28 17:08:34 -06:00
if (delete_id == 0) {
return true;
2019-12-28 17:08:34 -06:00
}
auto it = mob_list.find(delete_id);
if (it != mob_list.end()) {
2019-12-28 17:08:34 -06:00
if (npc_list.count(delete_id)) {
entity_list.RemoveNPC(delete_id);
2019-12-28 17:08:34 -06:00
}
else if (client_list.count(delete_id)) {
entity_list.RemoveClient(delete_id);
2019-12-28 17:08:34 -06:00
}
safe_delete(it->second);
2019-12-28 17:08:34 -06:00
if (!corpse_list.count(delete_id)) {
free_ids.push(it->first);
2019-12-28 17:08:34 -06:00
}
mob_list.erase(it);
2013-02-16 16:14:39 -08:00
return true;
}
return false;
}
2019-12-28 17:08:34 -06:00
/**
* @param delete_mob
* @return
*/
bool EntityList::RemoveMob(Mob *delete_mob)
{
if (delete_mob == 0) {
2013-02-16 16:14:39 -08:00
return true;
}
auto it = mob_list.begin();
while (it != mob_list.end()) {
if (it->second == delete_mob) {
safe_delete(it->second);
if (!corpse_list.count(it->first)) {
free_ids.push(it->first);
}
mob_list.erase(it);
2013-02-16 16:14:39 -08:00
return true;
}
++it;
2013-02-16 16:14:39 -08:00
}
return false;
}
2019-12-28 17:08:34 -06:00
/**
* @param delete_id
* @return
*/
bool EntityList::RemoveNPC(uint16 delete_id)
{
auto it = npc_list.find(delete_id);
if (it != npc_list.end()) {
NPC *npc = it->second;
RemoveProximity(delete_id);
npc_list.erase(it);
if (npc_limit_list.count(delete_id)) {
npc_limit_list.erase(delete_id);
}
return true;
2013-02-16 16:14:39 -08:00
}
return false;
}
/**
* @param mob
* @return
*/
bool EntityList::RemoveMobFromCloseLists(Mob *mob)
{
2019-12-29 13:30:05 -06:00
uint16 entity_id = mob->GetID() > 0 ? mob->GetID() : mob->GetInitialId();
LogEntityManagement(
"Attempting to remove mob [{}] from close lists entity_id ({})",
mob->GetCleanName(),
2019-12-29 13:30:05 -06:00
entity_id
);
2019-12-28 17:08:34 -06:00
auto it = mob_list.begin();
while (it != mob_list.end()) {
2019-12-29 13:30:05 -06:00
LogEntityManagement(
"Removing mob [{}] from [{}] close list entity_id ({})",
mob->GetCleanName(),
it->second->GetCleanName(),
2019-12-29 13:30:05 -06:00
entity_id
);
2019-12-29 13:30:05 -06:00
it->second->close_mobs.erase(entity_id);
++it;
}
2019-12-28 17:08:34 -06:00
return false;
}
/**
* @param mob
* @return
*/
void EntityList::RemoveAuraFromMobs(Mob *aura)
{
LogEntityManagement(
"Attempting to remove aura [{}] from mobs entity_id ({})",
aura->GetCleanName(),
aura->GetID()
);
for (auto &it : mob_list) {
auto mob = it.second;
mob->RemoveAura(aura->GetID());
}
}
/**
* @param close_mobs
* @param scanning_mob
*/
void EntityList::ScanCloseMobs(std::unordered_map<uint16, Mob *> &close_mobs, Mob *scanning_mob)
{
float scan_range = RuleI(Range, MobCloseScanDistance) * RuleI(Range, MobCloseScanDistance);
close_mobs.clear();
2019-12-29 17:20:39 -06:00
for (auto &e : mob_list) {
auto mob = e.second;
2019-12-29 17:10:53 -06:00
if (!mob->IsNPC() && !mob->IsClient()) {
continue;
}
2019-12-29 17:20:39 -06:00
if (mob->GetID() <= 0) {
continue;
}
float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition());
if (distance <= scan_range) {
close_mobs.insert(std::pair<uint16, Mob *>(mob->GetID(), mob));
}
2019-12-29 17:20:39 -06:00
else if (mob->GetAggroRange() >= scan_range) {
close_mobs.insert(std::pair<uint16, Mob *>(mob->GetID(), mob));
}
}
LogAIScanClose(
"[{}] Scanning Close List | list_size [{}] moving [{}]",
scanning_mob->GetCleanName(),
close_mobs.size(),
scanning_mob->IsMoving() ? "true" : "false"
);
}
bool EntityList::RemoveMerc(uint16 delete_id)
{
auto it = merc_list.find(delete_id);
if (it != merc_list.end()) {
merc_list.erase(it); // Already Deleted
return true;
2013-02-16 16:14:39 -08:00
}
return false;
}
bool EntityList::RemoveClient(uint16 delete_id)
{
auto it = client_list.find(delete_id);
if (it != client_list.end()) {
client_list.erase(it); // Already deleted
return true;
2013-02-16 16:14:39 -08:00
}
return false;
}
// If our ID was deleted already
bool EntityList::RemoveClient(Client *delete_client)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second == delete_client) {
client_list.erase(it);
2013-02-16 16:14:39 -08:00
return true;
}
++it;
2013-02-16 16:14:39 -08:00
}
return false;
}
bool EntityList::RemoveObject(uint16 delete_id)
{
auto it = object_list.find(delete_id);
if (it != object_list.end()) {
safe_delete(it->second);
free_ids.push(it->first);
object_list.erase(it);
return true;
2013-02-16 16:14:39 -08:00
}
return false;
}
bool EntityList::RemoveTrap(uint16 delete_id)
{
auto it = trap_list.find(delete_id);
if (it != trap_list.end()) {
safe_delete(it->second);
free_ids.push(it->first);
trap_list.erase(it);
return true;
2013-02-16 16:14:39 -08:00
}
return false;
}
bool EntityList::RemoveDoor(uint16 delete_id)
{
auto it = door_list.find(delete_id);
if (it != door_list.end()) {
safe_delete(it->second);
free_ids.push(it->first);
door_list.erase(it);
return true;
2013-02-16 16:14:39 -08:00
}
return false;
}
bool EntityList::RemoveCorpse(uint16 delete_id)
{
auto it = corpse_list.find(delete_id);
if (it != corpse_list.end()) {
safe_delete(it->second);
free_ids.push(it->first);
corpse_list.erase(it);
return true;
2013-02-16 16:14:39 -08:00
}
return false;
}
bool EntityList::RemoveGroup(uint32 delete_id)
{
auto it = std::find_if(group_list.begin(), group_list.end(),
[delete_id](const Group *a) { return a->GetID() == delete_id; });
if (it == group_list.end()) {
2013-02-16 16:14:39 -08:00
#if EQDEBUG >= 5
CheckGroupList (__FILE__, __LINE__);
2013-02-16 16:14:39 -08:00
#endif
return false;
2013-02-16 16:14:39 -08:00
}
auto group = *it;
group_list.erase(it);
delete group;
return true;
2013-02-16 16:14:39 -08:00
}
bool EntityList::RemoveRaid(uint32 delete_id)
{
auto it = std::find_if(raid_list.begin(), raid_list.end(),
[delete_id](const Raid *a) { return a->GetID() == delete_id; });
if (it == raid_list.end())
return false;
auto raid = *it;
raid_list.erase(it);
delete raid;
return true;
2013-02-16 16:14:39 -08:00
}
void EntityList::Clear()
{
RemoveAllClients();
entity_list.RemoveAllTraps(); //we can have child npcs so we go first
entity_list.RemoveAllNPCs();
entity_list.RemoveAllMobs();
entity_list.RemoveAllCorpses();
entity_list.RemoveAllGroups();
entity_list.RemoveAllDoors();
entity_list.RemoveAllObjects();
entity_list.RemoveAllRaids();
entity_list.RemoveAllLocalities();
}
void EntityList::UpdateWho(bool iSendFullUpdate)
{
if ((!worldserver.Connected()) || !is_zone_loaded)
2013-02-16 16:14:39 -08:00
return;
uint32 tmpNumUpdates = numclients + 5;
ServerPacket* pack = 0;
ServerClientListKeepAlive_Struct* sclka = 0;
if (!iSendFullUpdate) {
pack = new ServerPacket(ServerOP_ClientListKA, sizeof(ServerClientListKeepAlive_Struct) + (tmpNumUpdates * 4));
sclka = (ServerClientListKeepAlive_Struct*) pack->pBuffer;
}
2013-05-06 13:07:41 -04:00
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->InZone()) {
2013-02-16 16:14:39 -08:00
if (iSendFullUpdate) {
it->second->UpdateWho();
} else {
2013-02-16 16:14:39 -08:00
if (sclka->numupdates >= tmpNumUpdates) {
tmpNumUpdates += 10;
uint8* tmp = pack->pBuffer;
pack->pBuffer = new uint8[sizeof(ServerClientListKeepAlive_Struct) + (tmpNumUpdates * 4)];
memset(pack->pBuffer, 0, sizeof(ServerClientListKeepAlive_Struct) + (tmpNumUpdates * 4));
memcpy(pack->pBuffer, tmp, pack->size);
pack->size = sizeof(ServerClientListKeepAlive_Struct) + (tmpNumUpdates * 4);
safe_delete_array(tmp);
sclka = (ServerClientListKeepAlive_Struct*) pack->pBuffer;
2013-02-16 16:14:39 -08:00
}
sclka->wid[sclka->numupdates] = it->second->GetWID();
2013-02-16 16:14:39 -08:00
sclka->numupdates++;
}
}
++it;
2013-02-16 16:14:39 -08:00
}
if (!iSendFullUpdate) {
pack->size = sizeof(ServerClientListKeepAlive_Struct) + (sclka->numupdates * 4);
worldserver.SendPacket(pack);
safe_delete(pack);
}
}
void EntityList::RemoveEntity(uint16 id)
{
if (id == 0)
return;
if (entity_list.RemoveMob(id))
2013-02-16 16:14:39 -08:00
return;
else if (entity_list.RemoveCorpse(id))
2013-02-16 16:14:39 -08:00
return;
else if (entity_list.RemoveDoor(id))
2013-02-16 16:14:39 -08:00
return;
else if (entity_list.RemoveGroup(id))
2013-02-16 16:14:39 -08:00
return;
else if (entity_list.RemoveTrap(id))
2013-02-16 16:14:39 -08:00
return;
else if (entity_list.RemoveMerc(id))
return;
2013-02-16 16:14:39 -08:00
#ifdef BOTS
// This block of code is necessary to clean up bot objects
else if (entity_list.RemoveBot(id))
2013-02-16 16:14:39 -08:00
return;
#endif //BOTS
2013-05-06 13:07:41 -04:00
else
2013-02-16 16:14:39 -08:00
entity_list.RemoveObject(id);
}
void EntityList::Process()
{
CheckSpawnQueue();
}
void EntityList::CountNPC(uint32 *NPCCount, uint32 *NPCLootCount, uint32 *gmspawntype_count)
{
2013-02-16 16:14:39 -08:00
*NPCCount = 0;
*NPCLootCount = 0;
2013-05-06 13:07:41 -04:00
auto it = npc_list.begin();
while (it != npc_list.end()) {
2013-02-16 16:14:39 -08:00
(*NPCCount)++;
(*NPCLootCount) += it->second->CountLoot();
if (it->second->GetNPCTypeID() == 0)
2013-02-16 16:14:39 -08:00
(*gmspawntype_count)++;
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::Depop(bool StartSpawnTimer)
{
for (auto it = npc_list.begin(); it != npc_list.end(); ++it) {
NPC *pnpc = it->second;
if (pnpc) {
Mob *own = pnpc->GetOwner();
2013-02-16 16:14:39 -08:00
//do not depop player's pets...
if (own && own->IsClient())
2013-02-16 16:14:39 -08:00
continue;
if (pnpc->IsHorse)
continue;
if (pnpc->IsFindable())
UpdateFindableNPCState(pnpc, true);
2013-02-16 16:14:39 -08:00
pnpc->WipeHateList();
pnpc->Depop(StartSpawnTimer);
2013-02-16 16:14:39 -08:00
}
}
}
void EntityList::DepopAll(int NPCTypeID, bool StartSpawnTimer)
{
for (auto it = npc_list.begin(); it != npc_list.end(); ++it) {
NPC *pnpc = it->second;
if (pnpc && (pnpc->GetNPCTypeID() == (uint32)NPCTypeID))
pnpc->Depop(StartSpawnTimer);
2013-02-16 16:14:39 -08:00
}
}
void EntityList::SendTraders(Client *client)
{
Client *trader = nullptr;
auto it = client_list.begin();
while (it != client_list.end()) {
trader = it->second;
if (trader->IsTrader())
2013-02-16 16:14:39 -08:00
client->SendTraderPacket(trader);
if (trader->IsBuyer())
2013-02-16 16:14:39 -08:00
client->SendBuyerPacket(trader);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::RemoveFromHateLists(Mob *mob, bool settoone)
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second->CheckAggro(mob)) {
2017-10-16 01:25:14 -04:00
if (!settoone) {
it->second->RemoveFromHateList(mob);
2017-10-16 01:25:14 -04:00
if (mob->IsClient())
mob->CastToClient()->RemoveXTarget(it->second, false); // gotta do book keeping
} else {
it->second->SetHateAmountOnEnt(mob, 1);
2017-10-16 01:25:14 -04:00
}
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::RemoveDebuffs(Mob *caster)
2013-02-16 16:14:39 -08:00
{
auto it = mob_list.begin();
while (it != mob_list.end()) {
it->second->BuffFadeDetrimentalByCaster(caster);
++it;
2013-02-16 16:14:39 -08:00
}
}
char *EntityList::MakeNameUnique(char *name)
{
2013-02-16 16:14:39 -08:00
bool used[300];
memset(used, 0, sizeof(used));
name[61] = 0; name[62] = 0; name[63] = 0;
int len = strlen(name);
auto it = mob_list.begin();
while (it != mob_list.end()) {
if (it->second->IsMob()) {
if (strncasecmp(it->second->CastToMob()->GetName(), name, len) == 0) {
if (Seperator::IsNumber(&it->second->CastToMob()->GetName()[len])) {
used[atoi(&it->second->CastToMob()->GetName()[len])] = true;
2013-02-16 16:14:39 -08:00
}
}
}
++it;
2013-02-16 16:14:39 -08:00
}
for (int i=0; i < 300; i++) {
if (!used[i]) {
#ifdef _WINDOWS
snprintf(name, 64, "%s%03d", name, i);
#else
//glibc clears destination of snprintf
//make a copy of name before snprintf--misanthropicfiend
char temp_name[64];
strn0cpy(temp_name, name, 64);
snprintf(name, 64, "%s%03d", temp_name, i);
#endif
return name;
}
}
2019-09-01 22:05:44 -05:00
LogError("Fatal error in EntityList::MakeNameUnique: Unable to find unique name for [{}]", name);
2013-02-16 16:14:39 -08:00
char tmp[64] = "!";
strn0cpy(&tmp[1], name, sizeof(tmp) - 1);
strcpy(name, tmp);
return MakeNameUnique(name);
}
char *EntityList::RemoveNumbers(char *name)
{
char tmp[64];
2013-02-16 16:14:39 -08:00
memset(tmp, 0, sizeof(tmp));
int k = 0;
for (unsigned int i=0; i<strlen(name) && i<sizeof(tmp); i++) {
if (name[i] < '0' || name[i] > '9')
tmp[k++] = name[i];
}
strn0cpy(name, tmp, sizeof(tmp));
return name;
}
void EntityList::ListNPCCorpses(Client *client)
{
2013-02-16 16:14:39 -08:00
uint32 x = 0;
2013-05-06 13:07:41 -04:00
auto it = corpse_list.begin();
client->Message(Chat::White, "NPC Corpses in the zone:");
while (it != corpse_list.end()) {
if (it->second->IsNPCCorpse()) {
client->Message(Chat::White, " %5d: %s", it->first, it->second->GetName());
2013-02-16 16:14:39 -08:00
x++;
}
++it;
2013-02-16 16:14:39 -08:00
}
client->Message(Chat::White, "%d npc corpses listed.", x);
2013-02-16 16:14:39 -08:00
}
void EntityList::ListPlayerCorpses(Client *client)
{
2013-02-16 16:14:39 -08:00
uint32 x = 0;
2013-05-06 13:07:41 -04:00
auto it = corpse_list.begin();
client->Message(Chat::White, "Player Corpses in the zone:");
while (it != corpse_list.end()) {
if (it->second->IsPlayerCorpse()) {
client->Message(Chat::White, " %5d: %s", it->first, it->second->GetName());
2013-02-16 16:14:39 -08:00
x++;
}
++it;
2013-02-16 16:14:39 -08:00
}
client->Message(Chat::White, "%d player corpses listed.", x);
2013-02-16 16:14:39 -08:00
}
// returns the number of corpses deleted. A negative number indicates an error code.
int32 EntityList::DeleteNPCCorpses()
{
2013-02-16 16:14:39 -08:00
int32 x = 0;
2013-05-06 13:07:41 -04:00
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (it->second->IsNPCCorpse()) {
it->second->DepopNPCCorpse();
2013-02-16 16:14:39 -08:00
x++;
}
++it;
2013-02-16 16:14:39 -08:00
}
return x;
}
void EntityList::CorpseFix(Client* c)
{
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
Corpse* corpse = it->second;
if (corpse->IsNPCCorpse()) {
if (DistanceNoZ(c->GetPosition(), corpse->GetPosition()) < 100) {
2019-08-11 00:00:55 -05:00
c->Message(Chat::Yellow, "Attempting to fix %s", it->second->GetCleanName());
corpse->GMMove(corpse->GetX(), corpse->GetY(), c->GetZ() + 2, 0);
}
}
++it;
}
}
2013-02-16 16:14:39 -08:00
// returns the number of corpses deleted. A negative number indicates an error code.
int32 EntityList::DeletePlayerCorpses()
{
2013-02-16 16:14:39 -08:00
int32 x = 0;
2013-05-06 13:07:41 -04:00
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
if (it->second->IsPlayerCorpse()) {
it->second->CastToCorpse()->Delete();
2013-02-16 16:14:39 -08:00
x++;
}
++it;
2013-02-16 16:14:39 -08:00
}
return x;
}
void EntityList::SendPetitionToAdmins()
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_PetitionUpdate, sizeof(PetitionUpdate_Struct));
PetitionUpdate_Struct *pcus = (PetitionUpdate_Struct*) outapp->pBuffer;
2013-02-16 16:14:39 -08:00
pcus->petnumber = 0; // Petition Number
pcus->color = 0;
pcus->status = 0xFFFFFFFF;
pcus->senttime = 0;
strcpy(pcus->accountid, "");
strcpy(pcus->gmsenttoo, "");
pcus->quetotal=0;
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->CastToClient()->Admin() >= 80)
it->second->CastToClient()->QueuePacket(outapp);
++it;
2013-02-16 16:14:39 -08:00
}
safe_delete(outapp);
}
2013-05-06 13:07:41 -04:00
void EntityList::SendPetitionToAdmins(Petition *pet)
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_PetitionUpdate, sizeof(PetitionUpdate_Struct));
PetitionUpdate_Struct *pcus = (PetitionUpdate_Struct*) outapp->pBuffer;
2013-02-16 16:14:39 -08:00
pcus->petnumber = pet->GetID(); // Petition Number
if (pet->CheckedOut()) {
pcus->color = 0x00;
pcus->status = 0xFFFFFFFF;
pcus->senttime = pet->GetSentTime();
strcpy(pcus->accountid, "");
strcpy(pcus->gmsenttoo, "");
} else {
2013-02-16 16:14:39 -08:00
pcus->color = pet->GetUrgency(); // 0x00 = green, 0x01 = yellow, 0x02 = red
pcus->status = pet->GetSentTime();
pcus->senttime = pet->GetSentTime(); // 4 has to be 0x1F
strcpy(pcus->accountid, pet->GetAccountName());
strcpy(pcus->charname, pet->GetCharName());
}
pcus->quetotal = petition_list.GetTotalPetitions();
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->CastToClient()->Admin() >= 80) {
2013-02-16 16:14:39 -08:00
if (pet->CheckedOut())
strcpy(pcus->gmsenttoo, "");
else
strcpy(pcus->gmsenttoo, it->second->CastToClient()->GetName());
it->second->CastToClient()->QueuePacket(outapp);
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
safe_delete(outapp);
}
void EntityList::ClearClientPetitionQueue()
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_PetitionUpdate, sizeof(PetitionUpdate_Struct));
PetitionUpdate_Struct *pet = (PetitionUpdate_Struct*) outapp->pBuffer;
2013-02-16 16:14:39 -08:00
pet->color = 0x00;
pet->status = 0xFFFFFFFF;
pet->senttime = 0;
strcpy(pet->accountid, "");
strcpy(pet->gmsenttoo, "");
strcpy(pet->charname, "");
pet->quetotal = petition_list.GetTotalPetitions();
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->CastToClient()->Admin() >= 100) {
2013-02-16 16:14:39 -08:00
int x = 0;
for (x = 0; x < 64; x++) {
2013-02-16 16:14:39 -08:00
pet->petnumber = x;
it->second->CastToClient()->QueuePacket(outapp);
2013-02-16 16:14:39 -08:00
}
}
++it;
2013-02-16 16:14:39 -08:00
}
safe_delete(outapp);
return;
}
void EntityList::WriteEntityIDs()
{
auto it = mob_list.begin();
while (it != mob_list.end()) {
std::cout << "ID: " << it->first << " Name: " << it->second->GetName() << std::endl;
++it;
2013-02-16 16:14:39 -08:00
}
}
BulkZoneSpawnPacket::BulkZoneSpawnPacket(Client *iSendTo, uint32 iMaxSpawnsPerPacket)
{
data = nullptr;
2013-02-16 16:14:39 -08:00
pSendTo = iSendTo;
pMaxSpawnsPerPacket = iMaxSpawnsPerPacket;
}
BulkZoneSpawnPacket::~BulkZoneSpawnPacket()
{
2013-02-16 16:14:39 -08:00
SendBuffer();
safe_delete_array(data);
2013-02-16 16:14:39 -08:00
}
bool BulkZoneSpawnPacket::AddSpawn(NewSpawn_Struct *ns)
{
2013-02-16 16:14:39 -08:00
if (!data) {
data = new NewSpawn_Struct[pMaxSpawnsPerPacket];
memset(data, 0, sizeof(NewSpawn_Struct) * pMaxSpawnsPerPacket);
index = 0;
}
memcpy(&data[index], ns, sizeof(NewSpawn_Struct));
index++;
if (index >= pMaxSpawnsPerPacket) {
SendBuffer();
return true;
}
return false;
}
void BulkZoneSpawnPacket::SendBuffer()
{
2013-02-16 16:14:39 -08:00
if (!data)
return;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
uint32 tmpBufSize = (index * sizeof(NewSpawn_Struct));
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_ZoneSpawns, (unsigned char *)data, tmpBufSize);
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
if (pSendTo) {
pSendTo->FastQueuePacket(&outapp);
} else {
entity_list.QueueClients(0, outapp);
safe_delete(outapp);
}
memset(data, 0, sizeof(NewSpawn_Struct) * pMaxSpawnsPerPacket);
index = 0;
}
void EntityList::DoubleAggro(Mob *who)
2013-02-16 16:14:39 -08:00
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second->CheckAggro(who))
it->second->SetHateAmountOnEnt(who, it->second->CastToNPC()->GetHateAmount(who),
it->second->CastToNPC()->GetHateAmount(who) * 2);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::HalveAggro(Mob *who)
2013-02-16 16:14:39 -08:00
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second->CastToNPC()->CheckAggro(who))
it->second->CastToNPC()->SetHateAmountOnEnt(who, it->second->CastToNPC()->GetHateAmount(who) / 2);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::Evade(Mob *who)
{
uint32 flatval = who->GetLevel() * 13;
int amt = 0;
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second->CastToNPC()->CheckAggro(who)) {
amt = it->second->CastToNPC()->GetHateAmount(who);
2013-02-16 16:14:39 -08:00
amt -= flatval;
if (amt > 0)
it->second->CastToNPC()->SetHateAmountOnEnt(who, amt);
2013-02-16 16:14:39 -08:00
else
it->second->CastToNPC()->SetHateAmountOnEnt(who, 0);
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
}
//removes "targ" from all hate lists, including feigned, in the zone
void EntityList::ClearAggro(Mob* targ)
{
2017-03-02 14:31:48 -05:00
Client *c = nullptr;
if (targ->IsClient()) {
2017-03-02 14:31:48 -05:00
c = targ->CastToClient();
}
auto it = npc_list.begin();
while (it != npc_list.end()) {
2017-03-02 14:31:48 -05:00
if (it->second->CheckAggro(targ)) {
if (c) {
2017-03-02 14:31:48 -05:00
c->RemoveXTarget(it->second, false);
}
it->second->RemoveFromHateList(targ);
2017-03-02 14:31:48 -05:00
}
2017-10-16 01:25:14 -04:00
if (c && it->second->IsOnFeignMemory(c)) {
it->second->RemoveFromFeignMemory(c); //just in case we feigned
c->RemoveXTarget(it->second, false);
}
++it;
2013-02-16 16:14:39 -08:00
}
}
2020-04-02 12:03:45 -04:00
//removes "targ" from all hate lists of mobs that are water only.
void EntityList::ClearWaterAggro(Mob* targ)
{
Client *c = nullptr;
if (targ->IsClient()) {
2020-04-02 12:03:45 -04:00
c = targ->CastToClient();
}
2020-04-02 12:03:45 -04:00
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second->IsUnderwaterOnly()) {
if (it->second->CheckAggro(targ)) {
if (c) {
2020-04-02 12:03:45 -04:00
c->RemoveXTarget(it->second, false);
}
2020-04-02 12:03:45 -04:00
it->second->RemoveFromHateList(targ);
}
if (c && it->second->IsOnFeignMemory(c)) {
it->second->RemoveFromFeignMemory(c); //just in case we feigned
c->RemoveXTarget(it->second, false);
}
}
++it;
}
}
void EntityList::ClearFeignAggro(Mob *targ)
2013-02-16 16:14:39 -08:00
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
2017-10-16 01:25:14 -04:00
// add Feign Memory check because sometimes weird stuff happens
if (it->second->CheckAggro(targ) || (targ->IsClient() && it->second->IsOnFeignMemory(targ->CastToClient()))) {
if (it->second->GetSpecialAbility(IMMUNE_FEIGN_DEATH)) {
++it;
2013-02-16 16:14:39 -08:00
continue;
}
if (targ->IsClient()) {
std::vector<EQ::Any> args;
args.push_back(it->second);
int i = parse->EventPlayer(EVENT_FEIGN_DEATH, targ->CastToClient(), "", 0, &args);
if (i != 0) {
++it;
2013-06-06 14:29:45 -07:00
continue;
}
if (it->second->IsNPC()) {
int i = parse->EventNPC(EVENT_FEIGN_DEATH, it->second->CastToNPC(), targ, "", 0);
if (i != 0) {
++it;
2013-06-06 14:29:45 -07:00
continue;
}
}
}
it->second->RemoveFromHateList(targ);
if (targ->IsClient()) {
if (it->second->GetLevel() >= 35 && zone->random.Roll(60))
it->second->AddFeignMemory(targ->CastToClient());
else
targ->CastToClient()->RemoveXTarget(it->second, false);
2013-02-16 16:14:39 -08:00
}
}
++it;
2013-02-16 16:14:39 -08:00
}
}
2013-06-06 14:29:45 -07:00
void EntityList::ClearZoneFeignAggro(Client *targ)
2013-02-16 16:14:39 -08:00
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
it->second->RemoveFromFeignMemory(targ);
targ->CastToClient()->RemoveXTarget(it->second, false);
++it;
2013-02-16 16:14:39 -08:00
}
}
2014-12-27 20:23:49 -06:00
void EntityList::AggroZone(Mob *who, uint32 hate)
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
it->second->AddToHateList(who, hate);
++it;
2013-02-16 16:14:39 -08:00
}
}
// Signal Quest command function
void EntityList::SignalMobsByNPCID(uint32 snpc, int signal_id)
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC *pit = it->second;
if (pit->GetNPCTypeID() == snpc)
pit->SignalNPC(signal_id);
++it;
2013-02-16 16:14:39 -08:00
}
}
bool EntityList::MakeTrackPacket(Client *client)
2013-02-16 16:14:39 -08:00
{
std::list<std::pair<Mob *, float> > tracking_list;
2013-02-16 16:14:39 -08:00
uint32 distance = 0;
float MobDistance;
if (client->GetClass() == DRUID)
distance = (client->GetSkill(EQ::skills::SkillTracking) * 10);
else if (client->GetClass() == RANGER)
distance = (client->GetSkill(EQ::skills::SkillTracking) * 12);
else if (client->GetClass() == BARD)
distance = (client->GetSkill(EQ::skills::SkillTracking) * 7);
if (distance <= 0)
2013-02-16 16:14:39 -08:00
return false;
if (distance < 300)
distance = 300;
2013-05-06 13:07:41 -04:00
for (auto it = mob_list.cbegin(); it != mob_list.cend(); ++it) {
2014-11-19 15:00:09 -05:00
if (!it->second || it->second == client || !it->second->IsTrackable() ||
it->second->IsInvisible(client))
continue;
2013-02-16 16:14:39 -08:00
MobDistance = DistanceNoZ(it->second->GetPosition(), client->GetPosition());
if (MobDistance > distance)
continue;
tracking_list.push_back(std::make_pair(it->second, MobDistance));
2013-02-16 16:14:39 -08:00
}
tracking_list.sort(
[](const std::pair<Mob *, float> &a, const std::pair<Mob *, float> &b) {
return a.first->GetSpawnTimeStamp() > b.first->GetSpawnTimeStamp();
});
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_Track, sizeof(Track_Struct) * tracking_list.size());
Tracking_Struct *outtrack = (Tracking_Struct *)outapp->pBuffer;
outapp->priority = 6;
int index = 0;
for (auto it = tracking_list.cbegin(); it != tracking_list.cend(); ++it, ++index) {
Mob *cur_entity = it->first;
2014-12-22 16:09:11 -06:00
outtrack->Entrys[index].entityid = (uint32)cur_entity->GetID();
outtrack->Entrys[index].distance = it->second;
outtrack->Entrys[index].level = cur_entity->GetLevel();
2014-12-22 16:09:11 -06:00
outtrack->Entrys[index].is_npc = !cur_entity->IsClient();
strn0cpy(outtrack->Entrys[index].name, cur_entity->GetName(), sizeof(outtrack->Entrys[index].name));
2014-12-22 16:09:11 -06:00
outtrack->Entrys[index].is_pet = cur_entity->IsPet();
outtrack->Entrys[index].is_merc = cur_entity->IsMerc();
2013-02-16 16:14:39 -08:00
}
2013-05-06 13:07:41 -04:00
client->QueuePacket(outapp);
safe_delete(outapp);
2013-05-06 13:07:41 -04:00
return true;
2013-02-16 16:14:39 -08:00
}
void EntityList::MessageGroup(Mob *sender, bool skipclose, uint32 type, const char *message, ...)
{
2013-02-16 16:14:39 -08:00
va_list argptr;
char buffer[4096];
va_start(argptr, message);
vsnprintf(buffer, 4095, message, argptr);
va_end(argptr);
float dist2 = 100;
if (skipclose)
dist2 = 0;
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second != sender &&
(Distance(it->second->GetPosition(), sender->GetPosition()) <= dist2 || it->second->GetGroup() == sender->CastToClient()->GetGroup())) {
it->second->Message(type, buffer);
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
}
bool EntityList::Fighting(Mob *targ)
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second->CheckAggro(targ))
2013-02-16 16:14:39 -08:00
return true;
++it;
2013-02-16 16:14:39 -08:00
}
return false;
}
void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 hate)
2013-02-16 16:14:39 -08:00
{
if (hate == 0)
return;
2013-05-06 13:07:41 -04:00
for (auto &e : npc_list) {
auto &npc = e.second;
2018-01-09 18:00:34 -05:00
if (!npc->CheckAggro(target) || npc->IsFeared() || npc->IsPet())
2013-02-16 16:14:39 -08:00
continue;
2014-03-03 15:01:52 -05:00
2015-07-18 00:57:10 -04:00
if (zone->random.Roll(50)) // witness check -- place holder
// This is either a level check (con color check?) or a stat roll
continue;
2014-03-03 15:01:52 -05:00
if ((npc->IsMezzed() || npc->IsStunned()) && hate > 4) // patch notes say stunned/mezzed NPCs get a fraction of the hate
npc->AddToHateList(caster, hate / 4); // made up number
else
npc->AddToHateList(caster, hate);
2013-02-16 16:14:39 -08:00
}
}
void EntityList::OpenDoorsNear(Mob *who)
2013-02-16 16:14:39 -08:00
{
for (auto it = door_list.begin();it != door_list.end(); ++it) {
Doors *cdoor = it->second;
if (!cdoor || cdoor->IsDoorOpen())
continue;
auto diff = who->GetPosition() - cdoor->GetPosition();
float curdist = diff.x * diff.x + diff.y * diff.y;
if (diff.z * diff.z < 10 && curdist <= 100)
cdoor->Open(who);
2013-02-16 16:14:39 -08:00
}
}
void EntityList::SendAlarm(Trap *trap, Mob *currenttarget, uint8 kos)
2013-02-16 16:14:39 -08:00
{
float preSquareDistance = trap->effectvalue * trap->effectvalue;
2013-05-06 13:07:41 -04:00
for (auto it = npc_list.begin();it != npc_list.end(); ++it) {
NPC *cur = it->second;
auto diff = glm::vec3(cur->GetPosition()) - trap->m_Position;
float curdist = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;
if (cur->GetOwner() || cur->IsEngaged() || curdist > preSquareDistance )
continue;
if (kos) {
uint8 factioncon = currenttarget->GetReverseFactionCon(cur);
if (factioncon == FACTION_THREATENLY || factioncon == FACTION_SCOWLS) {
2013-02-16 16:14:39 -08:00
cur->AddToHateList(currenttarget,1);
}
}
else
cur->AddToHateList(currenttarget,1);
2013-02-16 16:14:39 -08:00
}
}
void EntityList::AddProximity(NPC *proximity_for)
{
2013-02-16 16:14:39 -08:00
RemoveProximity(proximity_for->GetID());
2013-05-06 13:07:41 -04:00
proximity_list.push_back(proximity_for);
2013-05-06 13:07:41 -04:00
2015-01-09 12:59:13 -05:00
proximity_for->proximity = new NPCProximity; // deleted in NPC::~NPC
2013-02-16 16:14:39 -08:00
}
bool EntityList::RemoveProximity(uint16 delete_npc_id)
{
2015-01-09 12:59:13 -05:00
auto it = std::find_if(proximity_list.begin(), proximity_list.end(),
[delete_npc_id](const NPC *a) { return a->GetID() == delete_npc_id; });
if (it == proximity_list.end())
return false;
2015-01-09 12:59:13 -05:00
proximity_list.erase(it);
return true;
2013-02-16 16:14:39 -08:00
}
void EntityList::RemoveAllLocalities()
{
proximity_list.clear();
2013-02-16 16:14:39 -08:00
}
struct quest_proximity_event {
QuestEventID event_id;
Client *client;
NPC *npc;
int area_id;
int area_type;
};
2013-05-06 13:07:41 -04:00
void EntityList::ProcessMove(Client *c, const glm::vec3& location)
{
2013-02-16 16:14:39 -08:00
float last_x = c->ProximityX();
float last_y = c->ProximityY();
float last_z = c->ProximityZ();
2013-05-06 13:07:41 -04:00
std::list<quest_proximity_event> events;
for (auto iter = proximity_list.begin(); iter != proximity_list.end(); ++iter) {
NPC *d = (*iter);
2013-02-16 16:14:39 -08:00
NPCProximity *l = d->proximity;
if (l == nullptr)
2013-02-16 16:14:39 -08:00
continue;
2013-02-16 16:14:39 -08:00
//check both bounding boxes, if either coords pairs
//cross a boundary, send the event.
bool old_in = true;
bool new_in = true;
if (last_x < l->min_x || last_x > l->max_x ||
last_y < l->min_y || last_y > l->max_y ||
last_z < l->min_z || last_z > l->max_z) {
2013-02-16 16:14:39 -08:00
old_in = false;
}
if (location.x < l->min_x || location.x > l->max_x ||
location.y < l->min_y || location.y > l->max_y ||
location.z < l->min_z || location.z > l->max_z) {
2013-02-16 16:14:39 -08:00
new_in = false;
}
if (old_in && !new_in) {
quest_proximity_event evt;
evt.event_id = EVENT_EXIT;
evt.client = c;
evt.npc = d;
evt.area_id = 0;
evt.area_type = 0;
events.push_back(evt);
} else if (new_in && !old_in) {
quest_proximity_event evt;
evt.event_id = EVENT_ENTER;
evt.client = c;
evt.npc = d;
evt.area_id = 0;
evt.area_type = 0;
events.push_back(evt);
2013-02-16 16:14:39 -08:00
}
}
for (auto iter = area_list.begin(); iter != area_list.end(); ++iter) {
Area& a = (*iter);
bool old_in = true;
bool new_in = true;
if (last_x < a.min_x || last_x > a.max_x ||
last_y < a.min_y || last_y > a.max_y ||
last_z < a.min_z || last_z > a.max_z) {
old_in = false;
}
if (location.x < a.min_x || location.x > a.max_x ||
location.y < a.min_y || location.y > a.max_y ||
location.z < a.min_z || location.z > a.max_z ) {
new_in = false;
}
if (old_in && !new_in) {
//were in but are no longer.
quest_proximity_event evt;
evt.event_id = EVENT_LEAVE_AREA;
evt.client = c;
evt.npc = nullptr;
evt.area_id = a.id;
evt.area_type = a.type;
events.push_back(evt);
} else if (!old_in && new_in) {
//were not in but now are
quest_proximity_event evt;
evt.event_id = EVENT_ENTER_AREA;
evt.client = c;
evt.npc = nullptr;
evt.area_id = a.id;
evt.area_type = a.type;
events.push_back(evt);
}
}
for (auto iter = events.begin(); iter != events.end(); ++iter) {
quest_proximity_event& evt = (*iter);
if (evt.npc) {
std::vector<EQ::Any> args;
2014-08-31 20:27:02 -07:00
parse->EventNPC(evt.event_id, evt.npc, evt.client, "", 0, &args);
} else {
std::vector<EQ::Any> args;
args.push_back(&evt.area_id);
args.push_back(&evt.area_type);
parse->EventPlayer(evt.event_id, evt.client, "", 0, &args);
}
}
}
void EntityList::ProcessMove(NPC *n, float x, float y, float z) {
float last_x = n->GetX();
float last_y = n->GetY();
float last_z = n->GetZ();
std::list<quest_proximity_event> events;
for (auto iter = area_list.begin(); iter != area_list.end(); ++iter) {
Area &a = (*iter);
bool old_in = true;
bool new_in = true;
if (last_x < a.min_x || last_x > a.max_x ||
last_y < a.min_y || last_y > a.max_y ||
last_z < a.min_z || last_z > a.max_z) {
old_in = false;
}
if (x < a.min_x || x > a.max_x ||
y < a.min_y || y > a.max_y ||
z < a.min_z || z > a.max_z) {
new_in = false;
}
if (old_in && !new_in) {
//were in but are no longer.
quest_proximity_event evt;
evt.event_id = EVENT_LEAVE_AREA;
evt.client = nullptr;
evt.npc = n;
evt.area_id = a.id;
evt.area_type = a.type;
events.push_back(evt);
}
else if (!old_in && new_in) {
//were not in but now are
quest_proximity_event evt;
evt.event_id = EVENT_ENTER_AREA;
evt.client = nullptr;
evt.npc = n;
evt.area_id = a.id;
evt.area_type = a.type;
events.push_back(evt);
}
}
for (auto iter = events.begin(); iter != events.end(); ++iter) {
quest_proximity_event &evt = (*iter);
std::vector<EQ::Any> args;
args.push_back(&evt.area_id);
args.push_back(&evt.area_type);
parse->EventNPC(evt.event_id, evt.npc, evt.client, "", 0, &args);
}
}
void EntityList::AddArea(int id, int type, float min_x, float max_x, float min_y,
float max_y, float min_z, float max_z)
{
RemoveArea(id);
Area a;
a.id = id;
a.type = type;
if (min_x > max_x) {
a.min_x = max_x;
a.max_x = min_x;
} else {
a.min_x = min_x;
a.max_x = max_x;
}
if (min_y > max_y) {
a.min_y = max_y;
a.max_y = min_y;
} else {
a.min_y = min_y;
a.max_y = max_y;
}
if (min_z > max_z) {
a.min_z = max_z;
a.max_z = min_z;
} else {
a.min_z = min_z;
a.max_z = max_z;
}
area_list.push_back(a);
}
void EntityList::RemoveArea(int id)
{
2015-01-09 12:59:13 -05:00
auto it = std::find_if(area_list.begin(), area_list.end(),
[id](const Area &a) { return a.id == id; });
if (it == area_list.end())
return;
area_list.erase(it);
}
void EntityList::ClearAreas()
{
area_list.clear();
2013-02-16 16:14:39 -08:00
}
void EntityList::ProcessProximitySay(const char *Message, Client *c, uint8 language)
{
if (!Message || !c)
2013-02-16 16:14:39 -08:00
return;
auto iter = proximity_list.begin();
for (; iter != proximity_list.end(); ++iter) {
NPC *d = (*iter);
2013-02-16 16:14:39 -08:00
NPCProximity *l = d->proximity;
if (l == nullptr || !l->say)
2013-02-16 16:14:39 -08:00
continue;
if (c->GetX() < l->min_x || c->GetX() > l->max_x
|| c->GetY() < l->min_y || c->GetY() > l->max_y
|| c->GetZ() < l->min_z || c->GetZ() > l->max_z)
2013-02-16 16:14:39 -08:00
continue;
2013-05-09 10:44:08 -04:00
parse->EventNPC(EVENT_PROXIMITY_SAY, d, c, Message, language);
2013-02-16 16:14:39 -08:00
}
}
void EntityList::SaveAllClientsTaskState()
{
if (!taskmanager)
return;
2013-02-16 16:14:39 -08:00
auto it = client_list.begin();
while (it != client_list.end()) {
Client *client = it->second;
if (client->IsTaskStateLoaded())
2013-02-16 16:14:39 -08:00
client->SaveTaskState();
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::ReloadAllClientsTaskState(int TaskID)
{
if (!taskmanager)
return;
2013-02-16 16:14:39 -08:00
auto it = client_list.begin();
while (it != client_list.end()) {
Client *client = it->second;
if (client->IsTaskStateLoaded()) {
2013-02-16 16:14:39 -08:00
// If we have been passed a TaskID, only reload the client state if they have
// that Task active.
if ((!TaskID) || (TaskID && client->IsTaskActive(TaskID))) {
Log(Logs::General, Logs::Tasks, "[CLIENTLOAD] Reloading Task State For Client %s", client->GetName());
2013-02-16 16:14:39 -08:00
client->RemoveClientTaskState();
client->LoadClientTaskState();
taskmanager->SendActiveTasksToClient(client);
}
}
++it;
2013-02-16 16:14:39 -08:00
}
}
bool EntityList::IsMobInZone(Mob *who)
{
//We don't use mob_list.find(who) because this code needs to be able to handle dangling pointers for the quest code.
auto it = mob_list.begin();
while(it != mob_list.end()) {
if(it->second == who) {
return true;
}
++it;
}
auto enc_it = encounter_list.begin();
while (enc_it != encounter_list.end()) {
if (enc_it->second == who) {
return true;
}
++enc_it;
}
return false;
2013-02-16 16:14:39 -08:00
}
/*
Code to limit the amount of certain NPCs in a given zone.
2013-02-16 16:14:39 -08:00
Primarily used to make a named mob unique within the zone, but written
to be more generic allowing limits larger than 1.
Maintain this stuff in a seperate list since the number
of limited NPCs will most likely be much smaller than the number
of NPCs in the entire zone.
*/
void EntityList::LimitAddNPC(NPC *npc)
{
if (!npc)
2013-02-16 16:14:39 -08:00
return;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
SpawnLimitRecord r;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
uint16 eid = npc->GetID();
r.spawngroup_id = npc->GetSpawnGroupId();
2013-02-16 16:14:39 -08:00
r.npc_type = npc->GetNPCTypeID();
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
npc_limit_list[eid] = r;
}
void EntityList::LimitRemoveNPC(NPC *npc)
{
if (!npc)
2013-02-16 16:14:39 -08:00
return;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
uint16 eid = npc->GetID();
npc_limit_list.erase(eid);
}
//check a limit over the entire zone.
//returns true if the limit has not been reached
bool EntityList::LimitCheckType(uint32 npc_type, int count)
{
if (count < 1)
return true;
2013-05-06 13:07:41 -04:00
std::map<uint16, SpawnLimitRecord>::iterator cur,end;
2013-02-16 16:14:39 -08:00
cur = npc_limit_list.begin();
end = npc_limit_list.end();
2013-05-06 13:07:41 -04:00
for (; cur != end; ++cur) {
if (cur->second.npc_type == npc_type) {
2013-02-16 16:14:39 -08:00
count--;
if (count == 0) {
return false;
2013-02-16 16:14:39 -08:00
}
}
}
return true;
2013-02-16 16:14:39 -08:00
}
//check limits on an npc type in a given spawn group.
//returns true if the limit has not been reached
bool EntityList::LimitCheckGroup(uint32 spawngroup_id, int count)
{
if (count < 1)
return true;
2013-05-06 13:07:41 -04:00
std::map<uint16, SpawnLimitRecord>::iterator cur,end;
2013-02-16 16:14:39 -08:00
cur = npc_limit_list.begin();
end = npc_limit_list.end();
2013-05-06 13:07:41 -04:00
for (; cur != end; ++cur) {
if (cur->second.spawngroup_id == spawngroup_id) {
2013-02-16 16:14:39 -08:00
count--;
if (count == 0) {
return false;
2013-02-16 16:14:39 -08:00
}
}
}
return true;
2013-02-16 16:14:39 -08:00
}
//check limits on an npc type in a given spawn group, and
//checks limits on the entire zone in one pass.
//returns true if neither limit has been reached
bool EntityList::LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count)
{
if (group_count < 1 && type_count < 1)
return true;
2013-05-06 13:07:41 -04:00
std::map<uint16, SpawnLimitRecord>::iterator cur,end;
2013-02-16 16:14:39 -08:00
cur = npc_limit_list.begin();
end = npc_limit_list.end();
2013-05-06 13:07:41 -04:00
for (; cur != end; ++cur) {
if (cur->second.npc_type == npc_type) {
2013-02-16 16:14:39 -08:00
type_count--;
if (type_count == 0) {
return false;
2013-02-16 16:14:39 -08:00
}
}
if (cur->second.spawngroup_id == spawngroup_id) {
2013-02-16 16:14:39 -08:00
group_count--;
if (group_count == 0) {
return false;
2013-02-16 16:14:39 -08:00
}
}
}
return true;
2013-02-16 16:14:39 -08:00
}
bool EntityList::LimitCheckName(const char *npc_name)
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC* npc = it->second;
if (npc)
if (strcasecmp(npc_name, npc->GetRawNPCTypeName()) == 0)
2013-02-16 16:14:39 -08:00
return false;
++it;
2013-02-16 16:14:39 -08:00
}
return true;
}
void EntityList::UpdateHoTT(Mob *target)
{
auto it = client_list.begin();
while (it != client_list.end()) {
Client *c = it->second;
2013-02-16 16:14:39 -08:00
if (c->GetTarget() == target) {
if (target->GetTarget())
c->SetHoTT(target->GetTarget()->GetID());
else
c->SetHoTT(0);
2013-02-16 16:14:39 -08:00
c->UpdateXTargetType(TargetsTarget, target->GetTarget());
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::DestroyTempPets(Mob *owner)
{
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC* n = it->second;
if (n->GetSwarmInfo()) {
if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
2013-02-16 16:14:39 -08:00
n->Depop();
}
}
++it;
2013-02-16 16:14:39 -08:00
}
}
int16 EntityList::CountTempPets(Mob *owner)
{
int16 count = 0;
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC* n = it->second;
if (n->GetSwarmInfo()) {
if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
count++;
}
}
++it;
}
owner->SetTempPetCount(count);
return count;
}
void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy)
{
if (!other || !owner)
return;
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC* n = it->second;
if (n->GetSwarmInfo()) {
if (n->GetSwarmInfo()->owner_id == owner->GetID()) {
if (!n->GetSpecialAbility(IMMUNE_AGGRO))
n->hate_list.AddEntToHateList(other, 0, 0, bFrenzy);
}
}
++it;
}
}
bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z,
float trg_x, float trg_y, float trg_z, float perwalk)
2013-02-16 16:14:39 -08:00
{
if (zone->zonemap == nullptr)
return true;
glm::vec3 myloc;
glm::vec3 oloc;
glm::vec3 hit;
2013-02-16 16:14:39 -08:00
myloc.x = cur_x;
myloc.y = cur_y;
myloc.z = cur_z+5;
oloc.x = trg_x;
oloc.y = trg_y;
oloc.z = trg_z+5;
if (myloc.x == oloc.x && myloc.y == oloc.y && myloc.z == oloc.z)
return true;
if (!zone->zonemap->LineIntersectsZoneNoZLeaps(myloc,oloc,perwalk,&hit))
2013-02-16 16:14:39 -08:00
return true;
return false;
}
void EntityList::QuestJournalledSayClose(Mob *sender, float dist, const char *mobname, const char *message,
Journal::Options &opts)
{
SerializeBuffer buf(sizeof(SpecialMesgHeader_Struct) + 12 + 64 + 64);
buf.WriteInt8(static_cast<int8>(opts.speak_mode));
buf.WriteInt8(static_cast<int8>(opts.journal_mode));
buf.WriteInt8(opts.language);
buf.WriteInt32(opts.message_type);
buf.WriteInt32(opts.target_spawn_id);
buf.WriteString(mobname);
buf.WriteInt32(0); // location, client doesn't seem to do anything with this
buf.WriteInt32(0);
buf.WriteInt32(0);
buf.WriteString(message);
auto outapp = new EQApplicationPacket(OP_SpecialMesg, buf);
// client only bothers logging if target spawn ID matches, safe to send to everyone
QueueCloseClients(sender, outapp, false, dist);
delete outapp;
2013-02-16 16:14:39 -08:00
}
Corpse *EntityList::GetClosestCorpse(Mob *sender, const char *Name)
2013-02-16 16:14:39 -08:00
{
if (!sender)
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
uint32 CurrentDistance, ClosestDistance = 4294967295u;
2013-05-04 18:06:58 -07:00
Corpse *CurrentCorpse, *ClosestCorpse = nullptr;
2013-02-16 16:14:39 -08:00
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
CurrentCorpse = it->second;
2013-02-16 16:14:39 -08:00
++it;
2013-05-06 13:07:41 -04:00
if (Name && strcasecmp(CurrentCorpse->GetOwnerName(), Name))
2013-02-16 16:14:39 -08:00
continue;
2013-05-06 13:07:41 -04:00
CurrentDistance = ((CurrentCorpse->GetY() - sender->GetY()) * (CurrentCorpse->GetY() - sender->GetY())) +
2013-05-09 10:44:08 -04:00
((CurrentCorpse->GetX() - sender->GetX()) * (CurrentCorpse->GetX() - sender->GetX()));
2013-02-16 16:14:39 -08:00
if (CurrentDistance < ClosestDistance) {
2013-02-16 16:14:39 -08:00
ClosestDistance = CurrentDistance;
ClosestCorpse = CurrentCorpse;
}
}
return ClosestCorpse;
}
void EntityList::ForceGroupUpdate(uint32 gid)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second){
2013-05-04 18:06:58 -07:00
Group *g = nullptr;
g = it->second->GetGroup();
if (g) {
if (g->GetID() == gid) {
database.RefreshGroupFromDB(it->second);
2013-02-16 16:14:39 -08:00
}
}
}
++it;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::SendGroupLeave(uint32 gid, const char *name)
{
auto it = client_list.begin();
while (it != client_list.end()) {
Client *c = it->second;
if (c) {
2013-05-04 18:06:58 -07:00
Group *g = nullptr;
2013-02-16 16:14:39 -08:00
g = c->GetGroup();
if (g) {
if (g->GetID() == gid) {
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct));
2013-05-06 13:07:41 -04:00
GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer;
2013-02-16 16:14:39 -08:00
strcpy(gj->membername, name);
gj->action = groupActLeave;
strcpy(gj->yourname, c->GetName());
Mob *Leader = g->GetLeader();
if (Leader)
2013-05-06 13:07:41 -04:00
Leader->CastToClient()->GetGroupAAs(&gj->leader_aas);
2013-02-16 16:14:39 -08:00
c->QueuePacket(outapp);
safe_delete(outapp);
g->DelMemberOOZ(name);
if (g->IsLeader(c) && c->IsLFP())
2013-02-16 16:14:39 -08:00
c->UpdateLFP();
}
}
}
++it;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::SendGroupJoin(uint32 gid, const char *name)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second){
2013-05-04 18:06:58 -07:00
Group *g = nullptr;
g = it->second->GetGroup();
if (g) {
if (g->GetID() == gid) {
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct));
2013-05-06 13:07:41 -04:00
GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer;
2013-02-16 16:14:39 -08:00
strcpy(gj->membername, name);
gj->action = groupActJoin;
strcpy(gj->yourname, it->second->GetName());
2013-02-16 16:14:39 -08:00
Mob *Leader = g->GetLeader();
if (Leader)
2013-02-16 16:14:39 -08:00
Leader->CastToClient()->GetGroupAAs(&gj->leader_aas);
2013-05-06 13:07:41 -04:00
it->second->QueuePacket(outapp);
2013-02-16 16:14:39 -08:00
safe_delete(outapp);
}
}
}
++it;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
void EntityList::GroupMessage(uint32 gid, const char *from, const char *message)
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second) {
2013-05-04 18:06:58 -07:00
Group *g = nullptr;
g = it->second->GetGroup();
if (g) {
if (g->GetID() == gid)
it->second->ChannelMessageSend(from, it->second->GetName(), ChatChannel_Group, 0, 100, message);
2013-02-16 16:14:39 -08:00
}
}
++it;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
uint16 EntityList::CreateGroundObject(uint32 itemid, const glm::vec4& position, uint32 decay_time)
2013-02-16 16:14:39 -08:00
{
const EQ::ItemData *is = database.GetItem(itemid);
if (!is)
return 0;
2013-02-16 16:14:39 -08:00
auto i = new EQ::ItemInstance(is, is->MaxCharges);
if (!i)
return 0;
2016-05-25 16:10:28 -04:00
auto object = new Object(i, position.x, position.y, position.z, position.w, decay_time);
entity_list.AddObject(object, true);
safe_delete(i);
if (!object)
return 0;
return object->GetID();
2013-02-16 16:14:39 -08:00
}
uint16 EntityList::CreateGroundObjectFromModel(const char *model, const glm::vec4& position, uint8 type, uint32 decay_time)
2013-02-16 16:14:39 -08:00
{
if (!model)
return 0;
2013-02-16 16:14:39 -08:00
2016-05-25 16:10:28 -04:00
auto object = new Object(model, position.x, position.y, position.z, position.w, type);
entity_list.AddObject(object, true);
if (!object)
return 0;
return object->GetID();
2013-02-16 16:14:39 -08:00
}
uint16 EntityList::CreateDoor(const char *model, const glm::vec4& position, uint8 opentype, uint16 size)
2013-02-16 16:14:39 -08:00
{
if (!model)
return 0; // fell through everything, this is bad/incomplete from perl
2016-05-25 16:10:28 -04:00
auto door = new Doors(model, position, opentype, size);
RemoveAllDoors();
zone->LoadZoneDoors(zone->GetShortName(), zone->GetInstanceVersion());
entity_list.AddDoor(door);
entity_list.RespawnAllDoors();
if (door)
return door->GetEntityID();
2013-02-16 16:14:39 -08:00
return 0; // fell through everything, this is bad/incomplete from perl
}
Mob *EntityList::GetTargetForMez(Mob *caster)
2013-02-16 16:14:39 -08:00
{
if (!caster)
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
auto it = mob_list.begin();
2013-02-16 16:14:39 -08:00
//TODO: make this smarter and not mez targets being damaged by dots
while (it != mob_list.end()) {
Mob *d = it->second;
if (d) {
if (d == caster) { //caster can't pick himself
++it;
2013-02-16 16:14:39 -08:00
continue;
}
if (caster->GetTarget() == d) { //caster can't pick his target
++it;
2013-02-16 16:14:39 -08:00
continue;
}
if (!caster->CheckAggro(d)) { //caster can't pick targets that aren't aggroed on himself
++it;
2013-02-16 16:14:39 -08:00
continue;
}
if (DistanceSquared(caster->GetPosition(), d->GetPosition()) > 22250) { //only pick targets within 150 range
++it;
2013-02-16 16:14:39 -08:00
continue;
}
if (!caster->CheckLosFN(d)) { //this is wasteful but can't really think of another way to do it
++it; //that wont have us trying to los the same target every time
continue; //it's only in combat so it's impact should be minimal.. but stil.
2013-02-16 16:14:39 -08:00
}
return d;
}
++it;
2013-02-16 16:14:39 -08:00
}
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
}
void EntityList::SendZoneAppearance(Client *c)
{
2018-12-31 03:18:59 -06:00
if (!c) {
2013-02-16 16:14:39 -08:00
return;
2018-12-31 03:18:59 -06:00
}
2013-02-16 16:14:39 -08:00
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *cur = it->second;
2013-02-16 16:14:39 -08:00
if (cur) {
if (cur == c) {
++it;
2013-02-16 16:14:39 -08:00
continue;
}
if (cur->GetAppearance() != eaStanding) {
2013-02-16 16:14:39 -08:00
cur->SendAppearancePacket(AT_Anim, cur->GetAppearanceValue(cur->GetAppearance()), false, true, c);
}
if (cur->GetSize() != cur->GetBaseSize()) {
2018-12-31 03:18:59 -06:00
cur->SendAppearancePacket(AT_Size, (uint32) cur->GetSize(), false, true, c);
2013-02-16 16:14:39 -08:00
}
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::SendNimbusEffects(Client *c)
{
if (!c)
2013-02-16 16:14:39 -08:00
return;
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *cur = it->second;
2013-02-16 16:14:39 -08:00
if (cur) {
if (cur == c) {
++it;
2013-02-16 16:14:39 -08:00
continue;
}
if (cur->GetNimbusEffect1() != 0) {
2013-02-16 16:14:39 -08:00
cur->SendSpellEffect(cur->GetNimbusEffect1(), 1000, 0, 1, 3000, false, c);
}
if (cur->GetNimbusEffect2() != 0) {
2013-02-16 16:14:39 -08:00
cur->SendSpellEffect(cur->GetNimbusEffect2(), 2000, 0, 1, 3000, false, c);
}
if (cur->GetNimbusEffect3() != 0) {
2013-02-16 16:14:39 -08:00
cur->SendSpellEffect(cur->GetNimbusEffect3(), 3000, 0, 1, 3000, false, c);
}
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::SendUntargetable(Client *c)
{
if (!c)
2013-02-16 16:14:39 -08:00
return;
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *cur = it->second;
2013-02-16 16:14:39 -08:00
if (cur) {
if (cur == c) {
++it;
2013-02-16 16:14:39 -08:00
continue;
}
if (!cur->IsTargetable())
2013-05-09 10:44:08 -04:00
cur->SendTargetable(false, c);
2013-02-16 16:14:39 -08:00
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::ZoneWho(Client *c, Who_All_Struct *Who)
{
2013-02-16 16:14:39 -08:00
// This is only called for SoF clients, as regular /who is now handled server-side for that client.
uint32 PacketLength = 0;
uint32 Entries = 0;
uint8 WhomLength = strlen(Who->whom);
std::list<Client *> client_sub_list;
auto it = client_list.begin();
while (it != client_list.end()) {
Client *ClientEntry = it->second;
++it;
2013-02-16 16:14:39 -08:00
if (ClientEntry) {
if (ClientEntry->GMHideMe(c))
2013-02-16 16:14:39 -08:00
continue;
if ((Who->wrace != 0xFFFFFFFF) && (ClientEntry->GetRace() != Who->wrace))
2013-02-16 16:14:39 -08:00
continue;
if ((Who->wclass != 0xFFFFFFFF) && (ClientEntry->GetClass() != Who->wclass))
2013-02-16 16:14:39 -08:00
continue;
if ((Who->lvllow != 0xFFFFFFFF) && (ClientEntry->GetLevel() < Who->lvllow))
2013-02-16 16:14:39 -08:00
continue;
if ((Who->lvlhigh != 0xFFFFFFFF) && (ClientEntry->GetLevel() > Who->lvlhigh))
2013-02-16 16:14:39 -08:00
continue;
if (Who->guildid != 0xFFFFFFFF) {
if ((Who->guildid == 0xFFFFFFFC) && !ClientEntry->IsTrader())
2013-02-16 16:14:39 -08:00
continue;
if ((Who->guildid == 0xFFFFFFFB) && !ClientEntry->IsBuyer())
2013-02-16 16:14:39 -08:00
continue;
if (Who->guildid != ClientEntry->GuildID())
2013-02-16 16:14:39 -08:00
continue;
}
if (WhomLength && strncasecmp(Who->whom, ClientEntry->GetName(), WhomLength) &&
2013-05-09 10:44:08 -04:00
strncasecmp(guild_mgr.GetGuildName(ClientEntry->GuildID()), Who->whom, WhomLength))
2013-02-16 16:14:39 -08:00
continue;
Entries++;
client_sub_list.push_back(ClientEntry);
2013-02-16 16:14:39 -08:00
PacketLength = PacketLength + strlen(ClientEntry->GetName());
if (strlen(guild_mgr.GetGuildName(ClientEntry->GuildID())) > 0)
2013-02-16 16:14:39 -08:00
PacketLength = PacketLength + strlen(guild_mgr.GetGuildName(ClientEntry->GuildID())) + 2;
}
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
PacketLength = PacketLength + sizeof(WhoAllReturnStruct) + (47 * Entries);
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_WhoAllResponse, PacketLength);
2013-02-16 16:14:39 -08:00
char *Buffer = (char *)outapp->pBuffer;
WhoAllReturnStruct *WARS = (WhoAllReturnStruct *)Buffer;
WARS->id = 0;
WARS->playerineqstring = 5001;
strncpy(WARS->line, "---------------------------", sizeof(WARS->line));
WARS->unknown35 = 0x0a;
WARS->unknown36 = 0;
2013-05-06 13:07:41 -04:00
2013-02-16 16:14:39 -08:00
switch(Entries) {
case 0:
WARS->playersinzonestring = 5029;
break;
case 1:
WARS->playersinzonestring = 5028; // 5028 There is %1 player in EverQuest.
break;
default:
WARS->playersinzonestring = 5036; // 5036 There are %1 players in EverQuest.
}
WARS->unknown44[0] = 0;
WARS->unknown44[1] = 0;
WARS->unknown52 = Entries;
WARS->unknown56 = Entries;
WARS->playercount = Entries;
Buffer += sizeof(WhoAllReturnStruct);
auto sit = client_sub_list.begin();
while (sit != client_sub_list.end()) {
Client *ClientEntry = *sit;
++sit;
2013-02-16 16:14:39 -08:00
if (ClientEntry) {
if (ClientEntry->GMHideMe(c))
2013-02-16 16:14:39 -08:00
continue;
if ((Who->wrace != 0xFFFFFFFF) && (ClientEntry->GetRace() != Who->wrace))
2013-02-16 16:14:39 -08:00
continue;
if ((Who->wclass != 0xFFFFFFFF) && (ClientEntry->GetClass() != Who->wclass))
2013-02-16 16:14:39 -08:00
continue;
if ((Who->lvllow != 0xFFFFFFFF) && (ClientEntry->GetLevel() < Who->lvllow))
2013-02-16 16:14:39 -08:00
continue;
if ((Who->lvlhigh != 0xFFFFFFFF) && (ClientEntry->GetLevel() > Who->lvlhigh))
continue;
if (Who->guildid != 0xFFFFFFFF) {
if ((Who->guildid == 0xFFFFFFFC) && !ClientEntry->IsTrader())
2013-02-16 16:14:39 -08:00
continue;
if ((Who->guildid == 0xFFFFFFFB) && !ClientEntry->IsBuyer())
2013-02-16 16:14:39 -08:00
continue;
if (Who->guildid != ClientEntry->GuildID())
2013-02-16 16:14:39 -08:00
continue;
}
if (WhomLength && strncasecmp(Who->whom, ClientEntry->GetName(), WhomLength) &&
2013-05-09 10:44:08 -04:00
strncasecmp(guild_mgr.GetGuildName(ClientEntry->GuildID()), Who->whom, WhomLength))
2013-02-16 16:14:39 -08:00
continue;
std::string GuildName;
if ((ClientEntry->GuildID() != GUILD_NONE) && (ClientEntry->GuildID() > 0)) {
2013-02-16 16:14:39 -08:00
GuildName = "<";
GuildName += guild_mgr.GetGuildName(ClientEntry->GuildID());
GuildName += ">";
}
uint32 FormatMSGID = 5025; // 5025 %T1[%2 %3] %4 (%5) %6 %7 %8 %9
if (ClientEntry->GetAnon() == 1)
2013-02-16 16:14:39 -08:00
FormatMSGID = 5024; // 5024 %T1[ANONYMOUS] %2 %3
else if (ClientEntry->GetAnon() == 2)
2013-02-16 16:14:39 -08:00
FormatMSGID = 5023; // 5023 %T1[ANONYMOUS] %2 %3 %4
uint32 PlayerClass = 0;
uint32 PlayerLevel = 0;
uint32 PlayerRace = 0;
uint32 ZoneMSGID = 0xFFFFFFFF;
if (ClientEntry->GetAnon()==0) {
2013-02-16 16:14:39 -08:00
PlayerClass = ClientEntry->GetClass();
PlayerLevel = ClientEntry->GetLevel();
PlayerRace = ClientEntry->GetRace();
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
WhoAllPlayerPart1* WAPP1 = (WhoAllPlayerPart1*)Buffer;
WAPP1->FormatMSGID = FormatMSGID;
WAPP1->PIDMSGID = 0xFFFFFFFF;
strcpy(WAPP1->Name, ClientEntry->GetName());
Buffer += sizeof(WhoAllPlayerPart1) + strlen(WAPP1->Name);
WhoAllPlayerPart2* WAPP2 = (WhoAllPlayerPart2*)Buffer;
if (ClientEntry->IsTrader())
2013-02-16 16:14:39 -08:00
WAPP2->RankMSGID = 12315;
else if (ClientEntry->IsBuyer())
2013-02-16 16:14:39 -08:00
WAPP2->RankMSGID = 6056;
else if (ClientEntry->Admin() >= 10 && ClientEntry->GetGM())
2013-02-16 16:14:39 -08:00
WAPP2->RankMSGID = 12312;
else
WAPP2->RankMSGID = 0xFFFFFFFF;
strcpy(WAPP2->Guild, GuildName.c_str());
Buffer += sizeof(WhoAllPlayerPart2) + strlen(WAPP2->Guild);
WhoAllPlayerPart3* WAPP3 = (WhoAllPlayerPart3*)Buffer;
WAPP3->Unknown80[0] = 0xFFFFFFFF;
if (ClientEntry->IsLD())
2013-02-16 16:14:39 -08:00
WAPP3->Unknown80[1] = 12313; // LinkDead
else
WAPP3->Unknown80[1] = 0xFFFFFFFF;
WAPP3->ZoneMSGID = ZoneMSGID;
WAPP3->Zone = 0;
WAPP3->Class_ = PlayerClass;
2013-05-09 10:44:08 -04:00
WAPP3->Level = PlayerLevel;
2013-02-16 16:14:39 -08:00
WAPP3->Race = PlayerRace;
WAPP3->Account[0] = 0;
Buffer += sizeof(WhoAllPlayerPart3);
WhoAllPlayerPart4* WAPP4 = (WhoAllPlayerPart4*)Buffer;
WAPP4->Unknown100 = 0;
Buffer += sizeof(WhoAllPlayerPart4);
}
}
c->QueuePacket(outapp);
safe_delete(outapp);
}
2013-05-06 13:07:41 -04:00
void EntityList::UnMarkNPC(uint16 ID)
2013-02-16 16:14:39 -08:00
{
// Designed to be called from the Mob destructor, this method calls Group::UnMarkNPC for
// each group to remove the dead mobs entity ID from the groups list of NPCs marked via the
// Group Leadership AA Mark NPC ability.
//
auto it = group_list.begin();
while (it != group_list.end()) {
if (*it)
(*it)->UnMarkNPC(ID);
++it;
2013-05-06 13:07:41 -04:00
}
2013-02-16 16:14:39 -08:00
}
uint32 EntityList::CheckNPCsClose(Mob *center)
{
2013-05-09 10:44:08 -04:00
uint32 count = 0;
2013-02-16 16:14:39 -08:00
auto it = npc_list.begin();
while (it != npc_list.end()) {
NPC *cur = it->second;
if (!cur || cur == center || cur->IsPet() || cur->GetClass() == LDON_TREASURE ||
cur->GetBodyType() == BT_NoTarget || cur->GetBodyType() == BT_Special) {
++it;
2013-02-16 16:14:39 -08:00
continue;
}
float xDiff = cur->GetX() - center->GetX();
float yDiff = cur->GetY() - center->GetY();
float zDiff = cur->GetZ() - center->GetZ();
2013-02-16 16:14:39 -08:00
float dist = ((xDiff * xDiff) + (yDiff * yDiff) + (zDiff * zDiff));
if (dist <= RuleR(Adventure, DistanceForRescueAccept))
2013-02-16 16:14:39 -08:00
count++;
++it;
2013-02-16 16:14:39 -08:00
}
return count;
}
void EntityList::GateAllClients()
{
auto it = client_list.begin();
while (it != client_list.end()) {
Client *c = it->second;
if (c)
2013-02-16 16:14:39 -08:00
c->GoToBind();
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::SignalAllClients(uint32 data)
{
auto it = client_list.begin();
while (it != client_list.end()) {
Client *ent = it->second;
if (ent)
2013-02-16 16:14:39 -08:00
ent->Signal(data);
++it;
2013-02-16 16:14:39 -08:00
}
}
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015' Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload intensive or metric based logging to a remote server process that could exist on another server entirely Implemented Player Event Logging Types (Go to table `qs_player_events`): 1 = Player_Log_Quest, 2 = Player_Log_Zoning, 3 = Player_Log_Deaths, 4 = Player_Log_Connect_State, 5 = Player_Log_Levels, 6 = Player_Log_Keyring_Addition, 7 = Player_Log_QGlobal_Update, 8 = Player_Log_Task_Updates, 9 = Player_Log_AA_Purchases, 10 = Player_Log_Trade_Skill_Events, 11 = Player_Log_Issued_Commands, 12 = Player_Log_Money_Transactions, 13 = Player_Log_Alternate_Currency_Transactions, - All QueryServ logging will be implemented with a front end in EoC 2.0 very soon Changed all QS Error related logging to 'QUERYSERV__ERROR' (Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE ( Implemented Perl Quest objects (LUA still needed to be exported): - quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging - quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events` Added MySQL Tables: - `qs_player_aa_rate_hourly` - `qs_player_events` - Source table structures from: - utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly To get the complete QueryServ schema, source from here: - utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql Added rules for each logging type, source rules here with them enabled by default: - utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql Spawn related logging cleanup General code cleanup Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
uint16 EntityList::GetClientCount(){
uint16 ClientCount = 0;
std::list<Client*> client_list;
entity_list.GetClientList(client_list);
2016-05-25 16:10:28 -04:00
auto iter = client_list.begin();
Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015' Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload intensive or metric based logging to a remote server process that could exist on another server entirely Implemented Player Event Logging Types (Go to table `qs_player_events`): 1 = Player_Log_Quest, 2 = Player_Log_Zoning, 3 = Player_Log_Deaths, 4 = Player_Log_Connect_State, 5 = Player_Log_Levels, 6 = Player_Log_Keyring_Addition, 7 = Player_Log_QGlobal_Update, 8 = Player_Log_Task_Updates, 9 = Player_Log_AA_Purchases, 10 = Player_Log_Trade_Skill_Events, 11 = Player_Log_Issued_Commands, 12 = Player_Log_Money_Transactions, 13 = Player_Log_Alternate_Currency_Transactions, - All QueryServ logging will be implemented with a front end in EoC 2.0 very soon Changed all QS Error related logging to 'QUERYSERV__ERROR' (Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE ( Implemented Perl Quest objects (LUA still needed to be exported): - quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging - quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events` Added MySQL Tables: - `qs_player_aa_rate_hourly` - `qs_player_events` - Source table structures from: - utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly To get the complete QueryServ schema, source from here: - utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql Added rules for each logging type, source rules here with them enabled by default: - utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql Spawn related logging cleanup General code cleanup Added queryserv.cpp and queryserv.h with QueryServ class
2014-08-23 23:59:20 -05:00
while (iter != client_list.end()) {
Client *entry = (*iter);
entry->GetCleanName();
ClientCount++;
iter++;
}
return ClientCount;
}
void EntityList::GetMobList(std::list<Mob *> &m_list)
2013-02-16 16:14:39 -08:00
{
m_list.clear();
auto it = mob_list.begin();
while (it != mob_list.end()) {
m_list.push_back(it->second);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::GetNPCList(std::list<NPC *> &n_list)
2013-02-16 16:14:39 -08:00
{
n_list.clear();
auto it = npc_list.begin();
while (it != npc_list.end()) {
n_list.push_back(it->second);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::GetClientList(std::list<Client *> &c_list)
2013-02-16 16:14:39 -08:00
{
c_list.clear();
auto it = client_list.begin();
while (it != client_list.end()) {
c_list.push_back(it->second);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::GetCorpseList(std::list<Corpse *> &c_list)
2013-02-16 16:14:39 -08:00
{
c_list.clear();
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
c_list.push_back(it->second);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::GetObjectList(std::list<Object *> &o_list)
2013-02-16 16:14:39 -08:00
{
o_list.clear();
auto it = object_list.begin();
while (it != object_list.end()) {
o_list.push_back(it->second);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::GetDoorsList(std::list<Doors*> &o_list)
2013-02-16 16:14:39 -08:00
{
o_list.clear();
auto it = door_list.begin();
while (it != door_list.end()) {
o_list.push_back(it->second);
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::GetSpawnList(std::list<Spawn2*> &o_list)
{
o_list.clear();
if(zone) {
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
while(iterator.MoreElements())
{
Spawn2 *ent = iterator.GetData();
o_list.push_back(ent);
iterator.Advance();
}
}
}
2013-02-16 16:14:39 -08:00
void EntityList::UpdateQGlobal(uint32 qid, QGlobal newGlobal)
{
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *ent = it->second;
2013-05-06 13:07:41 -04:00
if (ent->IsClient()) {
2013-02-16 16:14:39 -08:00
QGlobalCache *qgc = ent->CastToClient()->GetQGlobals();
if (qgc) {
2013-02-16 16:14:39 -08:00
uint32 char_id = ent->CastToClient()->CharacterID();
if (newGlobal.char_id == char_id && newGlobal.npc_id == 0)
2013-02-16 16:14:39 -08:00
qgc->AddGlobal(qid, newGlobal);
}
} else if (ent->IsNPC()) {
2013-02-16 16:14:39 -08:00
QGlobalCache *qgc = ent->CastToNPC()->GetQGlobals();
if (qgc) {
2013-02-16 16:14:39 -08:00
uint32 npc_id = ent->GetNPCTypeID();
if (newGlobal.npc_id == npc_id)
2013-02-16 16:14:39 -08:00
qgc->AddGlobal(qid, newGlobal);
}
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID)
{
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *ent = it->second;
2013-05-06 13:07:41 -04:00
if (ent->IsClient()) {
2013-02-16 16:14:39 -08:00
QGlobalCache *qgc = ent->CastToClient()->GetQGlobals();
if (qgc)
2013-02-16 16:14:39 -08:00
qgc->RemoveGlobal(name, npcID, charID, zoneID);
} else if (ent->IsNPC()) {
2013-02-16 16:14:39 -08:00
QGlobalCache *qgc = ent->CastToNPC()->GetQGlobals();
if (qgc)
2013-02-16 16:14:39 -08:00
qgc->RemoveGlobal(name, npcID, charID, zoneID);
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::SendFindableNPCList(Client *c)
{
if (!c)
2013-02-16 16:14:39 -08:00
return;
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_SendFindableNPCs, sizeof(FindableNPC_Struct));
2013-02-16 16:14:39 -08:00
FindableNPC_Struct *fnpcs = (FindableNPC_Struct *)outapp->pBuffer;
fnpcs->Unknown109 = 0x16;
fnpcs->Unknown110 = 0x06;
fnpcs->Unknown111 = 0x24;
fnpcs->Action = 0;
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second) {
NPC *n = it->second;
2013-02-16 16:14:39 -08:00
if (n->IsFindable()) {
2013-02-16 16:14:39 -08:00
fnpcs->EntityID = n->GetID();
strn0cpy(fnpcs->Name, n->GetCleanName(), sizeof(fnpcs->Name));
strn0cpy(fnpcs->LastName, n->GetLastName(), sizeof(fnpcs->LastName));
fnpcs->Race = n->GetRace();
fnpcs->Class = n->GetClass();
c->QueuePacket(outapp);
}
}
++it;
2013-02-16 16:14:39 -08:00
}
safe_delete(outapp);
}
void EntityList::UpdateFindableNPCState(NPC *n, bool Remove)
{
if (!n || !n->IsFindable())
2013-02-16 16:14:39 -08:00
return;
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_SendFindableNPCs, sizeof(FindableNPC_Struct));
2013-02-16 16:14:39 -08:00
FindableNPC_Struct *fnpcs = (FindableNPC_Struct *)outapp->pBuffer;
fnpcs->Unknown109 = 0x16;
fnpcs->Unknown110 = 0x06;
fnpcs->Unknown111 = 0x24;
fnpcs->Action = Remove ? 1: 0;
fnpcs->EntityID = n->GetID();
strn0cpy(fnpcs->Name, n->GetCleanName(), sizeof(fnpcs->Name));
strn0cpy(fnpcs->LastName, n->GetLastName(), sizeof(fnpcs->LastName));
fnpcs->Race = n->GetRace();
fnpcs->Class = n->GetClass();
auto it = client_list.begin();
while (it != client_list.end()) {
Client *c = it->second;
if (c && (c->ClientVersion() >= EQ::versions::ClientVersion::SoD))
2013-02-16 16:14:39 -08:00
c->QueuePacket(outapp);
2013-05-06 13:07:41 -04:00
++it;
2013-02-16 16:14:39 -08:00
}
safe_delete(outapp);
}
void EntityList::HideCorpses(Client *c, uint8 CurrentMode, uint8 NewMode)
{
if (!c)
2013-02-16 16:14:39 -08:00
return;
if (NewMode == HideCorpseNone) {
2013-02-16 16:14:39 -08:00
SendZoneCorpses(c);
return;
}
2013-05-04 18:06:58 -07:00
Group *g = nullptr;
2013-02-16 16:14:39 -08:00
if (NewMode == HideCorpseAllButGroup) {
2013-02-16 16:14:39 -08:00
g = c->GetGroup();
if (!g)
2013-02-16 16:14:39 -08:00
NewMode = HideCorpseAll;
}
auto it = corpse_list.begin();
while (it != corpse_list.end()) {
Corpse *b = it->second;
2013-02-16 16:14:39 -08:00
if (b && (b->GetCharID() != c->CharacterID())) {
if ((NewMode == HideCorpseAll) || ((NewMode == HideCorpseNPC) && (b->IsNPCCorpse()))) {
2013-02-16 16:14:39 -08:00
EQApplicationPacket outapp;
2013-05-09 10:44:08 -04:00
b->CreateDespawnPacket(&outapp, false);
2013-02-16 16:14:39 -08:00
c->QueuePacket(&outapp);
} else if(NewMode == HideCorpseAllButGroup) {
if (!g->IsGroupMember(b->GetOwnerName())) {
2013-02-16 16:14:39 -08:00
EQApplicationPacket outapp;
2013-05-09 10:44:08 -04:00
b->CreateDespawnPacket(&outapp, false);
2013-02-16 16:14:39 -08:00
c->QueuePacket(&outapp);
} else if((CurrentMode == HideCorpseAll)) {
2013-02-16 16:14:39 -08:00
EQApplicationPacket outapp;
2013-05-09 10:44:08 -04:00
b->CreateSpawnPacket(&outapp);
2013-02-16 16:14:39 -08:00
c->QueuePacket(&outapp);
}
}
}
++it;
2013-02-16 16:14:39 -08:00
}
}
void EntityList::AddLootToNPCS(uint32 item_id, uint32 count)
{
if (count == 0)
2013-02-16 16:14:39 -08:00
return;
int npc_count = 0;
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (!it->second->IsPet()
&& it->second->GetClass() != LDON_TREASURE
&& it->second->GetBodyType() != BT_NoTarget
&& it->second->GetBodyType() != BT_NoTarget2
&& it->second->GetBodyType() != BT_Special)
2013-02-16 16:14:39 -08:00
npc_count++;
++it;
2013-02-16 16:14:39 -08:00
}
if (npc_count == 0)
2013-02-16 16:14:39 -08:00
return;
2016-05-25 16:10:28 -04:00
auto npcs = new NPC *[npc_count];
auto counts = new int[npc_count];
auto marked = new bool[npc_count];
2013-02-16 16:14:39 -08:00
memset(counts, 0, sizeof(int) * npc_count);
memset(marked, 0, sizeof(bool) * npc_count);
int i = 0;
it = npc_list.begin();
while (it != npc_list.end()) {
if (!it->second->IsPet()
&& it->second->GetClass() != LDON_TREASURE
&& it->second->GetBodyType() != BT_NoTarget
&& it->second->GetBodyType() != BT_NoTarget2
&& it->second->GetBodyType() != BT_Special)
npcs[i++] = it->second;
++it;
}
while (count > 0) {
std::vector<int> selection;
2013-02-16 16:14:39 -08:00
selection.reserve(npc_count);
for (int j = 0; j < npc_count; ++j)
2013-02-16 16:14:39 -08:00
selection.push_back(j);
while (!selection.empty() && count > 0) {
int k = zone->random.Int(0, selection.size() - 1);
2013-02-16 16:14:39 -08:00
counts[selection[k]]++;
count--;
selection.erase(selection.begin() + k);
}
}
for (int j = 0; j < npc_count; ++j)
if (counts[j] > 0)
for (int k = 0; k < counts[j]; ++k)
2013-02-16 16:14:39 -08:00
npcs[j]->AddItem(item_id, 1);
safe_delete_array(npcs);
safe_delete_array(counts);
safe_delete_array(marked);
}
void EntityList::CameraEffect(uint32 duration, uint32 intensity)
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_CameraEffect, sizeof(Camera_Struct));
2013-02-16 16:14:39 -08:00
Camera_Struct* cs = (Camera_Struct*) outapp->pBuffer;
cs->duration = duration; // Duration in milliseconds
cs->intensity = ((intensity * 6710886) + 1023410176); // Intensity ranges from 1023410176 to 1090519040, so simplify it from 0 to 10.
entity_list.QueueClients(0, outapp);
safe_delete(outapp);
}
NPC *EntityList::GetClosestBanker(Mob *sender, uint32 &distance)
2013-02-16 16:14:39 -08:00
{
if (!sender)
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
2013-03-10 18:55:18 +00:00
distance = 4294967295u;
NPC *nc = nullptr;
auto it = npc_list.begin();
while (it != npc_list.end()) {
if (it->second->GetClass() == BANKER) {
uint32 nd = ((it->second->GetY() - sender->GetY()) * (it->second->GetY() - sender->GetY())) +
((it->second->GetX() - sender->GetX()) * (it->second->GetX() - sender->GetX()));
if (nd < distance){
2013-02-16 16:14:39 -08:00
distance = nd;
nc = it->second;
2013-02-16 16:14:39 -08:00
}
}
++it;
2013-02-16 16:14:39 -08:00
}
return nc;
}
void EntityList::ExpeditionWarning(uint32 minutes_left)
{
2016-05-25 16:10:28 -04:00
auto outapp = new EQApplicationPacket(OP_DzExpeditionEndsWarning, sizeof(ExpeditionExpireWarning));
2013-02-16 16:14:39 -08:00
ExpeditionExpireWarning *ew = (ExpeditionExpireWarning*)outapp->pBuffer;
ew->minutes_remaining = minutes_left;
auto it = client_list.begin();
while (it != client_list.end()) {
2019-08-11 00:14:02 -05:00
it->second->MessageString(Chat::Yellow, EXPEDITION_MIN_REMAIN, itoa((int)minutes_left));
it->second->QueuePacket(outapp);
++it;
2013-02-16 16:14:39 -08:00
}
safe_delete(outapp);
2013-02-16 16:14:39 -08:00
}
Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType)
2013-02-16 16:14:39 -08:00
{
if (!sender)
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
uint32 CurrentDistance, ClosestDistance = 4294967295u;
2013-05-04 18:06:58 -07:00
Mob *CurrentMob, *ClosestMob = nullptr;
2013-02-16 16:14:39 -08:00
auto it = mob_list.begin();
while (it != mob_list.end()) {
CurrentMob = it->second;
++it;
2013-05-06 13:07:41 -04:00
if (CurrentMob->GetBodyType() != BodyType)
2013-02-16 16:14:39 -08:00
continue;
2013-05-06 13:07:41 -04:00
CurrentDistance = ((CurrentMob->GetY() - sender->GetY()) * (CurrentMob->GetY() - sender->GetY())) +
2013-05-09 10:44:08 -04:00
((CurrentMob->GetX() - sender->GetX()) * (CurrentMob->GetX() - sender->GetX()));
2013-02-16 16:14:39 -08:00
if (CurrentDistance < ClosestDistance) {
2013-02-16 16:14:39 -08:00
ClosestDistance = CurrentDistance;
ClosestMob = CurrentMob;
}
}
return ClosestMob;
}
2016-11-30 20:53:39 -05:00
void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, int pcnpc, std::list<Mob*> &m_list)
2013-02-16 16:14:39 -08:00
{
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *ptr = it->second;
if (ptr == start) {
++it;
2013-02-16 16:14:39 -08:00
continue;
}
2016-11-30 20:53:39 -05:00
// check PC/NPC only flag 1 = PCs, 2 = NPCs
if (pcnpc == 1 && !ptr->IsClient() && !ptr->IsMerc() && !ptr->IsBot()) {
2016-11-30 20:53:39 -05:00
++it;
continue;
} else if (pcnpc == 2 && (ptr->IsClient() || ptr->IsMerc() || ptr->IsBot())) {
2016-11-30 20:53:39 -05:00
++it;
continue;
}
2014-08-03 16:04:55 -04:00
float x_diff = ptr->GetX() - start->GetX();
float y_diff = ptr->GetY() - start->GetY();
float z_diff = ptr->GetZ() - start->GetZ();
2013-02-16 16:14:39 -08:00
x_diff *= x_diff;
y_diff *= y_diff;
z_diff *= z_diff;
2014-08-03 16:04:55 -04:00
if ((x_diff + y_diff) <= (radius * radius) && (x_diff + y_diff) >= (min_radius * min_radius))
2013-02-16 16:14:39 -08:00
if(z_diff <= (height * height))
m_list.push_back(ptr);
++it;
2013-02-16 16:14:39 -08:00
}
}
Client *EntityList::FindCorpseDragger(uint16 CorpseID)
2013-02-16 16:14:39 -08:00
{
auto it = client_list.begin();
while (it != client_list.end()) {
if (it->second->IsDraggingCorpse(CorpseID))
return it->second;
++it;
2013-05-06 13:07:41 -04:00
}
return nullptr;
2013-02-16 16:14:39 -08:00
}
Mob *EntityList::GetTargetForVirus(Mob *spreader, int range)
2013-02-16 16:14:39 -08:00
{
int max_spread_range = RuleI(Spells, VirusSpreadDistance);
if (range)
max_spread_range = range;
2013-05-06 13:07:41 -04:00
std::vector<Mob *> TargetsInRange;
2013-05-06 13:07:41 -04:00
auto it = mob_list.begin();
while (it != mob_list.end()) {
Mob *cur = it->second;
2013-02-16 16:14:39 -08:00
// Make sure the target is in range, has los and is not the mob doing the spreading
if ((cur->GetID() != spreader->GetID()) &&
(cur->CalculateDistance(spreader->GetX(), spreader->GetY(),
spreader->GetZ()) <= max_spread_range) &&
(spreader->CheckLosFN(cur))) {
2013-02-16 16:14:39 -08:00
// If the spreader is an npc it can only spread to other npc controlled mobs
if (spreader->IsNPC() && !spreader->IsPet() && !spreader->CastToNPC()->GetSwarmOwner() && cur->IsNPC()) {
TargetsInRange.push_back(cur);
2013-02-16 16:14:39 -08:00
}
// If the spreader is an npc controlled pet it can spread to any other npc or an npc controlled pet
else if (spreader->IsNPC() && spreader->IsPet() && spreader->GetOwner()->IsNPC()) {
if (cur->IsNPC() && !cur->IsPet()) {
TargetsInRange.push_back(cur);
} else if (cur->IsNPC() && cur->IsPet() && cur->GetOwner()->IsNPC()) {
TargetsInRange.push_back(cur);
}
else if (cur->IsNPC() && cur->CastToNPC()->GetSwarmOwner() && cur->GetOwner()->IsNPC()) {
TargetsInRange.push_back(cur);
2013-02-16 16:14:39 -08:00
}
}
// if the spreader is anything else(bot, pet, etc) then it should spread to everything but non client controlled npcs
else if (!spreader->IsNPC() && !cur->IsNPC()) {
TargetsInRange.push_back(cur);
2013-02-16 16:14:39 -08:00
}
// if its a pet we need to determine appropriate targets(pet to client, pet to pet, pet to bot, etc)
else if (spreader->IsNPC() && (spreader->IsPet() || spreader->CastToNPC()->GetSwarmOwner()) && !spreader->GetOwner()->IsNPC()) {
if (!cur->IsNPC()) {
TargetsInRange.push_back(cur);
2013-02-16 16:14:39 -08:00
}
else if (cur->IsNPC() && (cur->IsPet() || cur->CastToNPC()->GetSwarmOwner()) && !cur->GetOwner()->IsNPC()) {
TargetsInRange.push_back(cur);
2013-02-16 16:14:39 -08:00
}
}
}
++it;
2013-02-16 16:14:39 -08:00
}
if(TargetsInRange.empty())
2013-05-04 18:06:58 -07:00
return nullptr;
2013-02-16 16:14:39 -08:00
return TargetsInRange[zone->random.Int(0, TargetsInRange.size() - 1)];
2013-02-16 16:14:39 -08:00
}
2013-05-09 10:44:08 -04:00
void EntityList::StopMobAI()
{
for (auto &mob : mob_list) {
mob.second->AI_Stop();
mob.second->AI_ShutDown();
}
}
2015-06-20 19:44:00 -07:00
void EntityList::SendAlternateAdvancementStats() {
for(auto &c : client_list) {
c.second->SendAlternateAdvancementTable();
c.second->SendAlternateAdvancementStats();
c.second->SendAlternateAdvancementPoints();
}
}
void EntityList::ReloadMerchants() {
for (auto it = npc_list.begin();it != npc_list.end(); ++it) {
NPC *cur = it->second;
if (cur->MerchantType != 0) {
zone->LoadNewMerchantData(cur->MerchantType);
}
}
}
2019-12-29 02:42:52 -06:00
/**
2019-12-29 03:15:42 -06:00
* If we have a distance requested that is greater than our scanning distance
* then we return the full list
*
2019-12-29 02:42:52 -06:00
* @param mob
* @param distance
* @return
*/
std::unordered_map<uint16, Mob *> &EntityList::GetCloseMobList(Mob *mob, float distance)
{
if (distance <= RuleI(Range, MobCloseScanDistance)) {
return mob->close_mobs;
}
return mob_list;
}