mirror of
https://github.com/ErikasKontenis/SabrehavenServer.git
synced 2025-05-01 18:19:20 +02:00
524 lines
15 KiB
C++
524 lines
15 KiB
C++
/**
|
|
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
|
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
|
*
|
|
* 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 "creatureevent.h"
|
|
#include "tools.h"
|
|
#include "player.h"
|
|
|
|
CreatureEvents::CreatureEvents() :
|
|
scriptInterface("CreatureScript Interface")
|
|
{
|
|
scriptInterface.initState();
|
|
}
|
|
|
|
CreatureEvents::~CreatureEvents()
|
|
{
|
|
for (const auto& it : creatureEvents) {
|
|
delete it.second;
|
|
}
|
|
}
|
|
|
|
void CreatureEvents::clear()
|
|
{
|
|
//clear creature events
|
|
for (const auto& it : creatureEvents) {
|
|
it.second->clearEvent();
|
|
}
|
|
|
|
//clear lua state
|
|
scriptInterface.reInitState();
|
|
}
|
|
|
|
LuaScriptInterface& CreatureEvents::getScriptInterface()
|
|
{
|
|
return scriptInterface;
|
|
}
|
|
|
|
std::string CreatureEvents::getScriptBaseName() const
|
|
{
|
|
return "creaturescripts";
|
|
}
|
|
|
|
Event* CreatureEvents::getEvent(const std::string& nodeName)
|
|
{
|
|
if (strcasecmp(nodeName.c_str(), "event") != 0) {
|
|
return nullptr;
|
|
}
|
|
return new CreatureEvent(&scriptInterface);
|
|
}
|
|
|
|
bool CreatureEvents::registerEvent(Event* event, const pugi::xml_node&)
|
|
{
|
|
CreatureEvent* creatureEvent = static_cast<CreatureEvent*>(event); //event is guaranteed to be a CreatureEvent
|
|
if (creatureEvent->getEventType() == CREATURE_EVENT_NONE) {
|
|
std::cout << "Error: [CreatureEvents::registerEvent] Trying to register event without type!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
CreatureEvent* oldEvent = getEventByName(creatureEvent->getName(), false);
|
|
if (oldEvent) {
|
|
//if there was an event with the same that is not loaded
|
|
//(happens when realoading), it is reused
|
|
if (!oldEvent->isLoaded() && oldEvent->getEventType() == creatureEvent->getEventType()) {
|
|
oldEvent->copyEvent(creatureEvent);
|
|
}
|
|
|
|
return false;
|
|
} else {
|
|
//if not, register it normally
|
|
creatureEvents[creatureEvent->getName()] = creatureEvent;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
CreatureEvent* CreatureEvents::getEventByName(const std::string& name, bool forceLoaded /*= true*/)
|
|
{
|
|
auto it = creatureEvents.find(name);
|
|
if (it != creatureEvents.end()) {
|
|
if (!forceLoaded || it->second->isLoaded()) {
|
|
return it->second;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool CreatureEvents::playerLogin(Player* player) const
|
|
{
|
|
//fire global event if is registered
|
|
for (const auto& it : creatureEvents) {
|
|
if (it.second->getEventType() == CREATURE_EVENT_LOGIN) {
|
|
if (!it.second->executeOnLogin(player)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CreatureEvents::playerLogout(Player* player) const
|
|
{
|
|
//fire global event if is registered
|
|
for (const auto& it : creatureEvents) {
|
|
if (it.second->getEventType() == CREATURE_EVENT_LOGOUT) {
|
|
if (!it.second->executeOnLogout(player)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CreatureEvents::playerAdvance(Player* player, skills_t skill, uint32_t oldLevel,
|
|
uint32_t newLevel)
|
|
{
|
|
for (const auto& it : creatureEvents) {
|
|
if (it.second->getEventType() == CREATURE_EVENT_ADVANCE) {
|
|
if (!it.second->executeAdvance(player, skill, oldLevel, newLevel)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/////////////////////////////////////
|
|
|
|
CreatureEvent::CreatureEvent(LuaScriptInterface* interface) :
|
|
Event(interface), type(CREATURE_EVENT_NONE), loaded(false) {}
|
|
|
|
bool CreatureEvent::configureEvent(const pugi::xml_node& node)
|
|
{
|
|
// Name that will be used in monster xml files and
|
|
// lua function to register events to reference this event
|
|
pugi::xml_attribute nameAttribute = node.attribute("name");
|
|
if (!nameAttribute) {
|
|
std::cout << "[Error - CreatureEvent::configureEvent] Missing name for creature event" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
eventName = nameAttribute.as_string();
|
|
|
|
pugi::xml_attribute typeAttribute = node.attribute("type");
|
|
if (!typeAttribute) {
|
|
std::cout << "[Error - CreatureEvent::configureEvent] Missing type for creature event: " << eventName << std::endl;
|
|
return false;
|
|
}
|
|
|
|
std::string tmpStr = asLowerCaseString(typeAttribute.as_string());
|
|
if (tmpStr == "login") {
|
|
type = CREATURE_EVENT_LOGIN;
|
|
} else if (tmpStr == "logout") {
|
|
type = CREATURE_EVENT_LOGOUT;
|
|
} else if (tmpStr == "think") {
|
|
type = CREATURE_EVENT_THINK;
|
|
} else if (tmpStr == "preparedeath") {
|
|
type = CREATURE_EVENT_PREPAREDEATH;
|
|
} else if (tmpStr == "death") {
|
|
type = CREATURE_EVENT_DEATH;
|
|
} else if (tmpStr == "kill") {
|
|
type = CREATURE_EVENT_KILL;
|
|
} else if (tmpStr == "advance") {
|
|
type = CREATURE_EVENT_ADVANCE;
|
|
} else if (tmpStr == "healthchange") {
|
|
type = CREATURE_EVENT_HEALTHCHANGE;
|
|
} else if (tmpStr == "manachange") {
|
|
type = CREATURE_EVENT_MANACHANGE;
|
|
} else if (tmpStr == "extendedopcode") {
|
|
type = CREATURE_EVENT_EXTENDED_OPCODE;
|
|
} else {
|
|
std::cout << "[Error - CreatureEvent::configureEvent] Invalid type for creature event: " << eventName << std::endl;
|
|
return false;
|
|
}
|
|
|
|
loaded = true;
|
|
return true;
|
|
}
|
|
|
|
std::string CreatureEvent::getScriptEventName() const
|
|
{
|
|
//Depending on the type script event name is different
|
|
switch (type) {
|
|
case CREATURE_EVENT_LOGIN:
|
|
return "onLogin";
|
|
|
|
case CREATURE_EVENT_LOGOUT:
|
|
return "onLogout";
|
|
|
|
case CREATURE_EVENT_THINK:
|
|
return "onThink";
|
|
|
|
case CREATURE_EVENT_PREPAREDEATH:
|
|
return "onPrepareDeath";
|
|
|
|
case CREATURE_EVENT_DEATH:
|
|
return "onDeath";
|
|
|
|
case CREATURE_EVENT_KILL:
|
|
return "onKill";
|
|
|
|
case CREATURE_EVENT_ADVANCE:
|
|
return "onAdvance";
|
|
|
|
case CREATURE_EVENT_HEALTHCHANGE:
|
|
return "onHealthChange";
|
|
|
|
case CREATURE_EVENT_MANACHANGE:
|
|
return "onManaChange";
|
|
|
|
case CREATURE_EVENT_EXTENDED_OPCODE:
|
|
return "onExtendedOpcode";
|
|
|
|
case CREATURE_EVENT_NONE:
|
|
default:
|
|
return std::string();
|
|
}
|
|
}
|
|
|
|
void CreatureEvent::copyEvent(CreatureEvent* creatureEvent)
|
|
{
|
|
scriptId = creatureEvent->scriptId;
|
|
scriptInterface = creatureEvent->scriptInterface;
|
|
scripted = creatureEvent->scripted;
|
|
loaded = creatureEvent->loaded;
|
|
}
|
|
|
|
void CreatureEvent::clearEvent()
|
|
{
|
|
scriptId = 0;
|
|
scriptInterface = nullptr;
|
|
scripted = false;
|
|
loaded = false;
|
|
}
|
|
|
|
bool CreatureEvent::executeOnLogin(Player* player)
|
|
{
|
|
//onLogin(player)
|
|
if (!scriptInterface->reserveScriptEnv()) {
|
|
std::cout << "[Error - CreatureEvent::executeOnLogin] Call stack overflow" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
|
env->setScriptId(scriptId, scriptInterface);
|
|
|
|
lua_State* L = scriptInterface->getLuaState();
|
|
|
|
scriptInterface->pushFunction(scriptId);
|
|
LuaScriptInterface::pushUserdata(L, player);
|
|
LuaScriptInterface::setMetatable(L, -1, "Player");
|
|
return scriptInterface->callFunction(1);
|
|
}
|
|
|
|
bool CreatureEvent::executeOnLogout(Player* player)
|
|
{
|
|
//onLogout(player)
|
|
if (!scriptInterface->reserveScriptEnv()) {
|
|
std::cout << "[Error - CreatureEvent::executeOnLogout] Call stack overflow" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
|
env->setScriptId(scriptId, scriptInterface);
|
|
|
|
lua_State* L = scriptInterface->getLuaState();
|
|
|
|
scriptInterface->pushFunction(scriptId);
|
|
LuaScriptInterface::pushUserdata(L, player);
|
|
LuaScriptInterface::setMetatable(L, -1, "Player");
|
|
return scriptInterface->callFunction(1);
|
|
}
|
|
|
|
bool CreatureEvent::executeOnThink(Creature* creature, uint32_t interval)
|
|
{
|
|
//onThink(creature, interval)
|
|
if (!scriptInterface->reserveScriptEnv()) {
|
|
std::cout << "[Error - CreatureEvent::executeOnThink] Call stack overflow" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
|
env->setScriptId(scriptId, scriptInterface);
|
|
|
|
lua_State* L = scriptInterface->getLuaState();
|
|
|
|
scriptInterface->pushFunction(scriptId);
|
|
LuaScriptInterface::pushUserdata<Creature>(L, creature);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
|
|
lua_pushnumber(L, interval);
|
|
|
|
return scriptInterface->callFunction(2);
|
|
}
|
|
|
|
bool CreatureEvent::executeOnPrepareDeath(Creature* creature, Creature* killer)
|
|
{
|
|
//onPrepareDeath(creature, killer)
|
|
if (!scriptInterface->reserveScriptEnv()) {
|
|
std::cout << "[Error - CreatureEvent::executeOnPrepareDeath] Call stack overflow" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
|
env->setScriptId(scriptId, scriptInterface);
|
|
|
|
lua_State* L = scriptInterface->getLuaState();
|
|
|
|
scriptInterface->pushFunction(scriptId);
|
|
|
|
LuaScriptInterface::pushUserdata<Creature>(L, creature);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
|
|
|
|
if (killer) {
|
|
LuaScriptInterface::pushUserdata<Creature>(L, killer);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, killer);
|
|
} else {
|
|
lua_pushnil(L);
|
|
}
|
|
|
|
return scriptInterface->callFunction(2);
|
|
}
|
|
|
|
bool CreatureEvent::executeOnDeath(Creature* creature, Item* corpse, Creature* killer, Creature* mostDamageKiller, bool lastHitUnjustified, bool mostDamageUnjustified)
|
|
{
|
|
//onDeath(creature, corpse, lasthitkiller, mostdamagekiller, lasthitunjustified, mostdamageunjustified)
|
|
if (!scriptInterface->reserveScriptEnv()) {
|
|
std::cout << "[Error - CreatureEvent::executeOnDeath] Call stack overflow" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
|
env->setScriptId(scriptId, scriptInterface);
|
|
|
|
lua_State* L = scriptInterface->getLuaState();
|
|
|
|
scriptInterface->pushFunction(scriptId);
|
|
LuaScriptInterface::pushUserdata<Creature>(L, creature);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
|
|
|
|
LuaScriptInterface::pushThing(L, corpse);
|
|
|
|
if (killer) {
|
|
LuaScriptInterface::pushUserdata<Creature>(L, killer);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, killer);
|
|
} else {
|
|
lua_pushnil(L);
|
|
}
|
|
|
|
if (mostDamageKiller) {
|
|
LuaScriptInterface::pushUserdata<Creature>(L, mostDamageKiller);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, mostDamageKiller);
|
|
} else {
|
|
lua_pushnil(L);
|
|
}
|
|
|
|
LuaScriptInterface::pushBoolean(L, lastHitUnjustified);
|
|
LuaScriptInterface::pushBoolean(L, mostDamageUnjustified);
|
|
|
|
return scriptInterface->callFunction(6);
|
|
}
|
|
|
|
bool CreatureEvent::executeAdvance(Player* player, skills_t skill, uint32_t oldLevel,
|
|
uint32_t newLevel)
|
|
{
|
|
//onAdvance(player, skill, oldLevel, newLevel)
|
|
if (!scriptInterface->reserveScriptEnv()) {
|
|
std::cout << "[Error - CreatureEvent::executeAdvance] Call stack overflow" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
|
env->setScriptId(scriptId, scriptInterface);
|
|
|
|
lua_State* L = scriptInterface->getLuaState();
|
|
|
|
scriptInterface->pushFunction(scriptId);
|
|
LuaScriptInterface::pushUserdata(L, player);
|
|
LuaScriptInterface::setMetatable(L, -1, "Player");
|
|
lua_pushnumber(L, static_cast<uint32_t>(skill));
|
|
lua_pushnumber(L, oldLevel);
|
|
lua_pushnumber(L, newLevel);
|
|
|
|
return scriptInterface->callFunction(4);
|
|
}
|
|
|
|
void CreatureEvent::executeOnKill(Creature* creature, Creature* target)
|
|
{
|
|
//onKill(creature, target)
|
|
if (!scriptInterface->reserveScriptEnv()) {
|
|
std::cout << "[Error - CreatureEvent::executeOnKill] Call stack overflow" << std::endl;
|
|
return;
|
|
}
|
|
|
|
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
|
env->setScriptId(scriptId, scriptInterface);
|
|
|
|
lua_State* L = scriptInterface->getLuaState();
|
|
|
|
scriptInterface->pushFunction(scriptId);
|
|
LuaScriptInterface::pushUserdata<Creature>(L, creature);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
|
|
LuaScriptInterface::pushUserdata<Creature>(L, target);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, target);
|
|
scriptInterface->callVoidFunction(2);
|
|
}
|
|
|
|
void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage)
|
|
{
|
|
//onHealthChange(creature, attacker, value, type, min, max, origin)
|
|
if (!scriptInterface->reserveScriptEnv()) {
|
|
std::cout << "[Error - CreatureEvent::executeHealthChange] Call stack overflow" << std::endl;
|
|
return;
|
|
}
|
|
|
|
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
|
env->setScriptId(scriptId, scriptInterface);
|
|
|
|
lua_State* L = scriptInterface->getLuaState();
|
|
scriptInterface->pushFunction(scriptId);
|
|
|
|
LuaScriptInterface::pushUserdata(L, creature);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
|
|
if (attacker) {
|
|
LuaScriptInterface::pushUserdata(L, attacker);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, attacker);
|
|
}
|
|
else {
|
|
lua_pushnil(L);
|
|
}
|
|
|
|
LuaScriptInterface::pushCombatDamage(L, damage);
|
|
|
|
if (scriptInterface->protectedCall(L, 7, 4) != 0) {
|
|
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
|
|
}
|
|
else {
|
|
damage.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -4));
|
|
damage.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
|
|
damage.min = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2));
|
|
damage.max = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
|
|
|
|
lua_pop(L, 4);
|
|
if (damage.type != COMBAT_HEALING) {
|
|
damage.value = -damage.value;
|
|
}
|
|
}
|
|
|
|
scriptInterface->resetScriptEnv();
|
|
}
|
|
|
|
void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage) {
|
|
//onManaChange(creature, attacker, value, type, min, max, origin)
|
|
if (!scriptInterface->reserveScriptEnv()) {
|
|
std::cout << "[Error - CreatureEvent::executeManaChange] Call stack overflow" << std::endl;
|
|
return;
|
|
}
|
|
|
|
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
|
env->setScriptId(scriptId, scriptInterface);
|
|
|
|
lua_State* L = scriptInterface->getLuaState();
|
|
scriptInterface->pushFunction(scriptId);
|
|
|
|
LuaScriptInterface::pushUserdata(L, creature);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
|
|
if (attacker) {
|
|
LuaScriptInterface::pushUserdata(L, attacker);
|
|
LuaScriptInterface::setCreatureMetatable(L, -1, attacker);
|
|
}
|
|
else {
|
|
lua_pushnil(L);
|
|
}
|
|
|
|
LuaScriptInterface::pushCombatDamage(L, damage);
|
|
|
|
if (scriptInterface->protectedCall(L, 7, 4) != 0) {
|
|
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
|
|
}
|
|
else {
|
|
damage = LuaScriptInterface::getCombatDamage(L);
|
|
}
|
|
|
|
scriptInterface->resetScriptEnv();
|
|
}
|
|
|
|
void CreatureEvent::executeExtendedOpcode(Player* player, uint8_t opcode, const std::string& buffer)
|
|
{
|
|
//onExtendedOpcode(player, opcode, buffer)
|
|
if (!scriptInterface->reserveScriptEnv()) {
|
|
std::cout << "[Error - CreatureEvent::executeExtendedOpcode] Call stack overflow" << std::endl;
|
|
return;
|
|
}
|
|
|
|
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
|
env->setScriptId(scriptId, scriptInterface);
|
|
|
|
lua_State* L = scriptInterface->getLuaState();
|
|
|
|
scriptInterface->pushFunction(scriptId);
|
|
|
|
LuaScriptInterface::pushUserdata<Player>(L, player);
|
|
LuaScriptInterface::setMetatable(L, -1, "Player");
|
|
|
|
lua_pushnumber(L, opcode);
|
|
LuaScriptInterface::pushString(L, buffer);
|
|
|
|
scriptInterface->callVoidFunction(3);
|
|
}
|