From 2c6fd44811cc76bb8201d03c96eecfe6381c6a23 Mon Sep 17 00:00:00 2001 From: Russell Kinasz Date: Tue, 2 Jun 2015 12:25:09 -0700 Subject: [PATCH] Implemented encounter timers - no spawn required --- zone/encounter.cpp | 53 +++++++++++++++++++++++++++ zone/encounter.h | 62 ++++++++++++++++++++++++++++++++ zone/entity.cpp | 47 ++++++++++++++++++++++++ zone/entity.h | 8 +++++ zone/lua_general.cpp | 22 ++++++++---- zone/lua_parser.cpp | 25 +++++++++---- zone/lua_parser.h | 6 ++-- zone/lua_parser_events.cpp | 11 ++++++ zone/lua_parser_events.h | 8 +++++ zone/net.cpp | 1 + zone/quest_interface.h | 2 +- zone/quest_parser_collection.cpp | 6 ++-- zone/quest_parser_collection.h | 3 +- zone/questmgr.cpp | 2 ++ 14 files changed, 237 insertions(+), 19 deletions(-) create mode 100644 zone/encounter.cpp create mode 100644 zone/encounter.h diff --git a/zone/encounter.cpp b/zone/encounter.cpp new file mode 100644 index 000000000..1f77de43a --- /dev/null +++ b/zone/encounter.cpp @@ -0,0 +1,53 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2003 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 +*/ + +#ifdef _WINDOWS +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +#include "../common/races.h" +#include "encounter.h" +#include "entity.h" +#include "mob.h" + +class Zone; + +Encounter::Encounter(const char* enc_name) + :Mob + ( + nullptr, nullptr, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, glm::vec4(0,0,0,0), 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ) +{ + encounter_name[0] = 0; + strn0cpy(encounter_name, enc_name, 64); + remove_me = false; +} + +Encounter::~Encounter() +{ + +} + +bool Encounter::Process() { + if (remove_me) return false; + return true; +} diff --git a/zone/encounter.h b/zone/encounter.h new file mode 100644 index 000000000..a2977d7e0 --- /dev/null +++ b/zone/encounter.h @@ -0,0 +1,62 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2003 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 +*/ + +#ifndef ENCOUNTER_H +#define ENCOUNTER_H + +#include "mob.h" +#include "../common/types.h" +#include "../common/timer.h" + +class Group; +class Raid; +struct ExtraAttackOptions; + +class Encounter : public Mob +{ +public: + Encounter(const char* enc_name); + ~Encounter(); + + //abstract virtual function implementations required by base abstract class + virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill) { return true; } + virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false) { return; } + virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, + ExtraAttackOptions *opts = nullptr) { + return false; + } + virtual bool HasRaid() { return false; } + virtual bool HasGroup() { return false; } + virtual Raid* GetRaid() { return 0; } + virtual Group* GetGroup() { return 0; } + + bool IsEncounter() const { return true; } + const char* GetEncounterName() const { return encounter_name; } + + bool Process(); + virtual void Depop(bool not_used = true) { remove_me = true; } + + +protected: + bool remove_me; + char encounter_name[64]; + +private: +}; + +#endif diff --git a/zone/entity.cpp b/zone/entity.cpp index bf1f6b720..baa4f4354 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -173,6 +173,11 @@ Beacon *Entity::CastToBeacon() return static_cast(this); } +Encounter *Entity::CastToEncounter() +{ + return static_cast(this); +} + const Client *Entity::CastToClient() const { if (this == 0x00) { @@ -263,6 +268,11 @@ const Beacon* Entity::CastToBeacon() const return static_cast(this); } +const Encounter* Entity::CastToEncounter() const +{ + return static_cast(this); +} + #ifdef BOTS Bot *Entity::CastToBot() { @@ -533,6 +543,21 @@ void EntityList::BeaconProcess() } } +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... @@ -708,6 +733,12 @@ void EntityList::AddBeacon(Beacon *beacon) beacon_list.insert(std::pair(beacon->GetID(), beacon)); } +void EntityList::AddEncounter(Encounter *encounter) +{ + encounter->SetID(GetFreeID()); + encounter_list.insert(std::pair(encounter->GetID(), encounter)); +} + void EntityList::AddToSpawnQueue(uint16 entityid, NewSpawn_Struct **ns) { uint32 count; @@ -935,6 +966,11 @@ Entity *EntityList::GetEntityBeacon(uint16 id) return beacon_list.count(id) ? beacon_list.at(id) : nullptr; } +Entity *EntityList::GetEntityEncounter(uint16 id) +{ + return encounter_list.count(id) ? encounter_list.at(id) : nullptr; +} + Entity *EntityList::GetID(uint16 get_id) { Entity *ent = 0; @@ -950,6 +986,8 @@ Entity *EntityList::GetID(uint16 get_id) return ent; else if ((ent=entity_list.GetEntityBeacon(get_id)) != 0) return ent; + else if ((ent = entity_list.GetEntityEncounter(get_id)) != 0) + return ent; else return 0; } @@ -3424,6 +3462,15 @@ bool EntityList::IsMobInZone(Mob *who) } ++it; } + + auto enc_it = encounter_list.begin(); + while (enc_it != encounter_list.end()) { + if (enc_it->second == who) { + return true; + } + ++enc_it; + } + return false; } diff --git a/zone/entity.h b/zone/entity.h index 8af87f649..446c5d505 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -30,6 +30,7 @@ #include "position.h" #include "zonedump.h" +class Encounter; class Beacon; class Client; class Corpse; @@ -77,6 +78,7 @@ public: virtual bool IsDoor() const { return false; } virtual bool IsTrap() const { return false; } virtual bool IsBeacon() const { return false; } + virtual bool IsEncounter() const { return false; } virtual bool Process() { return false; } virtual bool Save() { return true; } @@ -91,6 +93,7 @@ public: Doors *CastToDoors(); Trap *CastToTrap(); Beacon *CastToBeacon(); + Encounter *CastToEncounter(); const Client *CastToClient() const; const NPC *CastToNPC() const; @@ -101,6 +104,7 @@ public: const Doors *CastToDoors() const; const Trap *CastToTrap() const; const Beacon *CastToBeacon() const; + const Encounter *CastToEncounter() const; inline const uint16& GetID() const { return id; } inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; } @@ -203,6 +207,7 @@ public: void MobProcess(); void TrapProcess(); void BeaconProcess(); + void EncounterProcess(); void ProcessMove(Client *c, const glm::vec3& location); void ProcessMove(NPC *n, float x, float y, float z); void AddArea(int id, int type, float min_x, float max_x, float min_y, float max_y, float min_z, float max_z); @@ -228,6 +233,7 @@ public: void AddDoor(Doors* door); void AddTrap(Trap* trap); void AddBeacon(Beacon *beacon); + void AddEncounter(Encounter *encounter); void AddProximity(NPC *proximity_for); void Clear(); bool RemoveMob(uint16 delete_id); @@ -266,6 +272,7 @@ public: Entity *GetEntityCorpse(uint16 id); Entity *GetEntityTrap(uint16 id); Entity *GetEntityBeacon(uint16 id); + Entity *GetEntityEncounter(uint16 id); Entity *GetEntityMob(const char *name); Entity *GetEntityCorpse(const char *name); @@ -448,6 +455,7 @@ private: std::unordered_map door_list; std::unordered_map trap_list; std::unordered_map beacon_list; + std::unordered_map encounter_list; std::list proximity_list; std::list group_list; std::list raid_list; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 6b290dc03..271bc0a97 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -34,6 +34,8 @@ struct lua_registered_event { extern std::map> lua_encounter_events_registered; extern std::map lua_encounters_loaded; +extern std::map lua_encounters; + extern void MapOpcodes(); extern void ClearMappedOpcode(EmuOpcode op); @@ -42,19 +44,23 @@ void unregister_event(std::string package_name, std::string name, int evt); void load_encounter(std::string name) { if(lua_encounters_loaded.count(name) > 0) return; - + Encounter *enc = new Encounter(name.c_str()); + entity_list.AddEncounter(enc); + lua_encounters[name] = enc; lua_encounters_loaded[name] = true; - parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, 0); + parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, "", 0); } void load_encounter_with_data(std::string name, std::string info_str) { if(lua_encounters_loaded.count(name) > 0) return; - + Encounter *enc = new Encounter(name.c_str()); + entity_list.AddEncounter(enc); + lua_encounters[name] = enc; lua_encounters_loaded[name] = true; std::vector info_ptrs; info_ptrs.push_back(&info_str); - parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, 0, &info_ptrs); + parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, "", 0, &info_ptrs); } void unload_encounter(std::string name) { @@ -80,8 +86,10 @@ void unload_encounter(std::string name) { } } + lua_encounters[name]->Depop(); + lua_encounters.erase(name); lua_encounters_loaded.erase(name); - parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, 0); + parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, "", 0); } void unload_encounter_with_data(std::string name, std::string info_str) { @@ -109,10 +117,12 @@ void unload_encounter_with_data(std::string name, std::string info_str) { } } + lua_encounters[name]->Depop(); + lua_encounters.erase(name); lua_encounters_loaded.erase(name); std::vector info_ptrs; info_ptrs.push_back(&info_str); - parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, 0, &info_ptrs); + parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, "", 0, &info_ptrs); } void register_event(std::string package_name, std::string name, int evt, luabind::adl::object func) { diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index 7f6412b0f..383691864 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -128,6 +128,7 @@ struct lua_registered_event { std::map> lua_encounter_events_registered; std::map lua_encounters_loaded; +std::map lua_encounters; LuaParser::LuaParser() { for(int i = 0; i < _LargestEventID; ++i) { @@ -135,6 +136,7 @@ LuaParser::LuaParser() { PlayerArgumentDispatch[i] = handle_player_null; ItemArgumentDispatch[i] = handle_item_null; SpellArgumentDispatch[i] = handle_spell_null; + EncounterArgumentDispatch[i] = handle_encounter_null; } NPCArgumentDispatch[EVENT_SAY] = handle_npc_event_say; @@ -213,6 +215,8 @@ LuaParser::LuaParser() { SpellArgumentDispatch[EVENT_SPELL_FADE] = handle_spell_fade; SpellArgumentDispatch[EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE] = handle_translocate_finish; + EncounterArgumentDispatch[EVENT_TIMER] = handle_encounter_timer; + L = nullptr; } @@ -575,7 +579,7 @@ int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc, return 0; } -int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, std::vector *extra_pointers) { +int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -587,10 +591,10 @@ int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, uint return 0; } - return _EventEncounter(package_name, evt, encounter_name, extra_data, extra_pointers); + return _EventEncounter(package_name, evt, encounter_name, data, extra_data, extra_pointers); } -int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, uint32 extra_data, +int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers) { const char *sub_name = LuaEvents[evt]; @@ -604,13 +608,17 @@ int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std:: lua_pushstring(L, encounter_name.c_str()); lua_setfield(L, -2, "name"); - if(extra_pointers) { + /*if(extra_pointers) { std::string *str = EQEmu::any_cast(extra_pointers->at(0)); lua_pushstring(L, str->c_str()); lua_setfield(L, -2, "data"); - } + }*/ - quest_manager.StartQuest(nullptr, nullptr, nullptr, encounter_name); + auto arg_function = EncounterArgumentDispatch[evt]; + arg_function(this, L, data, extra_data, extra_pointers); + + Encounter *enc = lua_encounters[encounter_name]; + quest_manager.StartQuest(enc, nullptr, nullptr, encounter_name); if(lua_pcall(L, 1, 1, 0)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -786,6 +794,11 @@ void LuaParser::ReloadQuests() { lua_encounter_events_registered.clear(); lua_encounters_loaded.clear(); + for (auto encounter : lua_encounters) { + encounter.second->Depop(); + } + lua_encounters.clear(); + if(L) { lua_close(L); } diff --git a/zone/lua_parser.h b/zone/lua_parser.h index 13cebe0fc..63d9facfe 100644 --- a/zone/lua_parser.h +++ b/zone/lua_parser.h @@ -39,7 +39,7 @@ public: std::vector *extra_pointers); virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, std::vector *extra_pointers); - virtual int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, + virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers); virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt); @@ -82,7 +82,7 @@ private: uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); int _EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); - int _EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, uint32 extra_data, + int _EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers); void LoadScript(std::string filename, std::string package_name); @@ -99,6 +99,8 @@ private: PlayerArgumentHandler PlayerArgumentDispatch[_LargestEventID]; ItemArgumentHandler ItemArgumentDispatch[_LargestEventID]; SpellArgumentHandler SpellArgumentDispatch[_LargestEventID]; + EncounterArgumentHandler EncounterArgumentDispatch[_LargestEventID]; + }; #endif diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 2780d0212..adccf433b 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -704,4 +704,15 @@ void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* cl std::vector *extra_pointers) { } +void handle_encounter_timer(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers) { + lua_pushstring(L, data.c_str()); + lua_setfield(L, -2, "timer"); +} + +void handle_encounter_null(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers) { + +} + #endif diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 1965a9189..818bcf5be 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -6,6 +6,7 @@ typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std:: typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector*); typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, ItemInst*, Mob*, std::string, uint32, std::vector*); typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, uint32, std::vector*); +typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, std::string, uint32, std::vector*); //NPC void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, @@ -127,5 +128,12 @@ void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Cl void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, std::vector *extra_pointers); + +//Encounter +void handle_encounter_timer(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers); +void handle_encounter_null(QuestInterface *parse, lua_State* L, std::string data, uint32 extra_data, + std::vector *extra_pointers); + #endif #endif diff --git a/zone/net.cpp b/zone/net.cpp index 92f568af0..ef6b46836 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -423,6 +423,7 @@ int main(int argc, char** argv) { entity_list.Process(); entity_list.MobProcess(); entity_list.BeaconProcess(); + entity_list.EncounterProcess(); if (zone) { if(!zone->Process()) { diff --git a/zone/quest_interface.h b/zone/quest_interface.h index 68c79923f..c1cf3dc73 100644 --- a/zone/quest_interface.h +++ b/zone/quest_interface.h @@ -41,7 +41,7 @@ public: std::vector *extra_pointers) { return 0; } virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, std::vector *extra_pointers) { return 0; } - virtual int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, + virtual int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers) { return 0; } virtual bool HasQuestSub(uint32 npcid, QuestEventID evt) { return false; } diff --git a/zone/quest_parser_collection.cpp b/zone/quest_parser_collection.cpp index 37e518c24..845769532 100644 --- a/zone/quest_parser_collection.cpp +++ b/zone/quest_parser_collection.cpp @@ -434,14 +434,14 @@ int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client return 0; } -int QuestParserCollection::EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, +int QuestParserCollection::EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers) { auto iter = _encounter_quest_status.find(encounter_name); if(iter != _encounter_quest_status.end()) { //loaded or failed to load if(iter->second != QuestFailedToLoad) { std::map::iterator qiter = _interfaces.find(iter->second); - return qiter->second->EventEncounter(evt, encounter_name, extra_data, extra_pointers); + return qiter->second->EventEncounter(evt, encounter_name, data, extra_data, extra_pointers); } } else { std::string filename; @@ -449,7 +449,7 @@ int QuestParserCollection::EventEncounter(QuestEventID evt, std::string encounte if(qi) { _encounter_quest_status[encounter_name] = qi->GetIdentifier(); qi->LoadEncounterScript(filename, encounter_name); - return qi->EventEncounter(evt, encounter_name, extra_data, extra_pointers); + return qi->EventEncounter(evt, encounter_name, data, extra_data, extra_pointers); } else { _encounter_quest_status[encounter_name] = QuestFailedToLoad; } diff --git a/zone/quest_parser_collection.h b/zone/quest_parser_collection.h index 62cb034dc..3ebce378c 100644 --- a/zone/quest_parser_collection.h +++ b/zone/quest_parser_collection.h @@ -21,6 +21,7 @@ #include "../common/types.h" +#include "encounter.h" #include "beacon.h" #include "client.h" #include "corpse.h" @@ -71,7 +72,7 @@ public: std::vector *extra_pointers = nullptr); int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, std::vector *extra_pointers = nullptr); - int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, + int EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector *extra_pointers = nullptr); void GetErrors(std::list &err); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 825e3b7b1..67a1f06da 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -84,6 +84,8 @@ void QuestManager::Process() { if(entity_list.IsMobInZone(cur->mob)) { if(cur->mob->IsNPC()) { parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0); + } else if (cur->mob->IsEncounter()) { + parse->EventEncounter(EVENT_TIMER, cur->mob->CastToEncounter()->GetEncounterName(), cur->name, 0, nullptr); } else { //this is inheriently unsafe if we ever make it so more than npc/client start timers parse->EventPlayer(EVENT_TIMER, cur->mob->CastToClient(), cur->name, 0);