eqemu-server/zone/lua_parser.cpp
Aeadoin 9c3c5b5230
[Experience] Change Exp Calculations to be 64 bit where needed. (#2677)
* [Experience] Change Exp Calculations to be 64 bit where needed.

* Fix lua values

* Formatting
2022-12-30 22:03:30 -05:00

1679 lines
45 KiB
C++

#ifdef LUA_EQEMU
#include "lua.hpp"
#include <luabind/luabind.hpp>
#include <luabind/object.hpp>
#include <ctype.h>
#include <stdio.h>
#include <string>
#include <vector>
#include <algorithm>
#include "../common/spdat.h"
#include "masterentity.h"
#include "questmgr.h"
#include "zone.h"
#include "zone_config.h"
#include "lua_parser.h"
#include "lua_bit.h"
#include "lua_entity.h"
#include "lua_expedition.h"
#include "lua_item.h"
#include "lua_iteminst.h"
#include "lua_mob.h"
#include "lua_hate_list.h"
#include "lua_client.h"
#include "lua_inventory.h"
#include "lua_npc.h"
#include "lua_spell.h"
#include "lua_entity_list.h"
#include "lua_group.h"
#include "lua_raid.h"
#include "lua_corpse.h"
#include "lua_object.h"
#include "lua_door.h"
#include "lua_spawn.h"
#include "lua_packet.h"
#include "lua_general.h"
#include "lua_encounter.h"
#include "lua_stat_bonuses.h"
#include "../common/path_manager.h"
#ifdef BOTS
#include "lua_bot.h"
#endif
const char *LuaEvents[_LargestEventID] = {
"event_say",
"event_trade",
"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_enter_zone",
"event_click_door",
"event_loot",
"event_zone",
"event_level_up",
"event_killed_merit",
"event_cast_on",
"event_task_accepted",
"event_task_stage_complete",
"event_task_update",
"event_task_complete",
"event_task_fail",
"event_aggro_say",
"event_player_pickup",
"event_popup_response",
"event_environmental_damage",
"event_proximity_say",
"event_cast",
"event_cast_begin",
"event_scale_calc",
"event_item_enter_zone",
"event_target_change",
"event_hate_list",
"event_spell_effect",
"event_spell_effect",
"event_spell_buff_tic",
"event_spell_buff_tic",
"event_spell_fade",
"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",
"event_encounter_load",
"event_encounter_unload",
"event_command",
"event_drop_item",
"event_destroy_item",
"event_feign_death",
"event_weapon_proc",
"event_equip_item",
"event_unequip_item",
"event_augment_item",
"event_unaugment_item",
"event_augment_insert",
"event_augment_remove",
"event_enter_area",
"event_leave_area",
"event_respawn",
"event_death_complete",
"event_unhandled_opcode",
"event_tick",
"event_spawn_zone",
"event_death_zone",
"event_use_skill",
"event_combine_validate",
"event_bot_command",
"event_warp",
"event_test_buff",
"event_combine",
"event_consider",
"event_consider_corpse",
"event_loot_zone",
"event_equip_item_client",
"event_unequip_item_client",
"event_skill_up",
"event_language_skill_up",
"event_alt_currency_merchant_buy",
"event_alt_currency_merchant_sell",
"event_merchant_buy",
"event_merchant_sell",
"event_inspect",
"event_task_before_update",
"event_aa_buy",
"event_aa_gain",
"event_payload",
"event_level_down",
"event_gm_command"
};
extern Zone *zone;
struct lua_registered_event {
std::string encounter_name;
luabind::adl::object lua_reference;
QuestEventID event_id;
};
std::map<std::string, std::list<lua_registered_event>> lua_encounter_events_registered;
std::map<std::string, bool> lua_encounters_loaded;
std::map<std::string, Encounter *> lua_encounters;
LuaParser::LuaParser() {
for(int i = 0; i < _LargestEventID; ++i) {
NPCArgumentDispatch[i] = handle_npc_null;
PlayerArgumentDispatch[i] = handle_player_null;
ItemArgumentDispatch[i] = handle_item_null;
SpellArgumentDispatch[i] = handle_spell_null;
EncounterArgumentDispatch[i] = handle_encounter_null;
#ifdef BOTS
BotArgumentDispatch[i] = handle_bot_null;
#endif
}
NPCArgumentDispatch[EVENT_SAY] = handle_npc_event_say;
NPCArgumentDispatch[EVENT_AGGRO_SAY] = handle_npc_event_say;
NPCArgumentDispatch[EVENT_PROXIMITY_SAY] = handle_npc_event_say;
NPCArgumentDispatch[EVENT_TRADE] = handle_npc_event_trade;
NPCArgumentDispatch[EVENT_HP] = handle_npc_event_hp;
NPCArgumentDispatch[EVENT_TARGET_CHANGE] = handle_npc_single_mob;
NPCArgumentDispatch[EVENT_CAST_ON] = handle_npc_cast;
NPCArgumentDispatch[EVENT_KILLED_MERIT] = handle_npc_single_client;
NPCArgumentDispatch[EVENT_SLAY] = handle_npc_single_mob;
NPCArgumentDispatch[EVENT_ENTER] = handle_npc_single_client;
NPCArgumentDispatch[EVENT_EXIT] = handle_npc_single_client;
NPCArgumentDispatch[EVENT_TASK_ACCEPTED] = handle_npc_task_accepted;
NPCArgumentDispatch[EVENT_POPUP_RESPONSE] = handle_npc_popup;
NPCArgumentDispatch[EVENT_WAYPOINT_ARRIVE] = handle_npc_waypoint;
NPCArgumentDispatch[EVENT_WAYPOINT_DEPART] = handle_npc_waypoint;
NPCArgumentDispatch[EVENT_HATE_LIST] = handle_npc_hate;
NPCArgumentDispatch[EVENT_COMBAT] = handle_npc_hate;
NPCArgumentDispatch[EVENT_SIGNAL] = handle_npc_signal;
NPCArgumentDispatch[EVENT_TIMER] = handle_npc_timer;
NPCArgumentDispatch[EVENT_DEATH] = handle_npc_death;
NPCArgumentDispatch[EVENT_DEATH_COMPLETE] = handle_npc_death;
NPCArgumentDispatch[EVENT_DEATH_ZONE] = handle_npc_death;
NPCArgumentDispatch[EVENT_CAST] = handle_npc_cast;
NPCArgumentDispatch[EVENT_CAST_BEGIN] = handle_npc_cast;
NPCArgumentDispatch[EVENT_FEIGN_DEATH] = handle_npc_single_client;
NPCArgumentDispatch[EVENT_ENTER_AREA] = handle_npc_area;
NPCArgumentDispatch[EVENT_LEAVE_AREA] = handle_npc_area;
NPCArgumentDispatch[EVENT_LOOT_ZONE] = handle_npc_loot_zone;
NPCArgumentDispatch[EVENT_SPAWN_ZONE] = handle_npc_spawn_zone;
NPCArgumentDispatch[EVENT_PAYLOAD] = handle_npc_payload;
PlayerArgumentDispatch[EVENT_SAY] = handle_player_say;
PlayerArgumentDispatch[EVENT_ENVIRONMENTAL_DAMAGE] = handle_player_environmental_damage;
PlayerArgumentDispatch[EVENT_DEATH] = handle_player_death;
PlayerArgumentDispatch[EVENT_DEATH_COMPLETE] = handle_player_death;
PlayerArgumentDispatch[EVENT_TIMER] = handle_player_timer;
PlayerArgumentDispatch[EVENT_DISCOVER_ITEM] = handle_player_discover_item;
PlayerArgumentDispatch[EVENT_FISH_SUCCESS] = handle_player_fish_forage_success;
PlayerArgumentDispatch[EVENT_FORAGE_SUCCESS] = handle_player_fish_forage_success;
PlayerArgumentDispatch[EVENT_CLICK_OBJECT] = handle_player_click_object;
PlayerArgumentDispatch[EVENT_CLICK_DOOR] = handle_player_click_door;
PlayerArgumentDispatch[EVENT_SIGNAL] = handle_player_signal;
PlayerArgumentDispatch[EVENT_POPUP_RESPONSE] = handle_player_popup_response;
PlayerArgumentDispatch[EVENT_PLAYER_PICKUP] = handle_player_pick_up;
PlayerArgumentDispatch[EVENT_CAST] = handle_player_cast;
PlayerArgumentDispatch[EVENT_CAST_BEGIN] = handle_player_cast;
PlayerArgumentDispatch[EVENT_CAST_ON] = handle_player_cast;
PlayerArgumentDispatch[EVENT_TASK_FAIL] = handle_player_task_fail;
PlayerArgumentDispatch[EVENT_ZONE] = handle_player_zone;
PlayerArgumentDispatch[EVENT_DUEL_WIN] = handle_player_duel_win;
PlayerArgumentDispatch[EVENT_DUEL_LOSE] = handle_player_duel_loss;
PlayerArgumentDispatch[EVENT_LOOT] = handle_player_loot;
PlayerArgumentDispatch[EVENT_TASK_STAGE_COMPLETE] = handle_player_task_stage_complete;
PlayerArgumentDispatch[EVENT_TASK_COMPLETE] = handle_player_task_update;
PlayerArgumentDispatch[EVENT_TASK_UPDATE] = handle_player_task_update;
PlayerArgumentDispatch[EVENT_TASK_BEFORE_UPDATE] = handle_player_task_update;
PlayerArgumentDispatch[EVENT_COMMAND] = handle_player_command;
PlayerArgumentDispatch[EVENT_COMBINE_SUCCESS] = handle_player_combine;
PlayerArgumentDispatch[EVENT_COMBINE_FAILURE] = handle_player_combine;
PlayerArgumentDispatch[EVENT_FEIGN_DEATH] = handle_player_feign;
PlayerArgumentDispatch[EVENT_ENTER_AREA] = handle_player_area;
PlayerArgumentDispatch[EVENT_LEAVE_AREA] = handle_player_area;
PlayerArgumentDispatch[EVENT_RESPAWN] = handle_player_respawn;
PlayerArgumentDispatch[EVENT_UNHANDLED_OPCODE] = handle_player_packet;
PlayerArgumentDispatch[EVENT_USE_SKILL] = handle_player_use_skill;
PlayerArgumentDispatch[EVENT_TEST_BUFF] = handle_test_buff;
PlayerArgumentDispatch[EVENT_COMBINE_VALIDATE] = handle_player_combine_validate;
PlayerArgumentDispatch[EVENT_BOT_COMMAND] = handle_player_bot_command;
PlayerArgumentDispatch[EVENT_WARP] = handle_player_warp;
PlayerArgumentDispatch[EVENT_COMBINE] = handle_player_quest_combine;
PlayerArgumentDispatch[EVENT_CONSIDER] = handle_player_consider;
PlayerArgumentDispatch[EVENT_CONSIDER_CORPSE] = handle_player_consider_corpse;
PlayerArgumentDispatch[EVENT_EQUIP_ITEM_CLIENT] = handle_player_equip_item;
PlayerArgumentDispatch[EVENT_UNEQUIP_ITEM_CLIENT] = handle_player_equip_item;
PlayerArgumentDispatch[EVENT_SKILL_UP] = handle_player_skill_up;
PlayerArgumentDispatch[EVENT_LANGUAGE_SKILL_UP] = handle_player_skill_up;
PlayerArgumentDispatch[EVENT_ALT_CURRENCY_MERCHANT_BUY] = handle_player_alt_currency_merchant;
PlayerArgumentDispatch[EVENT_ALT_CURRENCY_MERCHANT_SELL] = handle_player_alt_currency_merchant;
PlayerArgumentDispatch[EVENT_MERCHANT_BUY] = handle_player_merchant;
PlayerArgumentDispatch[EVENT_MERCHANT_SELL] = handle_player_merchant;
PlayerArgumentDispatch[EVENT_INSPECT] = handle_player_inspect;
PlayerArgumentDispatch[EVENT_AA_BUY] = handle_player_aa_buy;
PlayerArgumentDispatch[EVENT_AA_GAIN] = handle_player_aa_gain;
PlayerArgumentDispatch[EVENT_PAYLOAD] = handle_player_payload;
PlayerArgumentDispatch[EVENT_LEVEL_UP] = handle_player_level_up;
PlayerArgumentDispatch[EVENT_LEVEL_DOWN] = handle_player_level_down;
PlayerArgumentDispatch[EVENT_GM_COMMAND] = handle_player_gm_command;
ItemArgumentDispatch[EVENT_ITEM_CLICK] = handle_item_click;
ItemArgumentDispatch[EVENT_ITEM_CLICK_CAST] = handle_item_click;
ItemArgumentDispatch[EVENT_TIMER] = handle_item_timer;
ItemArgumentDispatch[EVENT_WEAPON_PROC] = handle_item_proc;
ItemArgumentDispatch[EVENT_LOOT] = handle_item_loot;
ItemArgumentDispatch[EVENT_EQUIP_ITEM] = handle_item_equip;
ItemArgumentDispatch[EVENT_UNEQUIP_ITEM] = handle_item_equip;
ItemArgumentDispatch[EVENT_AUGMENT_ITEM] = handle_item_augment;
ItemArgumentDispatch[EVENT_UNAUGMENT_ITEM] = handle_item_augment;
ItemArgumentDispatch[EVENT_AUGMENT_INSERT] = handle_item_augment_insert;
ItemArgumentDispatch[EVENT_AUGMENT_REMOVE] = handle_item_augment_remove;
SpellArgumentDispatch[EVENT_SPELL_EFFECT_CLIENT] = handle_spell_event;
SpellArgumentDispatch[EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT] = handle_spell_event;
SpellArgumentDispatch[EVENT_SPELL_FADE] = handle_spell_event;
SpellArgumentDispatch[EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE] = handle_translocate_finish;
EncounterArgumentDispatch[EVENT_TIMER] = handle_encounter_timer;
EncounterArgumentDispatch[EVENT_ENCOUNTER_LOAD] = handle_encounter_load;
EncounterArgumentDispatch[EVENT_ENCOUNTER_UNLOAD] = handle_encounter_unload;
#ifdef BOTS
BotArgumentDispatch[EVENT_CAST] = handle_bot_cast;
BotArgumentDispatch[EVENT_CAST_BEGIN] = handle_bot_cast;
BotArgumentDispatch[EVENT_CAST_ON] = handle_bot_cast;
BotArgumentDispatch[EVENT_COMBAT] = handle_bot_combat;
BotArgumentDispatch[EVENT_DEATH] = handle_bot_death;
BotArgumentDispatch[EVENT_DEATH_COMPLETE] = handle_bot_death;
BotArgumentDispatch[EVENT_POPUP_RESPONSE] = handle_bot_popup_response;
BotArgumentDispatch[EVENT_SAY] = handle_bot_say;
BotArgumentDispatch[EVENT_SIGNAL] = handle_bot_signal;
BotArgumentDispatch[EVENT_SLAY] = handle_bot_slay;
BotArgumentDispatch[EVENT_TARGET_CHANGE] = handle_bot_target_change;
BotArgumentDispatch[EVENT_TIMER] = handle_bot_timer;
BotArgumentDispatch[EVENT_TRADE] = handle_bot_trade;
BotArgumentDispatch[EVENT_USE_SKILL] = handle_bot_use_skill;
BotArgumentDispatch[EVENT_PAYLOAD] = handle_bot_payload;
#endif
L = nullptr;
}
LuaParser::~LuaParser() {
// valgrind didn't like when we didn't clean these up :P
lua_encounters.clear();
lua_encounter_events_registered.clear();
lua_encounters_loaded.clear();
if(L) {
lua_close(L);
}
}
int LuaParser::EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
if(!npc) {
return 0;
}
if(!HasQuestSub(npc->GetNPCTypeID(), evt)) {
return 0;
}
std::string package_name = "npc_" + std::to_string(npc->GetNPCTypeID());
return _EventNPC(package_name, evt, npc, init, data, extra_data, extra_pointers);
}
int LuaParser::EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
if(!npc) {
return 0;
}
if(!HasGlobalQuestSub(evt)) {
return 0;
}
return _EventNPC("global_npc", evt, npc, init, data, extra_data, extra_pointers);
}
int LuaParser::_EventNPC(std::string package_name, QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers, luabind::adl::object *l_func) {
const char *sub_name = LuaEvents[evt];
int start = lua_gettop(L);
try {
int npop = 1;
if(l_func != nullptr) {
l_func->push(L);
} else {
lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_getfield(L, -1, sub_name);
npop = 2;
}
lua_createtable(L, 0, 0);
//always push self
Lua_NPC l_npc(npc);
luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc);
l_npc_o.push(L);
lua_setfield(L, -2, "self");
auto arg_function = NPCArgumentDispatch[evt];
arg_function(this, L, npc, init, data, extra_data, extra_pointers);
Client *c = (init && init->IsClient()) ? init->CastToClient() : nullptr;
quest_manager.StartQuest(npc, c);
if(lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
AddError(error);
quest_manager.EndQuest();
lua_pop(L, npop);
return 0;
}
quest_manager.EndQuest();
if(lua_isnumber(L, -1)) {
int ret = static_cast<int>(lua_tointeger(L, -1));
lua_pop(L, npop);
return ret;
}
lua_pop(L, npop);
} catch(std::exception &ex) {
std::string error = "Lua Exception: ";
error += std::string(ex.what());
AddError(error);
//Restore our stack to the best of our ability
int end = lua_gettop(L);
int n = end - start;
if(n > 0) {
lua_pop(L, n);
}
}
return 0;
}
int LuaParser::EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
if(!client) {
return 0;
}
if(!PlayerHasQuestSub(evt)) {
return 0;
}
return _EventPlayer("player", evt, client, data, extra_data, extra_pointers);
}
int LuaParser::EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
if(!client) {
return 0;
}
if(!GlobalPlayerHasQuestSub(evt)) {
return 0;
}
return _EventPlayer("global_player", evt, client, data, extra_data, extra_pointers);
}
int LuaParser::_EventPlayer(std::string package_name, QuestEventID evt, Client *client, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers, luabind::adl::object *l_func) {
const char *sub_name = LuaEvents[evt];
int start = lua_gettop(L);
try {
int npop = 1;
if(l_func != nullptr) {
l_func->push(L);
} else {
lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_getfield(L, -1, sub_name);
npop = 2;
}
lua_createtable(L, 0, 0);
//push self
Lua_Client l_client(client);
luabind::adl::object l_client_o = luabind::adl::object(L, l_client);
l_client_o.push(L);
lua_setfield(L, -2, "self");
auto arg_function = PlayerArgumentDispatch[evt];
arg_function(this, L, client, data, extra_data, extra_pointers);
quest_manager.StartQuest(client, client);
if(lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
AddError(error);
quest_manager.EndQuest();
lua_pop(L, npop);
return 0;
}
quest_manager.EndQuest();
if(lua_isnumber(L, -1)) {
int ret = static_cast<int>(lua_tointeger(L, -1));
lua_pop(L, npop);
return ret;
}
lua_pop(L, npop);
} catch(std::exception &ex) {
std::string error = "Lua Exception: ";
error += std::string(ex.what());
AddError(error);
//Restore our stack to the best of our ability
int end = lua_gettop(L);
int n = end - start;
if(n > 0) {
lua_pop(L, n);
}
}
return 0;
}
int LuaParser::EventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
if(!item) {
return 0;
}
if(!ItemHasQuestSub(item, evt)) {
return 0;
}
std::string package_name = "item_";
package_name += std::to_string(item->GetID());
return _EventItem(package_name, evt, client, item, mob, data, extra_data, extra_pointers);
}
int LuaParser::_EventItem(std::string package_name, QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob,
std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers, luabind::adl::object *l_func) {
const char *sub_name = LuaEvents[evt];
int start = lua_gettop(L);
try {
int npop = 1;
if(l_func != nullptr) {
l_func->push(L);
} else {
lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_getfield(L, -1, sub_name);
npop = 2;
}
lua_createtable(L, 0, 0);
//always push self
Lua_ItemInst l_item(item);
luabind::adl::object l_item_o = luabind::adl::object(L, l_item);
l_item_o.push(L);
lua_setfield(L, -2, "self");
Lua_Client l_client(client);
luabind::adl::object l_client_o = luabind::adl::object(L, l_client);
l_client_o.push(L);
lua_setfield(L, -2, "owner");
//redo this arg function
auto arg_function = ItemArgumentDispatch[evt];
arg_function(this, L, client, item, mob, data, extra_data, extra_pointers);
quest_manager.StartQuest(client, client, item);
if(lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
AddError(error);
quest_manager.EndQuest();
lua_pop(L, npop);
return 0;
}
quest_manager.EndQuest();
if(lua_isnumber(L, -1)) {
int ret = static_cast<int>(lua_tointeger(L, -1));
lua_pop(L, npop);
return ret;
}
lua_pop(L, npop);
} catch(std::exception &ex) {
std::string error = "Lua Exception: ";
error += std::string(ex.what());
AddError(error);
//Restore our stack to the best of our ability
int end = lua_gettop(L);
int n = end - start;
if(n > 0) {
lua_pop(L, n);
}
}
return 0;
}
int LuaParser::EventSpell(QuestEventID evt, Mob* mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
std::string package_name = "spell_" + std::to_string(spell_id);
if(!SpellHasQuestSub(spell_id, evt)) {
return 0;
}
return _EventSpell(package_name, evt, mob, client, spell_id, data, extra_data, extra_pointers);
}
int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, Mob* mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers, luabind::adl::object *l_func) {
const char *sub_name = LuaEvents[evt];
int start = lua_gettop(L);
try {
int npop = 1;
if(l_func != nullptr) {
l_func->push(L);
} else {
lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_getfield(L, -1, sub_name);
npop = 2;
}
lua_createtable(L, 0, 0);
//always push self even if invalid
if(IsValidSpell(spell_id)) {
Lua_Spell l_spell(&spells[spell_id]);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
} else {
Lua_Spell l_spell(nullptr);
luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell);
l_spell_o.push(L);
}
lua_setfield(L, -2, "self");
auto arg_function = SpellArgumentDispatch[evt];
arg_function(this, L, mob, client, spell_id, data, extra_data, extra_pointers);
quest_manager.StartQuest(mob, client, nullptr, const_cast<SPDat_Spell_Struct*>(&spells[spell_id]));
if(lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
AddError(error);
quest_manager.EndQuest();
lua_pop(L, npop);
return 0;
}
quest_manager.EndQuest();
if(lua_isnumber(L, -1)) {
int ret = static_cast<int>(lua_tointeger(L, -1));
lua_pop(L, npop);
return ret;
}
lua_pop(L, npop);
} catch(std::exception &ex) {
std::string error = "Lua Exception: ";
error += std::string(ex.what());
AddError(error);
//Restore our stack to the best of our ability
int end = lua_gettop(L);
int n = end - start;
if(n > 0) {
lua_pop(L, n);
}
}
return 0;
}
int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, std::string data, uint32 extra_data, std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
std::string package_name = "encounter_" + encounter_name;
if(!EncounterHasQuestSub(encounter_name, evt)) {
return 0;
}
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, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
const char *sub_name = LuaEvents[evt];
int start = lua_gettop(L);
try {
lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_getfield(L, -1, sub_name);
lua_createtable(L, 0, 0);
lua_pushstring(L, encounter_name.c_str());
lua_setfield(L, -2, "name");
Encounter *enc = lua_encounters[encounter_name];
auto arg_function = EncounterArgumentDispatch[evt];
arg_function(this, L, enc, data, extra_data, extra_pointers);
quest_manager.StartQuest(enc, nullptr, nullptr, nullptr, encounter_name);
if(lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
AddError(error);
quest_manager.EndQuest();
lua_pop(L, 2);
return 0;
}
quest_manager.EndQuest();
if(lua_isnumber(L, -1)) {
int ret = static_cast<int>(lua_tointeger(L, -1));
lua_pop(L, 2);
return ret;
}
lua_pop(L, 2);
} catch(std::exception &ex) {
std::string error = "Lua Exception: ";
error += std::string(ex.what());
AddError(error);
//Restore our stack to the best of our ability
int end = lua_gettop(L);
int n = end - start;
if(n > 0) {
lua_pop(L, n);
}
}
return 0;
}
bool LuaParser::HasQuestSub(uint32 npc_id, QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return false;
}
std::string package_name = "npc_" + std::to_string(npc_id);
const char *subname = LuaEvents[evt];
return HasFunction(subname, package_name);
}
bool LuaParser::HasGlobalQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "global_npc");
}
bool LuaParser::PlayerHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "player");
}
bool LuaParser::GlobalPlayerHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "global_player");
}
bool LuaParser::SpellHasQuestSub(uint32 spell_id, QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return false;
}
std::string package_name = "spell_" + std::to_string(spell_id);
const char *subname = LuaEvents[evt];
return HasFunction(subname, package_name);
}
bool LuaParser::ItemHasQuestSub(EQ::ItemInstance *itm, QuestEventID evt) {
if (itm == nullptr) {
return false;
}
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return false;
}
std::string package_name = "item_";
package_name += std::to_string(itm->GetID());
const char *subname = LuaEvents[evt];
return HasFunction(subname, package_name);
}
bool LuaParser::EncounterHasQuestSub(std::string encounter_name, QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return false;
}
std::string package_name = "encounter_" + encounter_name;
const char *subname = LuaEvents[evt];
return HasFunction(subname, package_name);
}
void LuaParser::LoadNPCScript(std::string filename, int npc_id) {
std::string package_name = "npc_" + std::to_string(npc_id);
LoadScript(filename, package_name);
}
void LuaParser::LoadGlobalNPCScript(std::string filename) {
LoadScript(filename, "global_npc");
}
void LuaParser::LoadPlayerScript(std::string filename) {
LoadScript(filename, "player");
}
void LuaParser::LoadGlobalPlayerScript(std::string filename) {
LoadScript(filename, "global_player");
}
void LuaParser::LoadItemScript(std::string filename, EQ::ItemInstance *item) {
if (item == nullptr)
return;
std::string package_name = "item_";
package_name += std::to_string(item->GetID());
LoadScript(filename, package_name);
}
void LuaParser::LoadSpellScript(std::string filename, uint32 spell_id) {
std::string package_name = "spell_" + std::to_string(spell_id);
LoadScript(filename, package_name);
}
void LuaParser::LoadEncounterScript(std::string filename, std::string encounter_name) {
std::string package_name = "encounter_" + encounter_name;
LoadScript(filename, package_name);
}
void LuaParser::AddVar(std::string name, std::string val) {
vars_[name] = val;
}
std::string LuaParser::GetVar(std::string name) {
auto iter = vars_.find(name);
if(iter != vars_.end()) {
return iter->second;
}
return std::string();
}
void LuaParser::Init() {
ReloadQuests();
}
void LuaParser::ReloadQuests() {
loaded_.clear();
errors_.clear();
mods_.clear();
lua_encounter_events_registered.clear();
lua_encounters_loaded.clear();
for (auto encounter : lua_encounters) {
encounter.second->Depop();
}
lua_encounters.clear();
// so the Depop function above depends on the Process being called again so ...
// And there is situations where it wouldn't be :P
entity_list.EncounterProcess();
if(L) {
lua_close(L);
}
L = luaL_newstate();
luaL_openlibs(L);
auto top = lua_gettop(L);
if(luaopen_bit(L) != 1) {
std::string error = lua_tostring(L, -1);
AddError(error);
}
if(luaL_dostring(L, "math.randomseed(os.time())")) {
std::string error = lua_tostring(L, -1);
AddError(error);
}
#ifdef SANITIZE_LUA_LIBS
//io
lua_pushnil(L);
//lua_setglobal(L, "io");
//some os/debug are okay some are not
lua_getglobal(L, "os");
lua_pushnil(L);
lua_setfield(L, -2, "exit");
lua_pushnil(L);
lua_setfield(L, -2, "execute");
lua_pushnil(L);
lua_setfield(L, -2, "getenv");
lua_pushnil(L);
lua_setfield(L, -2, "remove");
lua_pushnil(L);
lua_setfield(L, -2, "rename");
lua_pushnil(L);
lua_setfield(L, -2, "setlocale");
lua_pushnil(L);
lua_setfield(L, -2, "tmpname");
lua_pop(L, 1);
lua_pushnil(L);
lua_setglobal(L, "collectgarbage");
lua_pushnil(L);
lua_setglobal(L, "loadfile");
#endif
// lua 5.2+ defines these
#if defined(LUA_VERSION_MAJOR) && defined(LUA_VERSION_MINOR)
const char lua_version[] = LUA_VERSION_MAJOR "." LUA_VERSION_MINOR;
#elif LUA_VERSION_NUM == 501
const char lua_version[] = "5.1";
#else
#error Incompatible lua version
#endif
#ifdef _WINDOWS
const char libext[] = ".dll";
#else
// lua doesn't care OSX doesn't use sonames
const char libext[] = ".so";
#endif
lua_getglobal(L, "package");
lua_getfield(L, -1, "path");
std::string module_path = lua_tostring(L,-1);
module_path += ";" + path.GetLuaModulesPath() + "/?.lua;" + path.GetLuaModulesPath() + "/?/init.lua";
// luarock paths using lua_modules as tree
// to path it adds foo/share/lua/5.1/?.lua and foo/share/lua/5.1/?/init.lua
module_path += ";" + path.GetLuaModulesPath() + "/share/lua/" + lua_version + "/?.lua";
module_path += ";" + path.GetLuaModulesPath() + "/share/lua/" + lua_version + "/?/init.lua";
lua_pop(L, 1);
lua_pushstring(L, module_path.c_str());
lua_setfield(L, -2, "path");
lua_pop(L, 1);
lua_getglobal(L, "package");
lua_getfield(L, -1, "cpath");
module_path = lua_tostring(L, -1);
module_path += ";" + path.GetLuaModulesPath() + "/?" + libext;
// luarock paths using lua_modules as tree
// luarocks adds foo/lib/lua/5.1/?.so for cpath
module_path += ";" + path.GetLuaModulesPath() + "/lib/lua/" + lua_version + "/?" + libext;
lua_pop(L, 1);
lua_pushstring(L, module_path.c_str());
lua_setfield(L, -2, "cpath");
lua_pop(L, 1);
MapFunctions(L);
// load init
std::string filename = fmt::format("{}/{}/script_init.lua", path.GetQuestsPath(), QUEST_GLOBAL_DIRECTORY);
FILE *f = fopen(filename.c_str(), "r");
if(f) {
fclose(f);
if(luaL_dofile(L, filename.c_str())) {
std::string error = lua_tostring(L, -1);
AddError(error);
}
}
//zone init - always loads after global
if(zone) {
std::string zone_script = fmt::format(
"{}/{}/script_init_v{}.lua",
path.GetQuestsPath(),
zone->GetShortName(),
zone->GetInstanceVersion()
);
f = fopen(zone_script.c_str(), "r");
if(f) {
fclose(f);
if(luaL_dofile(L, zone_script.c_str())) {
std::string error = lua_tostring(L, -1);
AddError(error);
}
}
else {
zone_script = fmt::format("{}/{}/script_init.lua", path.GetQuestsPath(), zone->GetShortName());
f = fopen(zone_script.c_str(), "r");
if (f) {
fclose(f);
if (luaL_dofile(L, zone_script.c_str())) {
std::string error = lua_tostring(L, -1);
AddError(error);
}
}
}
}
FILE *load_order = fopen(fmt::format("{}/load_order.txt", path.GetLuaModsPath()).c_str(), "r");
if (load_order) {
char file_name[256] = { 0 };
while (fgets(file_name, 256, load_order) != nullptr) {
for (char & i : file_name) {
auto c = i;
if (c == '\n' || c == '\r' || c == ' ') {
i = 0;
break;
}
}
LoadScript(fmt::format("{}/{}", path.GetLuaModsPath(), std::string(file_name)), file_name);
mods_.emplace_back(L, this, file_name);
}
fclose(load_order);
}
auto end = lua_gettop(L);
int n = end - top;
if (n > 0) {
lua_pop(L, n);
}
}
/*
* This function is intended only to clean up lua_encounters when the Encounter object is
* about to be destroyed. It won't clean up memory else where, since the caller of this
* function is responsible for that
*/
void LuaParser::RemoveEncounter(const std::string &name)
{
lua_encounters.erase(name);
}
void LuaParser::LoadScript(std::string filename, std::string package_name) {
auto iter = loaded_.find(package_name);
if(iter != loaded_.end()) {
return;
}
auto top = lua_gettop(L);
if(luaL_loadfile(L, filename.c_str())) {
std::string error = lua_tostring(L, -1);
AddError(error);
lua_pop(L, 1);
return;
}
//This makes an env table named: package_name
//And makes it so we can see the global table _G from it
//Then sets it so this script is called from that table as an env
lua_createtable(L, 0, 0); // anon table
lua_getglobal(L, "_G"); // get _G
lua_setfield(L, -2, "__index"); //anon table.__index = _G
lua_pushvalue(L, -1); //copy table to top of stack
lua_setmetatable(L, -2); //setmetatable(anon_table, copied table)
lua_pushvalue(L, -1); //put the table we made into the registry
lua_setfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_setfenv(L, -2); //set the env to the table we made
if(lua_pcall(L, 0, 0, 0)) {
std::string error = lua_tostring(L, -1);
AddError(error);
lua_pop(L, 1);
}
else {
loaded_[package_name] = true;
}
auto end = lua_gettop(L);
int n = end - top;
if (n > 0) {
lua_pop(L, n);
}
}
bool LuaParser::HasFunction(std::string subname, std::string package_name) {
//std::transform(subname.begin(), subname.end(), subname.begin(), ::tolower);
auto iter = loaded_.find(package_name);
if(iter == loaded_.end()) {
return false;
}
lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_getfield(L, -1, subname.c_str());
if(lua_isfunction(L, -1)) {
lua_pop(L, 2);
return true;
}
lua_pop(L, 2);
return false;
}
bool LuaParser::HasEncounterSub(const std::string& package_name, QuestEventID evt)
{
auto it = lua_encounter_events_registered.find(package_name);
if (it != lua_encounter_events_registered.end()) {
for (auto & riter : it->second) {
if (riter.event_id == evt) {
return true;
}
}
}
return false;
}
void LuaParser::MapFunctions(lua_State *L) {
try {
luabind::open(L);
luabind::module(L)
[(
lua_register_general(),
lua_register_random(),
lua_register_events(),
lua_register_faction(),
lua_register_slot(),
lua_register_material(),
lua_register_client_version(),
lua_register_appearance(),
lua_register_classes(),
lua_register_skills(),
lua_register_bodytypes(),
lua_register_filters(),
lua_register_message_types(),
lua_register_entity(),
lua_register_encounter(),
lua_register_mob(),
lua_register_special_abilities(),
lua_register_npc(),
lua_register_client(),
#ifdef BOTS
lua_register_bot(),
#endif
lua_register_inventory(),
lua_register_inventory_where(),
lua_register_iteminst(),
lua_register_item(),
lua_register_spell(),
lua_register_spawn(),
lua_register_hate_entry(),
lua_register_hate_list(),
lua_register_entity_list(),
lua_register_mob_list(),
lua_register_client_list(),
#ifdef BOTS
lua_register_bot_list(),
#endif
lua_register_npc_list(),
lua_register_corpse_list(),
lua_register_object_list(),
lua_register_door_list(),
lua_register_spawn_list(),
lua_register_corpse_loot_list(),
lua_register_npc_loot_list(),
lua_register_group(),
lua_register_raid(),
lua_register_corpse(),
lua_register_door(),
lua_register_object(),
lua_register_packet(),
lua_register_packet_opcodes(),
lua_register_stat_bonuses(),
lua_register_rules_const(),
lua_register_rulei(),
lua_register_ruler(),
lua_register_ruleb(),
lua_register_journal_speakmode(),
lua_register_journal_mode(),
lua_register_expedition(),
lua_register_expedition_lock_messages()
)];
} catch(std::exception &ex) {
std::string error = ex.what();
AddError(error);
}
}
int LuaParser::DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
if(!npc)
return 0;
std::string package_name = "npc_" + std::to_string(npc->GetNPCTypeID());
int ret = 0;
auto iter = lua_encounter_events_registered.find(package_name);
if(iter != lua_encounter_events_registered.end()) {
auto riter = iter->second.begin();
while(riter != iter->second.end()) {
if(riter->event_id == evt) {
std::string package_name = "encounter_" + riter->encounter_name;
int i = _EventNPC(package_name, evt, npc, init, data, extra_data, extra_pointers, &riter->lua_reference);
if(i != 0)
ret = i;
}
++riter;
}
}
iter = lua_encounter_events_registered.find("npc_-1");
if(iter == lua_encounter_events_registered.end()) {
return ret;
}
auto riter = iter->second.begin();
while(riter != iter->second.end()) {
if(riter->event_id == evt) {
std::string package_name = "encounter_" + riter->encounter_name;
int i = _EventNPC(package_name, evt, npc, init, data, extra_data, extra_pointers, &riter->lua_reference);
if(i != 0)
ret = i;
}
++riter;
}
return ret;
}
int LuaParser::DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
std::string package_name = "player";
auto iter = lua_encounter_events_registered.find(package_name);
if(iter == lua_encounter_events_registered.end()) {
return 0;
}
int ret = 0;
auto riter = iter->second.begin();
while(riter != iter->second.end()) {
if(riter->event_id == evt) {
std::string package_name = "encounter_" + riter->encounter_name;
int i = _EventPlayer(package_name, evt, client, data, extra_data, extra_pointers, &riter->lua_reference);
if(i != 0)
ret = i;
}
++riter;
}
return ret;
}
int LuaParser::DispatchEventItem(QuestEventID evt, Client *client, EQ::ItemInstance *item, Mob *mob, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
if(!item)
return 0;
std::string package_name = "item_";
package_name += std::to_string(item->GetID());
int ret = 0;
auto iter = lua_encounter_events_registered.find(package_name);
if(iter != lua_encounter_events_registered.end()) {
auto riter = iter->second.begin();
while(riter != iter->second.end()) {
if(riter->event_id == evt) {
std::string package_name = "encounter_" + riter->encounter_name;
int i = _EventItem(package_name, evt, client, item, mob, data, extra_data, extra_pointers, &riter->lua_reference);
if(i != 0)
ret = i;
}
++riter;
}
}
iter = lua_encounter_events_registered.find("item_-1");
if(iter == lua_encounter_events_registered.end()) {
return ret;
}
auto riter = iter->second.begin();
while(riter != iter->second.end()) {
if(riter->event_id == evt) {
std::string package_name = "encounter_" + riter->encounter_name;
int i = _EventItem(package_name, evt, client, item, mob, data, extra_data, extra_pointers, &riter->lua_reference);
if(i != 0)
ret = i;
}
++riter;
}
return ret;
}
int LuaParser::DispatchEventSpell(QuestEventID evt, Mob* mob, Client *client, uint32 spell_id, std::string data, uint32 extra_data,
std::vector<std::any> *extra_pointers) {
evt = ConvertLuaEvent(evt);
if(evt >= _LargestEventID) {
return 0;
}
std::string package_name = "spell_" + std::to_string(spell_id);
int ret = 0;
auto iter = lua_encounter_events_registered.find(package_name);
if(iter != lua_encounter_events_registered.end()) {
auto riter = iter->second.begin();
while(riter != iter->second.end()) {
if(riter->event_id == evt) {
std::string package_name = "encounter_" + riter->encounter_name;
int i = _EventSpell(package_name, evt, mob, client, spell_id, data, extra_data, extra_pointers, &riter->lua_reference);
if(i != 0) {
ret = i;
}
}
++riter;
}
}
iter = lua_encounter_events_registered.find("spell_-1");
if(iter == lua_encounter_events_registered.end()) {
return ret;
}
auto riter = iter->second.begin();
while(riter != iter->second.end()) {
if(riter->event_id == evt) {
std::string package_name = "encounter_" + riter->encounter_name;
int i = _EventSpell(package_name, evt, mob, client, spell_id, data, extra_data, extra_pointers, &riter->lua_reference);
if(i != 0)
ret = i;
}
++riter;
}
return ret;
}
QuestEventID LuaParser::ConvertLuaEvent(QuestEventID evt) {
switch(evt) {
case EVENT_SLAY:
case EVENT_NPC_SLAY:
return EVENT_SLAY;
break;
#ifdef BOTS
case EVENT_SPELL_EFFECT_BOT:
#endif
case EVENT_SPELL_EFFECT_CLIENT:
case EVENT_SPELL_EFFECT_NPC:
return EVENT_SPELL_EFFECT_CLIENT;
break;
#ifdef BOTS
case EVENT_SPELL_EFFECT_BUFF_TIC_BOT:
#endif
case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT:
case EVENT_SPELL_EFFECT_BUFF_TIC_NPC:
return EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT;
break;
case EVENT_AGGRO:
case EVENT_ATTACK:
return _LargestEventID;
break;
default:
return evt;
}
}
void LuaParser::MeleeMitigation(Mob *self, Mob *attacker, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault)
{
for (auto &mod : mods_) {
mod.MeleeMitigation(self, attacker, hit, opts, ignoreDefault);
}
}
void LuaParser::ApplyDamageTable(Mob *self, DamageHitInfo &hit, bool &ignoreDefault)
{
for (auto &mod : mods_) {
mod.ApplyDamageTable(self, hit, ignoreDefault);
}
}
bool LuaParser::AvoidDamage(Mob *self, Mob *other, DamageHitInfo &hit, bool & ignoreDefault)
{
bool retval = false;
for (auto &mod : mods_) {
mod.AvoidDamage(self, other, hit, retval, ignoreDefault);
}
return retval;
}
bool LuaParser::CheckHitChance(Mob *self, Mob *other, DamageHitInfo &hit, bool &ignoreDefault)
{
bool retval = false;
for (auto &mod : mods_) {
mod.CheckHitChance(self, other, hit, retval, ignoreDefault);
}
return retval;
}
void LuaParser::TryCriticalHit(Mob *self, Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault)
{
for (auto &mod : mods_) {
mod.TryCriticalHit(self, defender, hit, opts, ignoreDefault);
}
}
void LuaParser::CommonOutgoingHitSuccess(Mob *self, Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool &ignoreDefault)
{
for (auto &mod : mods_) {
mod.CommonOutgoingHitSuccess(self, other, hit, opts, ignoreDefault);
}
}
uint32 LuaParser::GetRequiredAAExperience(Client *self, bool &ignoreDefault)
{
uint32 retval = 0;
for (auto &mod : mods_) {
mod.GetRequiredAAExperience(self, retval, ignoreDefault);
}
return retval;
}
uint32 LuaParser::GetEXPForLevel(Client *self, uint16 level, bool &ignoreDefault)
{
uint32 retval = 0;
for (auto &mod : mods_) {
mod.GetEXPForLevel(self, level, retval, ignoreDefault);
}
return retval;
}
uint64 LuaParser::GetExperienceForKill(Client *self, Mob *against, bool &ignoreDefault)
{
uint64 retval = 0;
for (auto &mod : mods_) {
mod.GetExperienceForKill(self, against, retval, ignoreDefault);
}
return retval;
}
#ifdef BOTS
int LuaParser::EventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
if (!bot) {
return 0;
}
if (!BotHasQuestSub(evt)) {
return 0;
}
return _EventBot("bot", evt, bot, init, data, extra_data, extra_pointers);
}
int LuaParser::EventGlobalBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
if (!bot) {
return 0;
}
if (!GlobalBotHasQuestSub(evt)) {
return 0;
}
return _EventBot("global_bot", evt, bot, init, data, extra_data, extra_pointers);
}
int LuaParser::_EventBot(
std::string package_name,
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers,
luabind::adl::object *l_func
) {
const char *sub_name = LuaEvents[evt];
int start = lua_gettop(L);
try {
int npop = 1;
if(l_func != nullptr) {
l_func->push(L);
} else {
lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str());
lua_getfield(L, -1, sub_name);
npop = 2;
}
lua_createtable(L, 0, 0);
//push self
Lua_Bot l_bot(bot);
luabind::adl::object l_bot_o = luabind::adl::object(L, l_bot);
l_bot_o.push(L);
lua_setfield(L, -2, "self");
auto arg_function = BotArgumentDispatch[evt];
arg_function(this, L, bot, init, data, extra_data, extra_pointers);
auto* c = (init && init->IsClient()) ? init->CastToClient() : nullptr;
quest_manager.StartQuest(bot, c);
if(lua_pcall(L, 1, 1, 0)) {
std::string error = lua_tostring(L, -1);
AddError(error);
quest_manager.EndQuest();
lua_pop(L, npop);
return 0;
}
quest_manager.EndQuest();
if(lua_isnumber(L, -1)) {
int ret = static_cast<int>(lua_tointeger(L, -1));
lua_pop(L, npop);
return ret;
}
lua_pop(L, npop);
} catch(std::exception &ex) {
std::string error = "Lua Exception: ";
error += std::string(ex.what());
AddError(error);
//Restore our stack to the best of our ability
int end = lua_gettop(L);
int n = end - start;
if(n > 0) {
lua_pop(L, n);
}
}
return 0;
}
int LuaParser::DispatchEventBot(
QuestEventID evt,
Bot *bot,
Mob *init,
std::string data,
uint32 extra_data,
std::vector<std::any> *extra_pointers
) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return 0;
}
std::string package_name = "bot";
auto iter = lua_encounter_events_registered.find(package_name);
if (iter == lua_encounter_events_registered.end()) {
return 0;
}
int ret = 0;
auto riter = iter->second.begin();
while (riter != iter->second.end()) {
if (riter->event_id == evt) {
package_name = fmt::format("encounter_{}", riter->encounter_name);
int i = _EventBot(package_name, evt, bot, init, data, extra_data, extra_pointers, &riter->lua_reference);
if (i != 0) {
ret = i;
}
}
++riter;
}
return ret;
}
bool LuaParser::BotHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "bot");
}
bool LuaParser::GlobalBotHasQuestSub(QuestEventID evt) {
evt = ConvertLuaEvent(evt);
if (evt >= _LargestEventID) {
return false;
}
const char *subname = LuaEvents[evt];
return HasFunction(subname, "global_bot");
}
void LuaParser::LoadBotScript(std::string filename) {
LoadScript(filename, "bot");
}
void LuaParser::LoadGlobalBotScript(std::string filename) {
LoadScript(filename, "global_bot");
}
#endif
#endif