/** * The Forgotten Server - a free and open-source MMORPG server emulator * Copyright (C) 2016 Mark Samman * * 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; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "otpch.h" #include "events.h" #include "tools.h" #include "item.h" #include "player.h" #include Events::Events() : scriptInterface("Event Interface") { clear(); scriptInterface.initState(); } void Events::clear() { // Creature creatureOnChangeOutfit = -1; creatureOnAreaCombat = -1; creatureOnTargetCombat = -1; // Party partyOnJoin = -1; partyOnLeave = -1; partyOnDisband = -1; // Player playerOnLook = -1; playerOnLookInBattleList = -1; playerOnLookInTrade = -1; playerOnMoveItem = -1; playerOnItemMoved = -1; playerOnMoveCreature = -1; playerOnTurn = -1; playerOnTradeRequest = -1; playerOnTradeAccept = -1; playerOnGainExperience = -1; playerOnLoseExperience = -1; playerOnGainSkillTries = -1; playerOnReportBug = -1; } bool Events::load() { pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file("data/events/events.xml"); if (!result) { printXMLError("Error - Events::load", "data/events/events.xml", result); return false; } std::set classes; for (auto eventNode : doc.child("events").children()) { if (!eventNode.attribute("enabled").as_bool()) { continue; } const std::string& className = eventNode.attribute("class").as_string(); auto res = classes.insert(className); if (res.second) { const std::string& lowercase = asLowerCaseString(className); if (scriptInterface.loadFile("data/events/scripts/" + lowercase + ".lua") != 0) { std::cout << "[Warning - Events::load] Can not load script: " << lowercase << ".lua" << std::endl; std::cout << scriptInterface.getLastLuaError() << std::endl; } } const std::string& methodName = eventNode.attribute("method").as_string(); const int32_t event = scriptInterface.getMetaEvent(className, methodName); if (className == "Creature") { if (methodName == "onChangeOutfit") { creatureOnChangeOutfit = event; } else if (methodName == "onAreaCombat") { creatureOnAreaCombat = event; } else if (methodName == "onTargetCombat") { creatureOnTargetCombat = event; } else { std::cout << "[Warning - Events::load] Unknown creature method: " << methodName << std::endl; } } else if (className == "Party") { if (methodName == "onJoin") { partyOnJoin = event; } else if (methodName == "onLeave") { partyOnLeave = event; } else if (methodName == "onDisband") { partyOnDisband = event; } else if (methodName == "onShareExperience") { partyOnShareExperience = event; } else { std::cout << "[Warning - Events::load] Unknown party method: " << methodName << std::endl; } } else if (className == "Player") { if (methodName == "onLook") { playerOnLook = event; } else if (methodName == "onLookInBattleList") { playerOnLookInBattleList = event; } else if (methodName == "onLookInTrade") { playerOnLookInTrade = event; } else if (methodName == "onTradeRequest") { playerOnTradeRequest = event; } else if (methodName == "onTradeAccept") { playerOnTradeAccept = event; } else if (methodName == "onMoveItem") { playerOnMoveItem = event; } else if (methodName == "onItemMoved") { playerOnItemMoved = event; } else if (methodName == "onMoveCreature") { playerOnMoveCreature = event; } else if (methodName == "onReportBug") { playerOnReportBug = event; } else if (methodName == "onTurn") { playerOnTurn = event; } else if (methodName == "onGainExperience") { playerOnGainExperience = event; } else if (methodName == "onLoseExperience") { playerOnLoseExperience = event; } else if (methodName == "onGainSkillTries") { playerOnGainSkillTries = event; } else { std::cout << "[Warning - Events::load] Unknown player method: " << methodName << std::endl; } } else { std::cout << "[Warning - Events::load] Unknown class: " << className << std::endl; } } return true; } // Creature bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit) { // Creature:onChangeOutfit(outfit) or Creature.onChangeOutfit(self, outfit) if (creatureOnChangeOutfit == -1) { return true; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventCreatureOnChangeOutfit] Call stack overflow" << std::endl; return false; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(creatureOnChangeOutfit, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(creatureOnChangeOutfit); LuaScriptInterface::pushUserdata(L, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature); LuaScriptInterface::pushOutfit(L, outfit); return scriptInterface.callFunction(2); } ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive) { // Creature:onAreaCombat(tile, aggressive) or Creature.onAreaCombat(self, tile, aggressive) if (creatureOnAreaCombat == -1) { return RETURNVALUE_NOERROR; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventCreatureOnAreaCombat] Call stack overflow" << std::endl; return RETURNVALUE_NOTPOSSIBLE; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(creatureOnAreaCombat, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(creatureOnAreaCombat); if (creature) { LuaScriptInterface::pushUserdata(L, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature); } else { lua_pushnil(L); } LuaScriptInterface::pushUserdata(L, tile); LuaScriptInterface::setMetatable(L, -1, "Tile"); LuaScriptInterface::pushBoolean(L, aggressive); ReturnValue returnValue; if (scriptInterface.protectedCall(L, 3, 1) != 0) { returnValue = RETURNVALUE_NOTPOSSIBLE; LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); } else { returnValue = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } scriptInterface.resetScriptEnv(); return returnValue; } ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* target) { // Creature:onTargetCombat(target) or Creature.onTargetCombat(self, target) if (creatureOnTargetCombat == -1) { return RETURNVALUE_NOERROR; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventCreatureOnTargetCombat] Call stack overflow" << std::endl; return RETURNVALUE_NOTPOSSIBLE; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(creatureOnTargetCombat, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(creatureOnTargetCombat); if (creature) { LuaScriptInterface::pushUserdata(L, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature); } else { lua_pushnil(L); } LuaScriptInterface::pushUserdata(L, target); LuaScriptInterface::setCreatureMetatable(L, -1, target); ReturnValue returnValue; if (scriptInterface.protectedCall(L, 2, 1) != 0) { returnValue = RETURNVALUE_NOTPOSSIBLE; LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); } else { returnValue = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } scriptInterface.resetScriptEnv(); return returnValue; } // Party bool Events::eventPartyOnJoin(Party* party, Player* player) { // Party:onJoin(player) or Party.onJoin(self, player) if (partyOnJoin == -1) { return true; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPartyOnJoin] Call stack overflow" << std::endl; return false; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(partyOnJoin, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(partyOnJoin); LuaScriptInterface::pushUserdata(L, party); LuaScriptInterface::setMetatable(L, -1, "Party"); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); return scriptInterface.callFunction(2); } bool Events::eventPartyOnLeave(Party* party, Player* player) { // Party:onLeave(player) or Party.onLeave(self, player) if (partyOnLeave == -1) { return true; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPartyOnLeave] Call stack overflow" << std::endl; return false; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(partyOnLeave, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(partyOnLeave); LuaScriptInterface::pushUserdata(L, party); LuaScriptInterface::setMetatable(L, -1, "Party"); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); return scriptInterface.callFunction(2); } bool Events::eventPartyOnDisband(Party* party) { // Party:onDisband() or Party.onDisband(self) if (partyOnDisband == -1) { return true; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPartyOnDisband] Call stack overflow" << std::endl; return false; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(partyOnDisband, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(partyOnDisband); LuaScriptInterface::pushUserdata(L, party); LuaScriptInterface::setMetatable(L, -1, "Party"); return scriptInterface.callFunction(1); } void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp) { // Party:onShareExperience(exp) or Party.onShareExperience(self, exp) if (partyOnShareExperience == -1) { return; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPartyOnShareExperience] Call stack overflow" << std::endl; return; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(partyOnShareExperience, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(partyOnShareExperience); LuaScriptInterface::pushUserdata(L, party); LuaScriptInterface::setMetatable(L, -1, "Party"); lua_pushnumber(L, exp); if (scriptInterface.protectedCall(L, 2, 1) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); } else { exp = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } scriptInterface.resetScriptEnv(); } void Events::eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance) { // Player:onLook(thing, position, distance) or Player.onLook(self, thing, position, distance) if (playerOnLook == -1) { return; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnLook] Call stack overflow" << std::endl; return; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnLook, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnLook); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); if (Creature* creature = thing->getCreature()) { LuaScriptInterface::pushUserdata(L, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature); } else if (Item* item = thing->getItem()) { LuaScriptInterface::pushUserdata(L, item); LuaScriptInterface::setItemMetatable(L, -1, item); } else { lua_pushnil(L); } LuaScriptInterface::pushPosition(L, position, stackpos); lua_pushnumber(L, lookDistance); scriptInterface.callVoidFunction(4); } void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance) { // Player:onLookInBattleList(creature, position, distance) or Player.onLookInBattleList(self, creature, position, distance) if (playerOnLookInBattleList == -1) { return; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnLookInBattleList] Call stack overflow" << std::endl; return; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnLookInBattleList, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnLookInBattleList); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushUserdata(L, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature); lua_pushnumber(L, lookDistance); scriptInterface.callVoidFunction(3); } void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance) { // Player:onLookInTrade(partner, item, distance) or Player.onLookInTrade(self, partner, item, distance) if (playerOnLookInTrade == -1) { return; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnLookInTrade] Call stack overflow" << std::endl; return; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnLookInTrade, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnLookInTrade); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushUserdata(L, partner); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushUserdata(L, item); LuaScriptInterface::setItemMetatable(L, -1, item); lua_pushnumber(L, lookDistance); scriptInterface.callVoidFunction(4); } bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder) { // Player:onMoveItem(item, count, fromPosition, toPosition) or Player.onMoveItem(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder) if (playerOnMoveItem == -1) { return true; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnMoveItem] Call stack overflow" << std::endl; return false; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnMoveItem, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnMoveItem); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushUserdata(L, item); LuaScriptInterface::setItemMetatable(L, -1, item); lua_pushnumber(L, count); LuaScriptInterface::pushPosition(L, fromPosition); LuaScriptInterface::pushPosition(L, toPosition); LuaScriptInterface::pushCylinder(L, fromCylinder); LuaScriptInterface::pushCylinder(L, toCylinder); return scriptInterface.callFunction(7); } void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder) { // Player:onItemMoved(item, count, fromPosition, toPosition) or Player.onItemMoved(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder) if (playerOnItemMoved == -1) { return; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnItemMoved] Call stack overflow" << std::endl; return; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnItemMoved, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnItemMoved); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushUserdata(L, item); LuaScriptInterface::setItemMetatable(L, -1, item); lua_pushnumber(L, count); LuaScriptInterface::pushPosition(L, fromPosition); LuaScriptInterface::pushPosition(L, toPosition); LuaScriptInterface::pushCylinder(L, fromCylinder); LuaScriptInterface::pushCylinder(L, toCylinder); scriptInterface.callVoidFunction(7); } bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition) { // Player:onMoveCreature(creature, fromPosition, toPosition) or Player.onMoveCreature(self, creature, fromPosition, toPosition) if (playerOnMoveCreature == -1) { return true; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnMoveCreature] Call stack overflow" << std::endl; return false; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnMoveCreature, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnMoveCreature); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushUserdata(L, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature); LuaScriptInterface::pushPosition(L, fromPosition); LuaScriptInterface::pushPosition(L, toPosition); return scriptInterface.callFunction(4); } bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position) { // Player:onReportBug(message, position, category) if (playerOnReportBug == -1) { return true; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnReportBug] Call stack overflow" << std::endl; return false; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnReportBug, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnReportBug); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushString(L, message); LuaScriptInterface::pushPosition(L, position); return scriptInterface.callFunction(3); } bool Events::eventPlayerOnTurn(Player* player, Direction direction) { // Player:onTurn(direction) or Player.onTurn(self, direction) if (playerOnTurn == -1) { return true; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnTurn] Call stack overflow" << std::endl; return false; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnTurn, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnTurn); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); lua_pushnumber(L, direction); return scriptInterface.callFunction(2); } bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* item) { // Player:onTradeRequest(target, item) if (playerOnTradeRequest == -1) { return true; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnTradeRequest] Call stack overflow" << std::endl; return false; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnTradeRequest, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnTradeRequest); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushUserdata(L, target); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushUserdata(L, item); LuaScriptInterface::setItemMetatable(L, -1, item); return scriptInterface.callFunction(3); } bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem) { // Player:onTradeAccept(target, item, targetItem) if (playerOnTradeAccept == -1) { return true; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnTradeAccept] Call stack overflow" << std::endl; return false; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnTradeAccept, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnTradeAccept); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushUserdata(L, target); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushUserdata(L, item); LuaScriptInterface::setItemMetatable(L, -1, item); LuaScriptInterface::pushUserdata(L, targetItem); LuaScriptInterface::setItemMetatable(L, -1, targetItem); return scriptInterface.callFunction(4); } void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp) { // Player:onGainExperience(source, exp, rawExp) // rawExp gives the original exp which is not multiplied if (playerOnGainExperience == -1) { return; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnGainExperience] Call stack overflow" << std::endl; return; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnGainExperience, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnGainExperience); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); if (source) { LuaScriptInterface::pushUserdata(L, source); LuaScriptInterface::setCreatureMetatable(L, -1, source); } else { lua_pushnil(L); } lua_pushnumber(L, exp); lua_pushnumber(L, rawExp); if (scriptInterface.protectedCall(L, 4, 1) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); } else { exp = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } scriptInterface.resetScriptEnv(); } void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp) { // Player:onLoseExperience(exp) if (playerOnLoseExperience == -1) { return; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnLoseExperience] Call stack overflow" << std::endl; return; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnLoseExperience, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnLoseExperience); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); lua_pushnumber(L, exp); if (scriptInterface.protectedCall(L, 2, 1) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); } else { exp = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } scriptInterface.resetScriptEnv(); } void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries) { // Player:onGainSkillTries(skill, tries) if (playerOnGainSkillTries == -1) { return; } if (!scriptInterface.reserveScriptEnv()) { std::cout << "[Error - Events::eventPlayerOnGainSkillTries] Call stack overflow" << std::endl; return; } ScriptEnvironment* env = scriptInterface.getScriptEnv(); env->setScriptId(playerOnGainSkillTries, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); scriptInterface.pushFunction(playerOnGainSkillTries); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); lua_pushnumber(L, skill); lua_pushnumber(L, tries); if (scriptInterface.protectedCall(L, 3, 1) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); } else { tries = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } scriptInterface.resetScriptEnv(); }