/* 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 */ //extends the parser to include perl #ifndef EMBPARSER_CPP #define EMBPARSER_CPP #ifdef EMBPERL #include "../common/debug.h" #include "masterentity.h" #include "../common/features.h" #include "embparser.h" #include "questmgr.h" #include "command.h" #include "../common/seperator.h" #include "../common/MiscFunctions.h" #include "QGlobals.h" #include "zone.h" #include //these MUST be in the same order as the QuestEventID enum const char *QuestEventSubroutines[_LargestEventID] = { "EVENT_SAY", "EVENT_ITEM", "EVENT_DEATH", "EVENT_SPAWN", "EVENT_ATTACK", "EVENT_COMBAT", "EVENT_AGGRO", "EVENT_SLAY", "EVENT_NPC_SLAY", "EVENT_WAYPOINT_ARRIVE", "EVENT_WAYPOINT_DEPART", "EVENT_TIMER", "EVENT_SIGNAL", "EVENT_HP", "EVENT_ENTER", "EVENT_EXIT", "EVENT_ENTERZONE", "EVENT_CLICKDOOR", "EVENT_LOOT", "EVENT_ZONE", "EVENT_LEVEL_UP", "EVENT_KILLED_MERIT", "EVENT_CAST_ON", "EVENT_TASKACCEPTED", "EVENT_TASK_STAGE_COMPLETE", "EVENT_TASK_UPDATE", "EVENT_TASK_COMPLETE", "EVENT_TASK_FAIL", "EVENT_AGGRO_SAY", "EVENT_PLAYER_PICKUP", "EVENT_POPUPRESPONSE", "EVENT_PROXIMITY_SAY", "EVENT_CAST", "EVENT_SCALE_CALC", "EVENT_ITEM_ENTERZONE", "EVENT_TARGET_CHANGE", "EVENT_HATE_LIST", "EVENT_SPELL_EFFECT_CLIENT", "EVENT_SPELL_EFFECT_NPC", "EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT", "EVENT_SPELL_EFFECT_BUFF_TIC_NPC", "EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE", "EVENT_COMBINE_SUCCESS", "EVENT_COMBINE_FAILURE", "EVENT_ITEM_CLICK", "EVENT_ITEM_CLICK_CAST", "EVENT_GROUP_CHANGE", "EVENT_FORAGE_SUCCESS", "EVENT_FORAGE_FAILURE", "EVENT_FISH_START", "EVENT_FISH_SUCCESS", "EVENT_FISH_FAILURE", "EVENT_CLICK_OBJECT", "EVENT_DISCOVER_ITEM", "EVENT_DISCONNECT", "EVENT_CONNECT", "EVENT_ITEM_TICK", "EVENT_DUEL_WIN", "EVENT_DUEL_LOSE" }; extern Zone* zone; PerlembParser::PerlembParser(void) : Parser() { perl = nullptr; eventQueueProcessing = false; globalPlayerQuestLoaded = pQuestReadyToLoad; globalNPCQuestLoaded = nQuestReadyToLoad; } PerlembParser::~PerlembParser() { safe_delete(perl); } void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, const char * value) const { if(!perl) return; //this crap cant possibly throw anything in its current state... oh well try { perl->setstr(std::string(pkgprefix).append("::").append(varname).c_str(), value); //todo: consider replacing ' w/ ", so that values can be expanded on the perl side } catch(const char * err) { //todo: consider rethrowing LogFile->write(EQEMuLog::Status, "Error exporting var: %s", err); } } // Exports key-value pairs to a hash named pkgprefix::hashname void PerlembParser::ExportHash(const char *pkgprefix, const char *hashname, std::map &vals) { if (!perl) return; try { perl->sethash( std::string(pkgprefix).append("::").append(hashname).c_str(), vals ); } catch(const char * err) { LogFile->write(EQEMuLog::Status, "Error exporting hash: %s", err); } } void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, int value) const { if(!perl) return; //this crap cant possibly throw anything in its current state... oh well try { perl->seti(std::string(pkgprefix).append("::").append(varname).c_str(), value); } catch(const char * err) { LogFile->write(EQEMuLog::Status, "Error exporting var: %s", err); } } void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, unsigned int value) const { if(!perl) return; //this crap cant possibly throw anything in its current state... oh well try { perl->seti(std::string(pkgprefix).append("::").append(varname).c_str(), value); } catch(const char * err) { LogFile->write(EQEMuLog::Status, "Error exporting var: %s", err); } } void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, float value) const { if(!perl) return; //this crap cant possibly throw anything in its current state... oh well try { perl->setd(std::string(pkgprefix).append("::").append(varname).c_str(), value); } catch(const char * err) { LogFile->write(EQEMuLog::Status, "Error exporting var: %s", err); } } void PerlembParser::ExportVarComplex(const char * pkgprefix, const char * varname, const char * value) const { if(!perl) return; try { //todo: consider replacing ' w/ ", so that values can be expanded on the perl side perl->eval(std::string("$").append(pkgprefix).append("::").append(varname).append("=").append(value).append(";").c_str()); } catch(const char * err) { //todo: consider rethrowing LogFile->write(EQEMuLog::Status, "Error exporting var: %s", err); } } void PerlembParser::HandleQueue() { if(eventQueueProcessing) return; eventQueueProcessing = true; while(!eventQueue.empty()) { EventRecord e = eventQueue.front(); eventQueue.pop(); EventCommon(e.event, e.objid, e.data.c_str(), e.npcmob, e.iteminst, e.mob, e.extradata, e.global); } eventQueueProcessing = false; } void PerlembParser::EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, ItemInst* iteminst, Mob* mob, uint32 extradata, bool global) { if(!perl) return; if(event >= _LargestEventID) return; if(perl->InUse()) { //queue the event for later. EventRecord e; e.event = event; e.objid = objid; if(data != nullptr) e.data = data; e.npcmob = npcmob; e.iteminst = iteminst; e.mob = mob; e.extradata = extradata; e.global = global; eventQueue.push(e); return; } bool isPlayerQuest = false; bool isGlobalPlayerQuest = false; bool isGlobalNPC = false; bool isItemQuest = false; bool isSpellQuest = false; if(event == EVENT_SPELL_EFFECT_CLIENT || event == EVENT_SPELL_EFFECT_NPC || event == EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT || event == EVENT_SPELL_EFFECT_BUFF_TIC_NPC || event == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE) { isSpellQuest = true; } else { if(!npcmob && mob) { if(!iteminst) { if(global) { isGlobalPlayerQuest = true; } else { isPlayerQuest = true; } } else isItemQuest = true; } } std::string packagename; if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest){ if(global){ isGlobalNPC = true; packagename = "global_npc"; LoadGlobalNPCScript(); }else{ packagename = GetPkgPrefix(objid); if(!isloaded(packagename.c_str())) { LoadScript(objid, zone->GetShortName()); } } } else if(isItemQuest) { const Item_Struct* item = iteminst->GetItem(); if (!item) return; if (event == EVENT_SCALE_CALC || event == EVENT_ITEM_ENTERZONE) { packagename = item->CharmFile; if(!isloaded(packagename.c_str())) { LoadItemScript(iteminst, packagename, itemQuestScale); } } else if (event == EVENT_ITEM_CLICK || event == EVENT_ITEM_CLICK_CAST) { packagename = "script_"; packagename += itoa(item->ScriptFileID); if(!isloaded(packagename.c_str())) { LoadItemScript(iteminst, packagename, itemScriptFileID); } } else { packagename = "item_"; packagename += itoa(objid); if(!isloaded(packagename.c_str())) LoadItemScript(iteminst, packagename, itemQuestID); } } else if(isPlayerQuest) { if(!zone || !zone->GetShortName()) // possible segfault fix return; packagename = "player"; packagename += "_"; packagename += zone->GetShortName(); if(!isloaded(packagename.c_str())) { LoadPlayerScript(zone->GetShortName()); } } else if(isGlobalPlayerQuest) { packagename = "global_player"; if(!isloaded(packagename.c_str())) { LoadGlobalPlayerScript(); } } else { packagename = "spell_effect_"; packagename += data; if(!isloaded(packagename.c_str())) { LoadSpellScript(atoi(data)); } } const char *sub_name = QuestEventSubroutines[event]; //make sure the sub we need even exists before we even do all this crap. if(!perl->SubExists(packagename.c_str(), sub_name)) { return; } int charid = 0; if (mob && mob->IsClient()) { // some events like waypoint and spawn don't have a player involved charid = mob->CastToClient()->CharacterID(); } else { if(npcmob) { charid = -npcmob->GetNPCTypeID(); // make char id negative npc id as a fudge } else if(mob && mob->IsNPC()) { charid = -mob->CastToNPC()->GetNPCTypeID(); // make char id negative npc id as a fudge } } ExportVar(packagename.c_str(), "charid", charid); //NPC quest if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { //only export for npcs that are global enabled. if(npcmob && npcmob->GetQglobal()) { std::map globhash; QGlobalCache *npc_c = nullptr; QGlobalCache *char_c = nullptr; QGlobalCache *zone_c = nullptr; //retrieve our globals npc_c = npcmob->GetQGlobals(); if(mob && mob->IsClient()) char_c = mob->CastToClient()->GetQGlobals(); zone_c = zone->GetQGlobals(); if(!npc_c) { npc_c = npcmob->CreateQGlobals(); npc_c->LoadByNPCID(npcmob->GetNPCTypeID()); } if(!char_c) { if(mob && mob->IsClient()) { char_c = mob->CastToClient()->CreateQGlobals(); char_c->LoadByCharID(mob->CastToClient()->CharacterID()); } } if(!zone_c) { zone_c = zone->CreateQGlobals(); zone_c->LoadByZoneID(zone->GetZoneID()); zone_c->LoadByGlobalContext(); } std::list globalMap; if(npc_c) { QGlobalCache::Combine(globalMap, npc_c->GetBucket(), npcmob->GetNPCTypeID(), charid, zone->GetZoneID()); } if(char_c) { QGlobalCache::Combine(globalMap, char_c->GetBucket(), npcmob->GetNPCTypeID(), charid, zone->GetZoneID()); } if(zone_c) { QGlobalCache::Combine(globalMap, zone_c->GetBucket(), npcmob->GetNPCTypeID(), charid, zone->GetZoneID()); } std::list::iterator iter = globalMap.begin(); while(iter != globalMap.end()) { globhash[(*iter).name] = (*iter).value; ExportVar(packagename.c_str(), (*iter).name.c_str(), (*iter).value.c_str()); ++iter; } ExportHash(packagename.c_str(), "qglobals", globhash); } } else { std::map globhash; QGlobalCache *char_c = nullptr; QGlobalCache *zone_c = nullptr; //retrieve our globals if(mob && mob->IsClient()) char_c = mob->CastToClient()->GetQGlobals(); zone_c = zone->GetQGlobals(); if(!char_c) { if(mob && mob->IsClient()) { char_c = mob->CastToClient()->CreateQGlobals(); char_c->LoadByCharID(mob->CastToClient()->CharacterID()); } } if(!zone_c) { zone_c = zone->CreateQGlobals(); zone_c->LoadByZoneID(zone->GetZoneID()); zone_c->LoadByGlobalContext(); } std::list globalMap; if(char_c) { QGlobalCache::Combine(globalMap, char_c->GetBucket(), 0, charid, zone->GetZoneID()); } if(zone_c) { QGlobalCache::Combine(globalMap, zone_c->GetBucket(), 0, charid, zone->GetZoneID()); } std::list::iterator iter = globalMap.begin(); while(iter != globalMap.end()) { globhash[(*iter).name] = (*iter).value; ExportVar(packagename.c_str(), (*iter).name.c_str(), (*iter).value.c_str()); ++iter; } ExportHash(packagename.c_str(), "qglobals", globhash); } uint8 fac = 0; if (mob && mob->IsClient()) { ExportVar(packagename.c_str(), "uguild_id", mob->CastToClient()->GuildID()); ExportVar(packagename.c_str(), "uguildrank", mob->CastToClient()->GuildRank()); ExportVar(packagename.c_str(), "status", mob->CastToClient()->Admin()); } if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest){ if (mob && npcmob && mob->IsClient() && npcmob->IsNPC()) { Client* client = mob->CastToClient(); NPC* npc = npcmob->CastToNPC(); // Need to figure out why one of these casts would fail.. if (client && npc) { fac = client->GetFactionLevel(client->CharacterID(), npcmob->GetID(), client->GetRace(), client->GetClass(), client->GetDeity(), npc->GetPrimaryFaction(), npcmob); } else if (!client) { LogFile->write(EQEMuLog::Status, "WARNING: cast failure on mob->CastToClient()"); } else if (!npc) { LogFile->write(EQEMuLog::Status, "WARNING: cast failure on npcmob->CastToNPC()"); } } } if (mob) { ExportVar(packagename.c_str(), "name", mob->GetName()); ExportVar(packagename.c_str(), "race", GetRaceName(mob->GetRace())); ExportVar(packagename.c_str(), "class", GetEQClassName(mob->GetClass())); ExportVar(packagename.c_str(), "ulevel", mob->GetLevel()); ExportVar(packagename.c_str(), "userid", mob->GetID()); } if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) { if (npcmob) { ExportVar(packagename.c_str(), "mname", npcmob->GetName()); ExportVar(packagename.c_str(), "mobid", npcmob->GetID()); ExportVar(packagename.c_str(), "mlevel", npcmob->GetLevel()); ExportVar(packagename.c_str(), "hpratio",npcmob->GetHPRatio()); ExportVar(packagename.c_str(), "x", npcmob->GetX() ); ExportVar(packagename.c_str(), "y", npcmob->GetY() ); ExportVar(packagename.c_str(), "z", npcmob->GetZ() ); ExportVar(packagename.c_str(), "h", npcmob->GetHeading() ); if ( npcmob->GetTarget() ) { ExportVar(packagename.c_str(), "targetid", npcmob->GetTarget()->GetID()); ExportVar(packagename.c_str(), "targetname", npcmob->GetTarget()->GetName()); } } if (fac) { ExportVar(packagename.c_str(), "faction", itoa(fac)); } } if (zone) { ExportVar(packagename.c_str(), "zoneid", zone->GetZoneID()); ExportVar(packagename.c_str(), "zoneln", zone->GetLongName()); ExportVar(packagename.c_str(), "zonesn", zone->GetShortName()); ExportVar(packagename.c_str(), "instanceid", zone->GetInstanceID()); ExportVar(packagename.c_str(), "instanceversion", zone->GetInstanceVersion()); TimeOfDay_Struct eqTime; zone->zone_time.getEQTimeOfDay( time(0), &eqTime); ExportVar(packagename.c_str(), "zonehour", eqTime.hour - 1); ExportVar(packagename.c_str(), "zonemin", eqTime.minute); ExportVar(packagename.c_str(), "zonetime", (eqTime.hour - 1) * 100 + eqTime.minute); ExportVar(packagename.c_str(), "zoneweather", zone->zone_weather); } // $hasitem #define HASITEM_FIRST 0 #define HASITEM_LAST 29 // this includes worn plus 8 base slots #define HASITEM_ISNULLITEM(item) ((item==-1) || (item==0)) if(mob && mob->IsClient()) { std::string hashname = packagename + std::string("::hasitem"); #if EQDEBUG >= 7 LogFile->write(EQEMuLog::Debug, "starting hasitem, on : %s",hashname.c_str() ); #endif //start with an empty hash perl->eval(std::string("%").append(hashname).append(" = ();").c_str()); for(int slot=HASITEM_FIRST; slot<=HASITEM_LAST;slot++) { char *hi_decl=nullptr; int itemid=mob->CastToClient()->GetItemIDAt(slot); if(!HASITEM_ISNULLITEM(itemid)) { MakeAnyLenString(&hi_decl, "push (@{$%s{%d}},%d);",hashname.c_str(),itemid,slot); // this is annoying #if EQDEBUG >= 7 LogFile->write(EQEMuLog::Debug, "declare hasitem : %s",hi_decl); #endif perl->eval(hi_decl); safe_delete_array(hi_decl); } } } // $oncursor if(mob && mob->IsClient()) { std::string hashname = packagename + std::string("::oncursor"); perl->eval(std::string("%").append(hashname).append(" = ();").c_str()); char *hi_decl = nullptr; int itemid = mob->CastToClient()->GetItemIDAt(30); if(!HASITEM_ISNULLITEM(itemid)) { MakeAnyLenString(&hi_decl, "push (@{$%s{%d}},%d);",hashname.c_str(),itemid,30); perl->eval(hi_decl); safe_delete_array(hi_decl); } } //do any event-specific stuff... switch (event) { case EVENT_SAY: { if (npcmob && npcmob->GetAppearance() != eaDead) npcmob->FaceTarget(mob); ExportVar(packagename.c_str(), "data", objid); ExportVar(packagename.c_str(), "text", data); ExportVar(packagename.c_str(), "langid", extradata); break; } case EVENT_ITEM: { if (npcmob->GetAppearance() != eaDead) npcmob->FaceTarget(mob); //this is such a hack... why aren't these just set directly.. ExportVar(packagename.c_str(), "item1", GetVar("item1", objid).c_str()); ExportVar(packagename.c_str(), "item2", GetVar("item2", objid).c_str()); ExportVar(packagename.c_str(), "item3", GetVar("item3", objid).c_str()); ExportVar(packagename.c_str(), "item4", GetVar("item4", objid).c_str()); ExportVar(packagename.c_str(), "item1_charges", GetVar("item1.charges", objid).c_str()); ExportVar(packagename.c_str(), "item2_charges", GetVar("item2.charges", objid).c_str()); ExportVar(packagename.c_str(), "item3_charges", GetVar("item3.charges", objid).c_str()); ExportVar(packagename.c_str(), "item4_charges", GetVar("item4.charges", objid).c_str()); ExportVar(packagename.c_str(), "item1_attuned", GetVar("item1.attuned", objid).c_str()); ExportVar(packagename.c_str(), "item2_attuned", GetVar("item2.attuned", objid).c_str()); ExportVar(packagename.c_str(), "item3_attuned", GetVar("item3.attuned", objid).c_str()); ExportVar(packagename.c_str(), "item4_attuned", GetVar("item4.attuned", objid).c_str()); ExportVar(packagename.c_str(), "copper", GetVar("copper", objid).c_str()); ExportVar(packagename.c_str(), "silver", GetVar("silver", objid).c_str()); ExportVar(packagename.c_str(), "gold", GetVar("gold", objid).c_str()); ExportVar(packagename.c_str(), "platinum", GetVar("platinum", objid).c_str()); std::string hashname = packagename + std::string("::itemcount"); perl->eval(std::string("%").append(hashname).append(" = ();").c_str()); perl->eval(std::string("++$").append(hashname).append("{$").append(packagename).append("::item1};").c_str()); perl->eval(std::string("++$").append(hashname).append("{$").append(packagename).append("::item2};").c_str()); perl->eval(std::string("++$").append(hashname).append("{$").append(packagename).append("::item3};").c_str()); perl->eval(std::string("++$").append(hashname).append("{$").append(packagename).append("::item4};").c_str()); break; } case EVENT_WAYPOINT_ARRIVE: case EVENT_WAYPOINT_DEPART: { ExportVar(packagename.c_str(), "wp", data); break; } case EVENT_HP: { if (extradata == 1) { ExportVar(packagename.c_str(), "hpevent", "-1"); ExportVar(packagename.c_str(), "inchpevent", data); } else { ExportVar(packagename.c_str(), "hpevent", data); ExportVar(packagename.c_str(), "inchpevent", "-1"); } break; } case EVENT_TIMER: { ExportVar(packagename.c_str(), "timer", data); break; } case EVENT_SIGNAL: { ExportVar(packagename.c_str(), "signal", data); break; } case EVENT_NPC_SLAY: { ExportVar(packagename.c_str(), "killed", mob->GetNPCTypeID()); break; } case EVENT_COMBAT: { ExportVar(packagename.c_str(), "combat_state", data); break; } case EVENT_CLICKDOOR: { Seperator *sep = new Seperator(data); ExportVar(packagename.c_str(), "doorid", sep->arg[0]); ExportVar(packagename.c_str(), "version", sep->arg[1]); break; } case EVENT_LOOT:{ Seperator *sep = new Seperator(data); ExportVar(packagename.c_str(), "looted_id", sep->arg[0]); ExportVar(packagename.c_str(), "looted_charges", sep->arg[1]); ExportVar(packagename.c_str(), "corpse", sep->arg[2]); safe_delete(sep); break; } case EVENT_ZONE:{ ExportVar(packagename.c_str(), "target_zone_id", data); break; } case EVENT_CAST_ON: case EVENT_CAST:{ ExportVar(packagename.c_str(), "spell_id", data); break; } case EVENT_TASKACCEPTED:{ ExportVar(packagename.c_str(), "task_id", data); break; } case EVENT_TASK_STAGE_COMPLETE:{ Seperator *sep = new Seperator(data); ExportVar(packagename.c_str(), "task_id", sep->arg[0]); ExportVar(packagename.c_str(), "activity_id", sep->arg[1]); safe_delete(sep); break; } case EVENT_TASK_FAIL:{ Seperator *sep = new Seperator(data); ExportVar(packagename.c_str(), "task_id", sep->arg[0]); safe_delete(sep); break; } case EVENT_TASK_COMPLETE: case EVENT_TASK_UPDATE:{ Seperator *sep = new Seperator(data); ExportVar(packagename.c_str(), "donecount", sep->arg[0]); ExportVar(packagename.c_str(), "activity_id", sep->arg[1]); ExportVar(packagename.c_str(), "task_id", sep->arg[2]); safe_delete(sep); break; } case EVENT_PLAYER_PICKUP:{ ExportVar(packagename.c_str(), "picked_up_id", data); break; } case EVENT_AGGRO_SAY: { ExportVar(packagename.c_str(), "data", objid); ExportVar(packagename.c_str(), "text", data); ExportVar(packagename.c_str(), "langid", extradata); break; } case EVENT_POPUPRESPONSE:{ ExportVar(packagename.c_str(), "popupid", data); break; } case EVENT_PROXIMITY_SAY: { ExportVar(packagename.c_str(), "data", objid); ExportVar(packagename.c_str(), "text", data); ExportVar(packagename.c_str(), "langid", extradata); break; } case EVENT_SCALE_CALC: case EVENT_ITEM_ENTERZONE: { ExportVar(packagename.c_str(), "itemid", objid); ExportVar(packagename.c_str(), "itemname", iteminst->GetItem()->Name); break; } case EVENT_ITEM_TICK: { ExportVar(packagename.c_str(), "itemid", objid); ExportVar(packagename.c_str(), "itemname", iteminst->GetItem()->Name); ExportVar(packagename.c_str(), "invslot", extradata); break; } case EVENT_ITEM_CLICK_CAST: case EVENT_ITEM_CLICK: { ExportVar(packagename.c_str(), "itemid", objid); ExportVar(packagename.c_str(), "itemname", iteminst->GetItem()->Name); ExportVar(packagename.c_str(), "slotid", extradata); break; } case EVENT_GROUP_CHANGE: { if(mob && mob->IsClient()) { ExportVar(packagename.c_str(), "grouped", mob->IsGrouped()); ExportVar(packagename.c_str(), "raided", mob->IsRaidGrouped()); } break; } case EVENT_HATE_LIST: { ExportVar(packagename.c_str(), "hate_state", data); break; } case EVENT_SPELL_EFFECT_CLIENT: case EVENT_SPELL_EFFECT_NPC: case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT: case EVENT_SPELL_EFFECT_BUFF_TIC_NPC: { ExportVar(packagename.c_str(), "caster_id", extradata); break; } //tradeskill events case EVENT_COMBINE_SUCCESS: case EVENT_COMBINE_FAILURE: { ExportVar(packagename.c_str(), "recipe_id", extradata); ExportVar(packagename.c_str(), "recipe_name", data); break; } case EVENT_FORAGE_SUCCESS: { ExportVar(packagename.c_str(), "foraged_item", extradata); break; } case EVENT_FISH_SUCCESS: { ExportVar(packagename.c_str(), "fished_item", extradata); break; } case EVENT_CLICK_OBJECT: { ExportVar(packagename.c_str(), "objectid", data); break; } case EVENT_DISCOVER_ITEM: { ExportVar(packagename.c_str(), "itemid", extradata); break; } case EVENT_DUEL_WIN: case EVENT_DUEL_LOSE: { ExportVar(packagename.c_str(), "enemyname", data); ExportVar(packagename.c_str(), "enemyid", extradata); break; } //nothing special about these events case EVENT_DEATH: case EVENT_SPAWN: case EVENT_ATTACK: case EVENT_SLAY: case EVENT_AGGRO: case EVENT_ENTER: case EVENT_EXIT: case EVENT_ENTERZONE: case EVENT_LEVEL_UP: case EVENT_KILLED_MERIT: case EVENT_TARGET_CHANGE: break; default: { // should we do anything here? break; } } if(isPlayerQuest || isGlobalPlayerQuest){ SendCommands(packagename.c_str(), sub_name, 0, mob, mob, nullptr); } else if(isItemQuest) { SendCommands(packagename.c_str(), sub_name, 0, mob, mob, iteminst); } else if(isSpellQuest) { if(mob) { SendCommands(packagename.c_str(), sub_name, 0, mob, mob, nullptr); } else { SendCommands(packagename.c_str(), sub_name, 0, npcmob, mob, nullptr); } } else { SendCommands(packagename.c_str(), sub_name, objid, npcmob, mob, nullptr); } //now handle any events that cropped up... HandleQueue(); } void PerlembParser::EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data) { EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, init, extra_data, true); } void PerlembParser::EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data) { EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, init, extra_data); } void PerlembParser::EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data) { EventCommon(evt, 0, data.c_str(), nullptr, nullptr, client, extra_data); } void PerlembParser::EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data) { EventCommon(evt, 0, data.c_str(), nullptr, nullptr, client, extra_data, true); } void PerlembParser::EventItem(QuestEventID evt, Client *client, ItemInst *item, uint32 objid, uint32 extra_data) { EventCommon(evt, objid, nullptr, nullptr, item, client, extra_data); } void PerlembParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data) { EventCommon(evt, 0, itoa(spell_id), npc, nullptr, client, extra_data); } void PerlembParser::ReloadQuests(bool with_timers) { if (with_timers) { // Clear all quest timers before reloading quests to prevent potential crashes quest_manager.ClearAllTimers(); } command_clear_perl(); try { if(perl == nullptr) perl = new Embperl; else perl->Reinit(); map_funs(); } catch(std::exception &e) { if(perl != nullptr) { delete perl; perl = nullptr; } LogFile->write(EQEMuLog::Status, "Error re-initializing perlembed: %s", e.what()); throw e.what(); } try { LoadScript(0, nullptr); } catch(const char * err) { LogFile->write(EQEMuLog::Status, "Error loading default script: %s", err); } hasQuests.clear(); playerQuestLoaded.clear(); globalPlayerQuestLoaded = pQuestReadyToLoad; globalNPCQuestLoaded = nQuestReadyToLoad; itemQuestLoaded.clear(); spellQuestLoaded.clear(); } int PerlembParser::LoadScript(int npcid, const char * zone, Mob* activater) { if(!perl) { return(0); } //we have already tried to load this quest... if(hasQuests.count(npcid) == 1) { return(1); } std::string filename = "quests/", packagename = GetPkgPrefix(npcid); //each package name is of the form qstxxxx where xxxx = npcid (since numbers alone are not valid package names) questMode curmode = questDefault; FILE *tmpf; //LogFile->write(EQEMuLog::Debug, "LoadScript(%d, %s):\n", npcid, zone); if(!npcid || !zone) { //Load quests/default.pl filename += DEFAULT_QUEST_PREFIX; filename += ".pl"; curmode = questDefault; } else { filename += zone; filename += "/"; #ifdef QUEST_SCRIPTS_BYNAME std::string bnfilename = filename; #endif filename += itoa(npcid); filename += ".pl"; curmode = questByID; #ifdef QUEST_SCRIPTS_BYNAME //assuming name limit stays 64 chars. char tmpname[64]; int count0 = 0; bool filefound = false; tmpf = fopen(filename.c_str(), "r"); if(tmpf != nullptr) { fclose(tmpf); filefound = true; } //LogFile->write(EQEMuLog::Debug, " tried '%s': %d", filename.c_str(), filefound); tmpname[0] = 0; //if there is no file for the NPC's ID, try for the NPC's name if(!filefound) { //revert to just path filename = bnfilename; const NPCType *npct = database.GetNPCType(npcid); if(npct == nullptr) { //LogFile->write(EQEMuLog::Debug, " no npc type"); //revert and go on with life filename += itoa(npcid); filename += ".pl"; curmode = questByID; } else { //trace out the ` characters, turn into - int nlen = strlen(npct->name); //just to make sure if(nlen < 64) { int r; //this should get our nullptr as well.. for(r = 0; r <= nlen; r++) { tmpname[r] = npct->name[r]; //watch for 00 delimiter if(tmpname[r] == '0') { count0++; //second '0' if(count0 > 1) { //stop before previous 0 tmpname[r-1] = '\0'; break; } } else { count0 = 0; } //rewrite ` to be more file name friendly if(tmpname[r] == '`') { tmpname[r] = '-'; } } filename += tmpname; filename += ".pl"; curmode = questByName; } else { //LogFile->write(EQEMuLog::Debug, " namelen too long"); //revert and go on with life, again filename += itoa(npcid); filename += ".pl"; curmode = questByID; } } } #ifdef QUEST_TEMPLATES_BYNAME tmpf = fopen(filename.c_str(), "r"); if(tmpf != nullptr) { fclose(tmpf); filefound = true; } //LogFile->write(EQEMuLog::Debug, " tried '%s': %d", filename.c_str(), filefound2); //if there is no file for the NPC's ID or name, //try for the NPC's name in the templates directory //only works if we have gotten the NPC's name above if(!filefound) { if(tmpname[0] != 0) { //revert to just path filename = "quests/"; filename += QUEST_TEMPLATES_DIRECTORY; filename += "/"; filename += tmpname; filename += ".pl"; curmode = questTemplate; //LogFile->write(EQEMuLog::Debug, " template '%s'", filename.c_str(), filefound2); } else { //LogFile->write(EQEMuLog::Debug, " no template name"); filename = "quests/"; filename += QUEST_TEMPLATES_DIRECTORY; filename += "/"; filename += itoa(npcid); filename += ".pl"; curmode = questTemplateByID; } } #endif //QUEST_TEMPLATES_BYNAME #endif //QUEST_SCRIPTS_BYNAME tmpf = fopen(filename.c_str(), "r"); if(tmpf != nullptr) { fclose(tmpf); filefound = true; } // If by ID, Name or Template wasn't found, load /quests/zone/default.pl if(!filefound) { //Load Default Quests Per Zone quests/zonename/default.pl filename = bnfilename; filename += "default.pl"; curmode = questDefaultByZone; //LogFile->write(EQEMuLog::Debug, "LoadScript(%s)", filename.c_str()); } tmpf = fopen(filename.c_str(), "r"); if(tmpf != nullptr) { fclose(tmpf); filefound = true; } // If zone template isn't found look for it globally /quests/template/default.pl if(!filefound) { //Load Default Quests Globally //filename = bnfilename; filename = "quests/"; filename += QUEST_TEMPLATES_DIRECTORY; filename += "/"; filename += "default.pl"; curmode = questDefaultByZone; //LogFile->write(EQEMuLog::Debug, "LoadScript(%s)", filename.c_str()); } } //check for existance of quest file before trying to make perl load it. tmpf = fopen(filename.c_str(), "r"); if(tmpf == nullptr) { //the npc has no qst file, attach the defaults std::string setdefcmd = "$"; setdefcmd += packagename; setdefcmd += "::isdefault = 1;"; perl->eval(setdefcmd.c_str()); setdefcmd = "$"; setdefcmd += packagename; setdefcmd += "::isloaded = 1;"; perl->eval(setdefcmd.c_str()); hasQuests[npcid] = questDefault; return(1); } else { fclose(tmpf); } //LogFile->write(EQEMuLog::Debug, " finally settling on '%s'", filename.c_str()); // LogFile->write(EQEMuLog::Status, "Looking for quest file: '%s'", filename.c_str()); // todo: decide whether or not to delete the package to allow for script refreshes w/o restarting the server // remember to guard against deleting the default package, on a similar note... consider deleting packages upon zone change // try { perl->eval(std::string("delete_package(\"").append(packagename).append("\");").c_str()); } // catch(...) {/*perl balked at us trynig to delete a non-existant package... no big deal.*/} try { perl->eval_file(packagename.c_str(), filename.c_str()); } catch(const char * err) { //try to reduce some of the console spam... //todo: tweak this to be more accurate at deciding what to filter (we don't want to gag legit errors) //if(!strstr(err,"No such file or directory")) LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s (reverting to default questfile)", filename.c_str(), err); } //todo: change this to just read eval_file's %cache - duh! if(!isloaded(packagename.c_str())) { //the npc has no qst file, attach the defaults std::string setdefcmd = "$"; setdefcmd += packagename; setdefcmd += "::isdefault = 1;"; perl->eval(setdefcmd.c_str()); setdefcmd = "$"; setdefcmd += packagename; setdefcmd += "::isloaded = 1;"; perl->eval(setdefcmd.c_str()); curmode = questDefault; } hasQuests[npcid] = curmode; return(1); } int PerlembParser::LoadGlobalNPCScript() { if(!perl) return 0; if(perl->InUse()) { return 0; } if(globalNPCQuestLoaded != nQuestReadyToLoad) { return 1; } std::string filename = "quests/"; filename += QUEST_TEMPLATES_DIRECTORY; filename += "/global_npc.pl"; std::string packagename = "global_npc"; try { perl->eval_file(packagename.c_str(), filename.c_str()); } catch(const char * err) { LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); } globalNPCQuestLoaded = nQuestLoaded; return 1; } int PerlembParser::LoadPlayerScript(const char *zone_name) { if(!perl) return 0; if(perl->InUse()) { return 0; } if(playerQuestLoaded.count(zone_name) == 1) { return 1; } std::string filename= "quests/"; filename += zone_name; filename += "/player_v"; filename += itoa(zone->GetInstanceVersion()); filename += ".pl"; std::string packagename = "player"; packagename += "_"; packagename += zone_name; try { perl->eval_file(packagename.c_str(), filename.c_str()); } catch(const char * err) { LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); } if(!isloaded(packagename.c_str())) { filename= "quests/"; filename += zone_name; filename += "/player.pl"; try { perl->eval_file(packagename.c_str(), filename.c_str()); } catch(const char * err) { LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); } } //todo: change this to just read eval_file's %cache - duh! if(!isloaded(packagename.c_str())) { filename = "quests/"; filename += QUEST_TEMPLATES_DIRECTORY; filename += "/player.pl"; try { perl->eval_file(packagename.c_str(), filename.c_str()); } catch(const char * err) { LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); } if(!isloaded(packagename.c_str())) { playerQuestLoaded[zone_name] = pQuestUnloaded; return 0; } } if(perl->SubExists(packagename.c_str(), "EVENT_CAST")) playerQuestLoaded[zone_name] = pQuestEventCast; else playerQuestLoaded[zone_name] = pQuestLoaded; return 1; } int PerlembParser::LoadGlobalPlayerScript() { if(!perl) return 0; if(perl->InUse()) { return 0; } if(globalPlayerQuestLoaded != pQuestReadyToLoad) { return 1; } std::string filename = "quests/"; filename += QUEST_TEMPLATES_DIRECTORY; filename += "/global_player.pl"; std::string packagename = "global_player"; try { perl->eval_file(packagename.c_str(), filename.c_str()); } catch(const char * err) { LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); } if(perl->SubExists(packagename.c_str(), "EVENT_CAST")) globalPlayerQuestLoaded = pQuestEventCast; else globalPlayerQuestLoaded = pQuestLoaded; return 1; } int PerlembParser::LoadItemScript(ItemInst* iteminst, std::string packagename, itemQuestMode Qtype) { if(!perl) return 0; if(perl->InUse()) { return 0; } // if we've already tried to load it, don't try again if(itemQuestLoaded.count(packagename) == 1) return 1; std::string filename = "quests/items/"; if(Qtype == itemQuestScale) filename += packagename; else if(Qtype == itemQuestLore) { filename += "lore_"; filename += itoa(iteminst->GetItem()->LoreGroup); } else if(Qtype == itemScriptFileID) { filename += "script_"; filename += itoa(iteminst->GetItemScriptID()); } else filename += itoa(iteminst->GetID()); filename += ".pl"; printf("Loading file %s\n",filename.c_str()); try { perl->eval_file(packagename.c_str(), filename.c_str()); } catch(const char* err) { LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); } if(!isloaded(packagename.c_str())) { itemQuestLoaded[packagename] = Qtype; return 0; } itemQuestLoaded[packagename] = itemQuestUnloaded; return 1; } int PerlembParser::LoadSpellScript(uint32 id) { if(!perl) return 0; if(perl->InUse()) { return 0; } // if we've already tried to load it, don't try again if(spellQuestLoaded.count(id) == 1) return 1; std::string filename = "quests/spells/"; std::string packagename = "spell_effect_"; filename += itoa(id); packagename += itoa(id); filename += ".pl"; printf("Loading file %s\n", filename.c_str()); try { perl->eval_file(packagename.c_str(), filename.c_str()); } catch(const char* err) { LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); } if(!isloaded(packagename.c_str())) { spellQuestLoaded[id] = spellQuestFailed; return 0; } spellQuestLoaded[id] = spellQuestFullyLoaded; return 1; } bool PerlembParser::isloaded(const char *packagename) const { char buffer[120]; snprintf(buffer, 120, "$%s::isloaded", packagename); if(!perl->VarExists(packagename, "isloaded")) return(false); return perl->geti(buffer); } //this function does NOT consider the default to be a quest int PerlembParser::HasQuestFile(uint32 npcid) { int32 qstID = GetNPCqstID(npcid); int success=1; if(hasQuests.count(npcid) == 1) { questMode mode = hasQuests[npcid]; if(mode == questDefault) return(false); return(true); } if (qstID==-1) success = LoadScript(npcid, zone->GetShortName()); if (!success) return(false); if(hasQuests.count(npcid) != 1) return(false); questMode mode = hasQuests[npcid]; if(mode == questDefault) return(false); return(true); } bool PerlembParser::HasQuestSub(uint32 npcid, const char *subname) { int32 qstID = GetNPCqstID(npcid); if (qstID == -1) { if(!LoadScript(npcid, zone->GetShortName())) { return(false); } } std::string packagename = GetPkgPrefix(npcid); return(perl->SubExists(packagename.c_str(), subname)); } bool PerlembParser::HasGlobalQuestSub(const char *subname) { if(!LoadGlobalNPCScript()) { return(false); } std::string packagename = "global_npc"; return(perl->SubExists(packagename.c_str(), subname)); } bool PerlembParser::PlayerHasQuestSub(const char *subname) { std::string packagename = "player_"; packagename += zone->GetShortName(); if(playerQuestLoaded.count(zone->GetShortName()) == 0) LoadPlayerScript(zone->GetShortName()); if(subname == "EVENT_CAST") return (playerQuestLoaded[zone->GetShortName()] == pQuestEventCast); return(perl->SubExists(packagename.c_str(), subname)); } bool PerlembParser::GlobalPlayerHasQuestSub(const char *subname) { std::string packagename = "global_player"; if(globalPlayerQuestLoaded == pQuestReadyToLoad) LoadGlobalPlayerScript(); if(subname == "EVENT_CAST") return (globalPlayerQuestLoaded == pQuestEventCast); return(perl->SubExists(packagename.c_str(), subname)); } bool PerlembParser::SpellHasQuestSub(uint32 id, const char *subname) { std::string packagename = "spell_effect_"; packagename += itoa(id); if(spellQuestLoaded.count(id) == 0) LoadSpellScript(id); return(perl->SubExists(packagename.c_str(), subname)); } bool PerlembParser::ItemHasQuestSub(ItemInst *itm, const char *subname) { std::string packagename; const Item_Struct* item = itm->GetItem(); if(!item) return false; if(strcmp("EVENT_SCALE_CALC", subname) == 0 || strcmp("EVENT_ITEM_ENTERZONE", subname) == 0) { packagename = item->CharmFile; if(itemQuestLoaded.count(packagename) == 0) LoadItemScript(itm, packagename, itemQuestScale); } else if(strcmp("EVENT_ITEM_CLICK", subname) == 0 || strcmp("EVENT_ITEM_CLICK_CAST", subname) == 0 ) { packagename = "script_"; packagename += itoa(item->ScriptFileID); if(itemQuestLoaded.count(packagename) == 0) LoadItemScript(itm, packagename, itemScriptFileID); } else { packagename = "item_"; packagename += itoa(item->ID); if(itemQuestLoaded.count(packagename) == 0) LoadItemScript(itm, packagename, itemQuestID); } return perl->SubExists(packagename.c_str(), subname); } //utility - return something of the form "qst1234"... //will return "qst[DEFAULT_QUEST_PREFIX]" if the npc in question has no script of its own or failed to compile and defaultOK is set to true std::string PerlembParser::GetPkgPrefix(uint32 npcid, bool defaultOK) { char buf[32]; snprintf(buf, 32, "qst%lu", (unsigned long) npcid); // std::string prefix = "qst"; // std::string temp = prefix + (std::string)(itoa(npcid)); // if(!npcid || (defaultOK && isdefault(temp.c_str()))) if(!npcid || (defaultOK && (hasQuests.count(npcid) == 1 && hasQuests[npcid] == questDefault))) { snprintf(buf, 32, "qst%s", DEFAULT_QUEST_PREFIX.c_str()); } return(std::string(buf)); } void PerlembParser::SendCommands(const char * pkgprefix, const char *event, uint32 npcid, Mob* other, Mob* mob, ItemInst* iteminst) { if(!perl) return; _ZP(PerlembParser_SendCommands); if(mob && mob->IsClient()) quest_manager.StartQuest(other, mob->CastToClient()); else quest_manager.StartQuest(other, nullptr); try { std::string cmd = "@quest::cmd_queue = (); package " + (std::string)(pkgprefix) + (std::string)(";"); perl->eval(cmd.c_str()); perl->dosub(std::string(pkgprefix).append("::").append(event).c_str()); } catch(const char * err) { //try to reduce some of the console spam... //todo: tweak this to be more accurate at deciding what to filter (we don't want to gag legit errors) if(!strstr(err,"Undefined subroutine")) LogFile->write(EQEMuLog::Status, "Script error: %s::%s - %s", pkgprefix, event, err); return; } int numcoms = perl->geti("quest::qsize()"); for(int c = 0; c < numcoms; ++c) { char var[1024] = {0}; sprintf(var,"$quest::cmd_queue[%d]{func}",c); std::string cmd = perl->getstr(var); sprintf(var,"$quest::cmd_queue[%d]{args}",c); std::string args = perl->getstr(var); size_t num_args = std::count(args.begin(), args.end(), ',') + 1; ExCommands(cmd, args, num_args, npcid, other, mob); } quest_manager.EndQuest(); } #ifdef EMBPERL_COMMANDS void PerlembParser::ExecCommand(Client *c, Seperator *sep) { #ifdef EMBPERL_XS_CLASSES SV *client = get_sv("commands::client", true); if(c != nullptr) { sv_setref_pv(client, "Client", c); } else { //clear out the value, mainly to get rid of blessedness //which prevents us from accessing an invalid pointer sv_setsv(client, newSV(0)); } #endif char namebuf[128]; snprintf(namebuf, 128, "commands::%s", sep->arg[0]+1); namebuf[127] = '\0'; std::vector args; int i; for(i = 1; i <= sep->argnum; i++) { args.push_back(sep->arg[i]); } try { perl->dosub(namebuf, &args); } catch(const char * err) { c->Message(13, "Error executing perl command, check the logs."); LogFile->write(EQEMuLog::Quest, "Script error: %s", err); } //now handle any events that cropped up... HandleQueue(); } #endif void PerlembParser::map_funs() { //map each "exported" function to a variable list that we can access from c //todo: // break 1|settimer 2|stoptimer 1|dbspawnadd 2|flagcheck 1|write 2| // settarget 2|follow 1|sfollow 1|save 1|setallskill 1 //update/ensure that the api matches that of the native script engine perl->eval( "{" "package quest;" "&boot_qc;" "@cmd_queue = ();" "sub qsize{return scalar(@cmd_queue)};" "sub say{push(@cmd_queue,{func=>'say',args=>join(',',@_)});}" "sub emote{push(@cmd_queue,{func=>'emote',args=>join(',',@_)});}" "sub shout{push(@cmd_queue,{func=>'shout',args=>join(',',@_)});}" "sub spawn{push(@cmd_queue,{func=>'spawn',args=>join(',',@_)});}" "sub spawn2{push(@cmd_queue,{func=>'spawn2',args=>join(',',@_)});}" "sub unique_spawn{push(@cmd_queue,{func=>'unique_spawn',args=>join(',',@_)});}" "sub echo{push(@cmd_queue,{func=>'echo',args=>join(',',@_)});}" "sub summonitem{push(@cmd_queue,{func=>'summonitem',args=>join(',',@_)});}" "sub castspell{push(@cmd_queue,{func=>'castspell',args=>join(',',@_)});}" "sub selfcast{push(@cmd_queue,{func=>'selfcast',args=>join(',',@_)});}" "sub depop{push(@cmd_queue,{func=>'depop'});}" "sub exp{push(@cmd_queue,{func=>'exp',args=>join(',',@_)});}" "sub level{push(@cmd_queue,{func=>'level',args=>join(',',@_)});}" "sub safemove{push(@cmd_queue,{func=>'safemove'});}" "sub rain{push(@cmd_queue,{func=>'rain',args=>join(',',@_)});}" "sub snow{push(@cmd_queue,{func=>'snow',args=>join(',',@_)});}" "sub givecash{push(@cmd_queue,{func=>'givecash',args=>join(',',@_)});}" "sub pvp{push(@cmd_queue,{func=>'pvp',args=>join(',',@_)});}" "sub doanim{push(@cmd_queue,{func=>'doanim',args=>join(',',@_)});}" "sub addskill{push(@cmd_queue,{func=>'addskill',args=>join(',',@_)});}" "sub me{push(@cmd_queue,{func=>'me',args=>join(',',@_)});}" "sub permagender{push(@cmd_queue,{func=>'permagender',args=>join(',',@_)});}" "sub permarace{push(@cmd_queue,{func=>'permarace',args=>join(',',@_)});}" "sub scribespells{push(@cmd_queue,{func=>'scribespells',args=>join(',',@_)});}" "sub permaclass{push(@cmd_queue,{func=>'permaclass',args=>join(',',@_)});}" "sub surname{push(@cmd_queue,{func=>'surname',args=>join(',',@_)});}" "sub addldonpoint{push(@cmd_queue,{func=>'addldonpoint',args=>join(',',@_)});}" "sub ding{push(@cmd_queue,{func=>'ding',args=>join(',',@_)});}" "sub faction{push(@cmd_queue,{func=>'faction',args=>join(',',@_)});}" "sub setguild{push(@cmd_queue,{func=>'setguild',args=>join(',',@_)});}" "sub rebind{push(@cmd_queue,{func=>'rebind',args=>join(',',@_)});}" "sub flagcheck{push(@cmd_queue,{func=>'flagcheck',args=>join(',',@_)});}" "sub write{push(@cmd_queue,{func=>'write',args=>join(',',@_)});}" "sub settime{push(@cmd_queue,{func=>'settime',args=>join(',',@_)});}" "sub setsky{push(@cmd_queue,{func=>'setsky',args=>join(',',@_)});}" "sub settimer{push(@cmd_queue,{func=>'settimer',args=>join(',',@_)});}" "sub stoptimer{push(@cmd_queue,{func=>'stoptimer',args=>join(',',@_)});}" "sub settarget{push(@cmd_queue,{func=>'settarget',args=>join(',',@_)});}" "sub follow{push(@cmd_queue,{func=>'follow',args=>join(',',@_)});}" "sub sfollow{push(@cmd_queue,{func=>'sfollow',args=>join(',',@_)});}" "sub movepc{push(@cmd_queue,{func=>'movepc',args=>join(',',@_)});}" "sub gmmove{push(@cmd_queue,{func=>'gmmove',args=>join(',',@_)});}" "sub movegrp{push(@cmd_queue,{func=>'movegrp',args=>join(',',@_)});}" "sub setlanguage{push(@cmd_queue,{func=>'setlanguage',args=>join(',',@_)});}" "sub setskill{push(@cmd_queue,{func=>'setskill',args=>join(',',@_)});}" "sub setallskill{push(@cmd_queue,{func=>'setallskill',args=>join(',',@_)});}" "sub attack{push(@cmd_queue,{func=>'attack',args=>join(',',@_)});}" "sub save{push(@cmd_queue,{func=>'save',args=>join(',',@_)});}" "sub linkitem{push(@cmd_queue,{func=>'linkitem',args=>join(',',@_)});}" "sub sethp{push(@cmd_queue,{func=>'sethp',args=>join(',',@_)});}" "sub signal{push(@cmd_queue,{func=>'signal',args=>join(',',@_)});}" "sub setglobal{push(@cmd_queue,{func=>'setglobal',args=>join(',',@_)});}" "sub targlobal{push(@cmd_queue,{func=>'targlobal',args=>join(',',@_)});}" "sub delglobal{push(@cmd_queue,{func=>'delglobal',args=>join(',',@_)});}" "sub setnexthpevent{push(@cmd_queue,{func=>'setnexthpevent',args=>join(',',@_)});}" "sub setnextinchpevent{push(@cmd_queue,{func=>'setnextinchpevent',args=>join(',',@_)});}" "sub respawn{push(@cmd_queue,{func=>'respawn',args=>join(',',@_)});}" "sub stop{push(@cmd_queue,{func=>'stop',args=>join(',',@_)});}" "sub pause{push(@cmd_queue,{func=>'pause',args=>join(',',@_)});}" "sub resume{push(@cmd_queue,{func=>'resume',args=>join(',',@_)});}" "sub start{push(@cmd_queue,{func=>'start',args=>join(',',@_)});}" "sub moveto{push(@cmd_queue,{func=>'moveto',args=>join(',',@_)});}" "sub warp{push(@cmd_queue,{func=>'warp',args=>join(',',@_)});}" "sub changedeity{push(@cmd_queue,{func=>'changedeity',args=>join(',',@_)});}" "sub addldonpoints{push(@cmd_queue,{func=>'addldonpoints',args=>join(',',@_)});}" "sub addloot{push(@cmd_queue,{func=>'addloot',args=>join(',',@_)});}" "sub traindisc{push(@cmd_queue,{func=>'traindisc',args=>join(',',@_)});}" "sub set_proximity{push(@cmd_queue,{func=>'set_proximity',args=>join(',',@_)});}" "sub clear_proximity{push(@cmd_queue,{func=>'clear_proximity',args=>join(',',@_)});}" "sub setanim{push(@cmd_queue,{func=>'setanim',args=>join(',',@_)});}" "sub showgrid{push(@cmd_queue,{func=>'showgrid',args=>join(',',@_)});}" "sub showpath{push(@cmd_queue,{func=>'showpath',args=>join(',',@_)});}" "sub pathto{push(@cmd_queue,{func=>'pathto',args=>join(',',@_)});}" "sub spawn_condition{push(@cmd_queue,{func=>'spawn_condition',args=>join(',',@_)});}" "sub toggle_spawn_event{push(@cmd_queue,{func=>'toggle_spawn_event',args=>join(',',@_)});}" "sub set_zone_flag{push(@cmd_queue,{func=>'set_zone_flag',args=>join(',',@_)});}" "sub clear_zone_flag{push(@cmd_queue,{func=>'clear_zone_flag',args=>join(',',@_)});}" "package main;" "}" );//eval } #endif //EMBPERL #endif //EMBPARSER_CPP