mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
340 lines
9.0 KiB
C++
340 lines
9.0 KiB
C++
/* EQEMu: Everquest Server Emulator
|
|
Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net)
|
|
|
|
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.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
|
are required to give you total support for your newly bought product;
|
|
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
#include "../common/debug.h"
|
|
#include "../common/types.h"
|
|
#include "entity.h"
|
|
#include "masterentity.h"
|
|
#include "../common/spdat.h"
|
|
#include "../common/MiscFunctions.h"
|
|
|
|
/*
|
|
|
|
Schema:
|
|
CREATE TABLE traps (
|
|
id int(11) NOT NULL auto_increment,
|
|
zone varchar(16) NOT NULL default '',
|
|
x int(11) NOT NULL default '0',
|
|
y int(11) NOT NULL default '0',
|
|
z int(11) NOT NULL default '0',
|
|
chance tinyint NOT NULL default '0',
|
|
maxzdiff float NOT NULL default '0',
|
|
radius float NOT NULL default '0',
|
|
effect int(11) NOT NULL default '0',
|
|
effectvalue int(11) NOT NULL default '0',
|
|
effectvalue2 int(11) NOT NULL default '0',
|
|
message varcahr(200) NOT NULL;
|
|
skill int(11) NOT NULL default '0',
|
|
spawnchance int(11) NOT NULL default '0',
|
|
PRIMARY KEY (id)
|
|
) TYPE=MyISAM;
|
|
|
|
|
|
*/
|
|
|
|
Trap::Trap() :
|
|
Entity(),
|
|
respawn_timer(600000),
|
|
chkarea_timer(500)
|
|
{
|
|
trap_id = 0;
|
|
x = 0;
|
|
y = 0;
|
|
z = 0;
|
|
maxzdiff = 0;
|
|
radius = 0;
|
|
effect = 0;
|
|
effectvalue = 0;
|
|
effectvalue2 = 0;
|
|
skill = 0;
|
|
level = 0;
|
|
respawn_timer.Disable();
|
|
detected = false;
|
|
disarmed = false;
|
|
respawn_time = 0;
|
|
respawn_var = 0;
|
|
hiddenTrigger = NULL;
|
|
ownHiddenTrigger = false;
|
|
}
|
|
|
|
Trap::~Trap()
|
|
{
|
|
//don't need to clean up mob as traps are always cleaned up same time as NPCs
|
|
//cleaning up mob here can actually cause a crash via race condition
|
|
}
|
|
|
|
bool Trap::Process()
|
|
{
|
|
if (chkarea_timer.Enabled() && chkarea_timer.Check()
|
|
/*&& zone->GetClientCount() > 0*/ )
|
|
{
|
|
Mob* trigger = entity_list.GetTrapTrigger(this);
|
|
if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM()))
|
|
{
|
|
Trigger(trigger);
|
|
}
|
|
}
|
|
if (respawn_timer.Enabled() && respawn_timer.Check())
|
|
{
|
|
detected = false;
|
|
disarmed = false;
|
|
chkarea_timer.Enable();
|
|
respawn_timer.Disable();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Trap::Trigger(Mob* trigger)
|
|
{
|
|
int i = 0;
|
|
const NPCType* tmp = 0;
|
|
switch (effect)
|
|
{
|
|
case trapTypeDebuff:
|
|
if(message.empty())
|
|
{
|
|
entity_list.MessageClose(trigger,false,100,13,"%s triggers a trap!",trigger->GetName());
|
|
}
|
|
else
|
|
{
|
|
entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str());
|
|
}
|
|
if(hiddenTrigger){
|
|
hiddenTrigger->SpellFinished(effectvalue, trigger, 10, 0, -1, spells[effectvalue].ResistDiff);
|
|
}
|
|
break;
|
|
case trapTypeAlarm:
|
|
if (message.empty())
|
|
{
|
|
entity_list.MessageClose(trigger,false,effectvalue,13,"A loud alarm rings out through the air...");
|
|
}
|
|
else
|
|
{
|
|
entity_list.MessageClose(trigger,false,effectvalue,13,"%s",message.c_str());
|
|
}
|
|
|
|
entity_list.SendAlarm(this,trigger,effectvalue);
|
|
break;
|
|
case trapTypeMysticSpawn:
|
|
if (message.empty())
|
|
{
|
|
entity_list.MessageClose(trigger,false,100,13,"The air shimmers...");
|
|
}
|
|
else
|
|
{
|
|
entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str());
|
|
}
|
|
|
|
for (i = 0; i < effectvalue2; i++)
|
|
{
|
|
if ((tmp = database.GetNPCType(effectvalue)))
|
|
{
|
|
NPC* new_npc = new NPC(tmp, 0, x-5+MakeRandomInt(0, 10), y-5+MakeRandomInt(0, 10), z-5+MakeRandomInt(0, 10), MakeRandomInt(0, 249), FlyMode3);
|
|
new_npc->AddLootTable();
|
|
entity_list.AddNPC(new_npc);
|
|
new_npc->AddToHateList(trigger,1);
|
|
}
|
|
}
|
|
break;
|
|
case trapTypeBanditSpawn:
|
|
if (message.empty())
|
|
{
|
|
entity_list.MessageClose(trigger,false,100,13,"A bandit leaps out from behind a tree!");
|
|
}
|
|
else
|
|
{
|
|
entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str());
|
|
}
|
|
|
|
for (i = 0; i < effectvalue2; i++)
|
|
{
|
|
if ((tmp = database.GetNPCType(effectvalue)))
|
|
{
|
|
NPC* new_npc = new NPC(tmp, 0, x-2+MakeRandomInt(0, 5), y-2+MakeRandomInt(0, 5), z-2+MakeRandomInt(0, 5), MakeRandomInt(0, 249), FlyMode3);
|
|
new_npc->AddLootTable();
|
|
entity_list.AddNPC(new_npc);
|
|
new_npc->AddToHateList(trigger,1);
|
|
}
|
|
}
|
|
break;
|
|
case trapTypeDamage:
|
|
if (message.empty())
|
|
{
|
|
entity_list.MessageClose(trigger,false,100,13,"%s triggers a trap!",trigger->GetName());
|
|
}
|
|
else
|
|
{
|
|
entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str());
|
|
}
|
|
if(trigger->IsClient())
|
|
{
|
|
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct));
|
|
CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer;
|
|
int dmg = MakeRandomInt(effectvalue, effectvalue2);
|
|
trigger->SetHP(trigger->GetHP() - dmg);
|
|
a->damage = dmg;
|
|
a->sequence = MakeRandomInt(0, 1234567);
|
|
a->source = GetHiddenTrigger()!=NULL ? GetHiddenTrigger()->GetID() : trigger->GetID();
|
|
a->spellid = 0;
|
|
a->target = trigger->GetID();
|
|
a->type = 253;
|
|
trigger->CastToClient()->QueuePacket(outapp);
|
|
safe_delete(outapp);
|
|
}
|
|
}
|
|
respawn_timer.Start((respawn_time + MakeRandomInt(0, respawn_var)) * 1000);
|
|
chkarea_timer.Disable();
|
|
disarmed = true;
|
|
}
|
|
|
|
Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) {
|
|
LinkedListIterator<Trap*> iterator(trap_list);
|
|
iterator.Reset();
|
|
float dist = 999999;
|
|
Trap* current_trap = NULL;
|
|
|
|
float max_dist2 = max_dist*max_dist;
|
|
Trap *cur;
|
|
while(iterator.MoreElements())
|
|
{
|
|
cur = iterator.GetData();
|
|
if(!cur->disarmed) {
|
|
float curdist = 0;
|
|
float tmp = searcher->GetX() - cur->x;
|
|
curdist += tmp*tmp;
|
|
tmp = searcher->GetY() - cur->y;
|
|
curdist += tmp*tmp;
|
|
tmp = searcher->GetZ() - cur->z;
|
|
curdist += tmp*tmp;
|
|
|
|
if (curdist < max_dist2 && curdist < dist)
|
|
{
|
|
dist = curdist;
|
|
current_trap = cur;
|
|
}
|
|
}
|
|
iterator.Advance();
|
|
}
|
|
return current_trap;
|
|
}
|
|
|
|
Mob* EntityList::GetTrapTrigger(Trap* trap) {
|
|
LinkedListIterator<Client*> iterator(client_list);
|
|
|
|
Mob* savemob = 0;
|
|
iterator.Reset();
|
|
|
|
float xdiff, ydiff, zdiff;
|
|
|
|
float maxdist = trap->radius * trap->radius;
|
|
|
|
while(iterator.MoreElements()) {
|
|
Client* cur = iterator.GetData();
|
|
zdiff = cur->GetZ() - trap->z;
|
|
if(zdiff < 0)
|
|
zdiff = 0 - zdiff;
|
|
|
|
xdiff = cur->GetX() - trap->x;
|
|
ydiff = cur->GetY() - trap->y;
|
|
if ((xdiff*xdiff + ydiff*ydiff) <= maxdist
|
|
&& zdiff < trap->maxzdiff)
|
|
{
|
|
if (MakeRandomInt(0,100) < trap->chance)
|
|
return(cur);
|
|
else
|
|
savemob = cur;
|
|
}
|
|
iterator.Advance();
|
|
}
|
|
return savemob;
|
|
}
|
|
|
|
//todo: rewrite this to not need direct access to trap members.
|
|
bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) {
|
|
char errbuf[MYSQL_ERRMSG_SIZE];
|
|
char *query = 0;
|
|
MYSQL_RES *result;
|
|
MYSQL_ROW row;
|
|
|
|
// int char_num = 0;
|
|
unsigned long* lengths;
|
|
|
|
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id,x,y,z,effect,effectvalue,effectvalue2,skill,maxzdiff,radius,chance,message,respawn_time,respawn_var,level FROM traps WHERE zone='%s' AND version=%u", zonename, version), errbuf, &result)) {
|
|
safe_delete_array(query);
|
|
while ((row = mysql_fetch_row(result)))
|
|
{
|
|
lengths = mysql_fetch_lengths(result);
|
|
Trap* trap = new Trap();
|
|
trap->trap_id = atoi(row[0]);
|
|
trap->x = atof(row[1]);
|
|
trap->y = atof(row[2]);
|
|
trap->z = atof(row[3]);
|
|
trap->effect = atoi(row[4]);
|
|
trap->effectvalue = atoi(row[5]);
|
|
trap->effectvalue2 = atoi(row[6]);
|
|
trap->skill = atoi(row[7]);
|
|
trap->maxzdiff = atof(row[8]);
|
|
trap->radius = atof(row[9]);
|
|
trap->chance = atoi(row[10]);
|
|
trap->message = row[11];
|
|
trap->respawn_time = atoi(row[12]);
|
|
trap->respawn_var = atoi(row[13]);
|
|
trap->level = atoi(row[14]);
|
|
entity_list.AddTrap(trap);
|
|
trap->CreateHiddenTrigger();
|
|
}
|
|
mysql_free_result(result);
|
|
}
|
|
else {
|
|
LogFile->write(EQEMuLog::Error, "Error in LoadTraps query '%s': %s", query, errbuf);
|
|
safe_delete_array(query);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Trap::CreateHiddenTrigger()
|
|
{
|
|
if(hiddenTrigger)
|
|
return;
|
|
|
|
const NPCType *base_type = database.GetNPCType(500);
|
|
NPCType *make_npc = new NPCType;
|
|
memcpy(make_npc, base_type, sizeof(NPCType));
|
|
make_npc->max_hp = 100000;
|
|
make_npc->cur_hp = 100000;
|
|
strcpy(make_npc->name, "a_trap");
|
|
make_npc->runspeed = 0.0f;
|
|
make_npc->bodytype = BT_Special;
|
|
make_npc->race = 127;
|
|
make_npc->gender = 0;
|
|
make_npc->loottable_id = 0;
|
|
make_npc->npc_spells_id = 0;
|
|
make_npc->d_meele_texture1 = 0;
|
|
make_npc->d_meele_texture2 = 0;
|
|
make_npc->trackable = 0;
|
|
make_npc->level = level;
|
|
strcpy(make_npc->npc_attacks, "ABHG");
|
|
NPC* npca = new NPC(make_npc, 0, x, y, z, 0, FlyMode3);
|
|
npca->GiveNPCTypeData(make_npc);
|
|
entity_list.AddNPC(npca);
|
|
|
|
hiddenTrigger = npca;
|
|
ownHiddenTrigger = true;
|
|
}
|