From f6e7e0f4122c31aa0c03812373fd79b4dcaa194c Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Sat, 1 Aug 2015 14:17:18 -0300 Subject: [PATCH 01/10] Drop raids in C++ in favor of Lua global events - Move raids to globalevents, create Lua interface - Move force raid command to talkaction - Create example raid as globalevent - Add Lua interface to execute timed GlobalEvents Co-authored-by: Evil Puncker --- data/lib/compat/compat.lua | 4 +- data/raids/raids.xml | 7 - data/raids/testraid.xml | 21 - data/scripts/globalevents/#testraid.lua | 52 ++ data/scripts/globalevents/raids.lua | 37 ++ data/scripts/globalevents/raids_xml.lua | 241 ++++++++ data/scripts/lib/raids.lua | 43 ++ .../{force_raid.lua => force_event.lua} | 7 +- data/talkactions/scripts/reload.lua | 3 - data/talkactions/talkactions.xml | 2 +- src/CMakeLists.txt | 2 - src/const.h | 1 - src/enums.h | 2 - src/game.cpp | 9 - src/game.h | 2 - src/globalevent.cpp | 9 +- src/luascript.cpp | 31 +- src/luascript.h | 2 +- src/raids.cpp | 572 ------------------ src/raids.h | 188 ------ src/signals.cpp | 5 - src/tools.cpp | 6 - vc17/theforgottenserver.vcxproj | 4 +- 23 files changed, 401 insertions(+), 849 deletions(-) delete mode 100644 data/raids/raids.xml delete mode 100644 data/raids/testraid.xml create mode 100644 data/scripts/globalevents/#testraid.lua create mode 100644 data/scripts/globalevents/raids.lua create mode 100644 data/scripts/globalevents/raids_xml.lua create mode 100644 data/scripts/lib/raids.lua rename data/talkactions/scripts/{force_raid.lua => force_event.lua} (74%) delete mode 100644 src/raids.cpp delete mode 100644 src/raids.h diff --git a/data/lib/compat/compat.lua b/data/lib/compat/compat.lua index 2fded53faa..9caf9e5b5c 100644 --- a/data/lib/compat/compat.lua +++ b/data/lib/compat/compat.lua @@ -1333,8 +1333,10 @@ function doSetGameState(state) end function doExecuteRaid(raidName) - return Game.startRaid(raidName) + debugPrint("Deprecated function, use Game.startEvent('" .. raidName .. "') instead.") + return Game.startEvent(raidName) end +Game.startRaid = doExecuteRaid function Game.convertIpToString(ip) print("[Warning - " .. debug.getinfo(2).source:match("@?(.*)") .. "] Function Game.convertIpToString is deprecated and will be removed in the future. Use the return value of player:getIp() instead.") diff --git a/data/raids/raids.xml b/data/raids/raids.xml deleted file mode 100644 index e61a5a6e49..0000000000 --- a/data/raids/raids.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/data/raids/testraid.xml b/data/raids/testraid.xml deleted file mode 100644 index ebfe9d0c9c..0000000000 --- a/data/raids/testraid.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/data/scripts/globalevents/#testraid.lua b/data/scripts/globalevents/#testraid.lua new file mode 100644 index 0000000000..bbcd159564 --- /dev/null +++ b/data/scripts/globalevents/#testraid.lua @@ -0,0 +1,52 @@ +local raid = GlobalEvent("Testraid") +raid:type("timer") +raid:interval(1800) + +local function event0() + Game.broadcastMessage("Rats are attacking near Trekolt Temple!", MESSAGE_STATUS_WARNING) +end + +local function event1() + local center, radius, z = {x=94, y=126}, 5, 7 + for _ = 1, 3 do + local x, y = math.random(center.x - radius, center.x + radius), math.random(center.y - radius, center.y + radius) + Game.createMonster("Rat", Position(x, y, z)) + end +end + +local function event2() + Game.createMonster("Cave Rat", Position(93, 123, 7)) +end + +local function event3() + Game.broadcastMessage("Rats attack continues!", MESSAGE_STATUS_WARNING) +end + +local function event4() + local from, to, z = {x=89, y=122}, {x=99, y=130}, 7 + for _ = 1, math.random(4, 10) do + local x, y = math.random(from.x, to.x), math.random(from.y, to.y) + Game.createMonster("Rat", Position(x, y, z)) + end +end + +local function event5() + Game.createMonster("Cave Rat", Position(98, 125, 7)) +end + +local function event6() + Game.createMonster("Cave Rat", Position(94, 128, 7)) +end + +function raid.onTime(interval) + addEvent(event0, 1000) + addEvent(event1, 2000) + addEvent(event2, 15000) + addEvent(event3, 25000) + addEvent(event4, 30000) + addEvent(event5, 30000) + addEvent(event6, 30000) + return true +end + +raid:register() diff --git a/data/scripts/globalevents/raids.lua b/data/scripts/globalevents/raids.lua new file mode 100644 index 0000000000..4c5e7327bf --- /dev/null +++ b/data/scripts/globalevents/raids.lua @@ -0,0 +1,37 @@ +local event = GlobalEvent("raids") + +local CHECK_RAIDS_INTERVAL = 60 +local MAX_RAND_RANGE = 10000000 + +event:interval(CHECK_RAIDS_INTERVAL) +event:type("timer") + +local running = nil +local lastRaidEnd = 0 + +function event.onTime(interval) + io.write("Executing raids event...\n") + if running then + return + end + + local now = os.mtime() + + raids = Raid.getRaids() + for key, raid in pairs(raids) do + local chance = (MAX_RAND_RANGE * CHECK_RAIDS_INTERVAL) / raid.interval + if now >= lastRaidEnd + raid.margin and chance >= math.random(0, MAX_RAND_RANGE) then + running = key + + io.write("Executing raid: " .. raid.name .. "\n") + raid:execute() + + if not raid.repeats then + raids[key] = nil + end + break + end + end +end + +event:register() diff --git a/data/scripts/globalevents/raids_xml.lua b/data/scripts/globalevents/raids_xml.lua new file mode 100644 index 0000000000..b4d6723538 --- /dev/null +++ b/data/scripts/globalevents/raids_xml.lua @@ -0,0 +1,241 @@ +-- loads legacy raids from data/raids/raids.xml +local messageTypes = { + ["warning"] = MESSAGE_STATUS_WARNING, + ["event"] = MESSAGE_EVENT_ADVANCE, + ["default"] = MESSAGE_EVENT_DEFAULT, + ["description"] = MESSAGE_INFO_DESCR, + ["smallstatus"] = MESSAGE_STATUS_SMALL, + ["blueconsole"] = MESSAGE_STATUS_CONSOLE_BLUE, + ["redconsole"] = MESSAGE_STATUS_CONSOLE_RED, +} +local defaultMessageType = "event" + +local function parseAnnounce(node, filename) + local message = node:attribute("message") + if not message then + io.write("[Error] Missing message attribute, check data/raids/" .. filename .. "\n") + end + + local type = node:attribute("type") + if not type then + io.write("[Notice] Missing type for announce event in " .. filename .. ". Using default: " .. defaultMessageType .. ".\n") + type = defaultMessageType + end + + local messageType = messageTypes[type:lower()] + if not messageType then + io.write("[Notice] Unknown type " .. type .. " for announce event in " .. filename .. ". Using default: " .. messageTypes[defaultMessageType] .. ".\n") + messageType = messageTypes[defaultMessageType] + end + + return function() + Game.broadcastMessage(message, messageType) + end +end + +local function parseAreaSpawn(node, filename) + local fromx, fromy, fromz, tox, toy, toz + + local radius = tonumber(node:attribute("radius")) + if radius then + local centerx, centery, centerz = tonumber(node:attribute("centerx")), tonumber(node:attribute("centery")), tonumber(node:attribute("centerz")) + if not centerx or not centery or not centerz then + io.write("[Error] Missing one of: centerx, centery, centerz, check data/raids/" .. filename .. "\n") + end + + fromx, fromy, fromz = centerx - radius, centery - radius, z + tox, toy, toz = centerx + radius, centery + radius, z + else + fromx, fromy, fromz = tonumber(node:attribute("fromx")), tonumber(node:attribute("fromy")), tonumber(node:attribute("fromz")) + if not fromx or not fromy or not fromz then + io.write("[Error] Missing one of: fromx, fromy, fromz, check data/raids/" .. filename .. "\n") + end + + tox, toy, toz = tonumber(node:attribute("tox")), tonumber(node:attribute("toy")), tonumber(node:attribute("toz")) + if not tox or not toy or not toz then + io.write("[Error] Missing one of: tox, toy, toz, check data/raids/" .. filename .. "\n") + end + end + + local spawns = {} + for spawnNode in node:children() do + local name = spawnNode:attribute("name") + if not name then + io.write("[Error] Missing attribute name, check data/raids/" .. filename .. "\n") + return nil + end + + local minAmount, maxAmount = tonumber(spawnNode:attribute("minamount")), tonumber(spawnNode:attribute("maxamount")) + if not minAmount and not maxAmount then + local amount = tonumber(spawnNode:attribute("amount")) + if not amount then + io.write("[Error] Missing attributes minamount/maxamount or amount, check data/raids/" .. filename .. "\n") + end + + minAmount, maxAmount = amount, amount + elseif not minAmount then + io.write("[Warning] Missing attribute minamount in " .. filename .. ", using maxamount as default.\n") + minAmount = maxAmount + elseif not maxAmount then + io.write("[Warning] Missing attribute maxamount in " .. filename .. ", using minamount as default.\n") + maxAmount = minAmount + end + + spawns[#spawns] = { monsterName = name, minAmount = minAmount, maxAmount = maxAmount } + end + + return function() + for _, spawn in ipairs(spawns) do + for _ = 1, math.random(spawn.minAmount, spawn.maxAmount) do + local x, y = math.random(fromx, tox), math.random(fromy, toy) + Game.createMonster(spawns.name, Position(x, y, z)) + end + end + end +end + +local function parseScript(node) + local script = node:attribute("script") + if not script then + io.write("[Error] Missing attribute script, check data/raids/" .. filename .. "\n") + return nil + end + + local scriptFile = "data/raids/scripts/" .. script + dofile(script) + if not onRaid then + io.write("[Error] Can not load raid script, check " .. scriptFile .. " for a missing onRaid callback\n") + return nil + end + + local callback = onRaid + + -- let it be garbage collected + onRaid = nil + + return callback +end + +local function parseSingleSpawn(node, filename) + local name = node:attribute("name") + if not name then + io.write("[Error] Missing attribute name, check data/raids/" .. filename .. "\n") + return nil + end + + local x, y, z = tonumber(node:attribute("x")), tonumber(node:attribute("y")), tonumber(node:attribute("z")) + if not x or not y or not z then + io.write("[Error] Missing one of: x, y, z, check data/raids/" .. filename .. "\n") + end + + return function() + Game.createMonster(spawns.name, Position(x, y, z)) + end +end + +local eventParsers = { + ["announce"] = parseAnnounce, + ["areaspawn"] = parseAreaSpawn, + ["script"] = parseScript, + ["singlespawn"] = parseSingleSpawn, +} + +local function parseRaid(filename) + local doc = XMLDocument("data/raids/" .. filename) + local eventNodes = doc:child("raid") + + local events = {} + for eventNode in eventNodes:children() do + local parse = eventParsers[eventNode:name()] + if not parse then + io.write("[Error] Unknown event type: " .. eventNode:name() .. ".\n") + return nil + end + + local delay = tonumber(eventNode:attribute("delay")) + if not delay then + io.write("[Error] Missing attribute delay, check data/raids/" .. filename .. "\n") + return nil + end + + local callback = parse(eventNode, filename) + if not callback then + return nil + end + + events[#events] = { delay = delay, callback = callback } + end + + return events +end + +local function configureRaidEvent(node) + local name = node:attribute("name") + if not name then + io.write("[Error] Missing attribute name for raid.\n") + return nil + end + + local filename = node:attribute("file") + if not filename then + io.write('[Warning] file attribute missing for raid "' .. name .. '". Using default: "' .. name .. '.xml"\n') + filename = name .. ".xml" + end + + -- using filename instead of name because name can be duplicate, but filename cannot + local raid = Raid("data/raids/" .. filename) + + local interval = tonumber(node:attribute("interval2")) + if not interval or interval == 0 then + io.write("[Error] interval2 attribute missing or zero (would divide by 0), check raid " .. name .. " in data/raids/raids.xml\n") + return nil + end + raid.interval = interval + + local margin = tonumber(node:attribute("margin")) + if margin and margin > 0 then + raid.margin = margin * 60 * 1000 + else + io.write("[Warning] margin attribute missing for raid " .. name .. ". Using default: 0\n") + end + + local repeats = tobool(node:attribute("repeat")) + if repeats then + raid.repeats = repeats + end + + local events = parseRaid(filename) + if not events then + return nil + end + + for _, event in ipairs(events) do + raid:addEvent(event.delay, event.callback) + end + + return raid +end + +local event = GlobalEvent("load raids.xml") +event:type("startup") + +function event.onStartup() + local doc = XMLDocument("data/raids/raids.xml") + local raids = doc:child("raids") + + io.write(">> Loading legacy XML raids from data/raids/raids.xml...\n") + local loaded, start = 0, os.mtime() + for node in raids:children() do + local enabled = node:attribute("enabled") + if enabled == nil or tobool(enabled) then + local raid = configureRaidEvent(node) + if raid then + raid:register() + loaded = loaded + 1 + end + end + end + io.write(">> Loaded " .. loaded .. " raids in " .. os.mtime() - start .. "ms.\n") +end + +event:register() diff --git a/data/scripts/lib/raids.lua b/data/scripts/lib/raids.lua new file mode 100644 index 0000000000..8891a4c3d4 --- /dev/null +++ b/data/scripts/lib/raids.lua @@ -0,0 +1,43 @@ +local raids = {} + +Raid = setmetatable({ + getRaids = function() return raids end +}, { + __call = function(self, name) + local events = {} + + local obj = { + margin = 0, + name = name, + repeats = false, + } + + function obj:__newindex(key, value) + if key == "interval" or key == "margin" or key == "repeats" then + rawset(self, key, value) + else + io.write("[Warning] Invalid attribute for raid: " .. key .. ". Ignoring...\n") + end + end + + function obj:addEvent(delay, fn) + events[#events] = { delay = delay, fn = fn } + end + + function obj:execute() + for _, event in ipairs(events) do + addEvent(event.delay, event.fn) + end + end + + function obj:register() + raids[self.name] = self + end + + function obj:unregister() + raids[self.name] = nil + end + + return obj + end +}) diff --git a/data/talkactions/scripts/force_raid.lua b/data/talkactions/scripts/force_event.lua similarity index 74% rename from data/talkactions/scripts/force_raid.lua rename to data/talkactions/scripts/force_event.lua index 99dd2e9a4f..6af65adb99 100644 --- a/data/talkactions/scripts/force_raid.lua +++ b/data/talkactions/scripts/force_event.lua @@ -9,12 +9,11 @@ function onSay(player, words, param) logCommand(player, words, param) - local returnValue = Game.startRaid(param) + local returnValue = Game.startEvent(param) if returnValue ~= RETURNVALUE_NOERROR then player:sendTextMessage(MESSAGE_INFO_DESCR, Game.getReturnMessage(returnValue)) else - player:sendTextMessage(MESSAGE_INFO_DESCR, "Raid started.") + player:sendTextMessage(MESSAGE_INFO_DESCR, "Event started.") end - - return false + return true end diff --git a/data/talkactions/scripts/reload.lua b/data/talkactions/scripts/reload.lua index b98c08902c..2ba4e88cd3 100644 --- a/data/talkactions/scripts/reload.lua +++ b/data/talkactions/scripts/reload.lua @@ -39,9 +39,6 @@ local reloadTypes = { ["quest"] = RELOAD_TYPE_QUESTS, ["quests"] = RELOAD_TYPE_QUESTS, - ["raid"] = RELOAD_TYPE_RAIDS, - ["raids"] = RELOAD_TYPE_RAIDS, - ["spell"] = RELOAD_TYPE_SPELLS, ["spells"] = RELOAD_TYPE_SPELLS, diff --git a/data/talkactions/talkactions.xml b/data/talkactions/talkactions.xml index 11469b48d4..c54e0494d5 100644 --- a/data/talkactions/talkactions.xml +++ b/data/talkactions/talkactions.xml @@ -33,7 +33,7 @@ - + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e5bedaa438..560f02305a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,7 +54,6 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/protocollogin.cpp ${CMAKE_CURRENT_LIST_DIR}/protocolold.cpp ${CMAKE_CURRENT_LIST_DIR}/protocolstatus.cpp - ${CMAKE_CURRENT_LIST_DIR}/raids.cpp ${CMAKE_CURRENT_LIST_DIR}/rsa.cpp ${CMAKE_CURRENT_LIST_DIR}/scheduler.cpp ${CMAKE_CURRENT_LIST_DIR}/script.cpp @@ -141,7 +140,6 @@ set(tfs_HDR ${CMAKE_CURRENT_LIST_DIR}/protocolold.h ${CMAKE_CURRENT_LIST_DIR}/protocolstatus.h ${CMAKE_CURRENT_LIST_DIR}/pugicast.h - ${CMAKE_CURRENT_LIST_DIR}/raids.h ${CMAKE_CURRENT_LIST_DIR}/rsa.h ${CMAKE_CURRENT_LIST_DIR}/scheduler.h ${CMAKE_CURRENT_LIST_DIR}/script.h diff --git a/src/const.h b/src/const.h index 6dbc04df45..a5e7d9184e 100644 --- a/src/const.h +++ b/src/const.h @@ -674,7 +674,6 @@ enum ReloadTypes_t : uint8_t RELOAD_TYPE_MOVEMENTS, RELOAD_TYPE_NPCS, RELOAD_TYPE_QUESTS, - RELOAD_TYPE_RAIDS, RELOAD_TYPE_SCRIPTS, RELOAD_TYPE_SPELLS, RELOAD_TYPE_TALKACTIONS, diff --git a/src/enums.h b/src/enums.h index 3a05d77553..64606fb708 100644 --- a/src/enums.h +++ b/src/enums.h @@ -479,8 +479,6 @@ enum ReturnValue RETURNVALUE_CANONLYUSEONESHIELD, RETURNVALUE_NOPARTYMEMBERSINRANGE, RETURNVALUE_YOUARENOTTHEOWNER, - RETURNVALUE_NOSUCHRAIDEXISTS, - RETURNVALUE_ANOTHERRAIDISALREADYEXECUTING, RETURNVALUE_TRADEPLAYERFARAWAY, RETURNVALUE_YOUDONTOWNTHISHOUSE, RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE, diff --git a/src/game.cpp b/src/game.cpp index 6de690c024..2474edfa6b 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -103,9 +103,6 @@ void Game::setGameState(GameState_t newState) map.spawns.startup(); - raids.loadFromXml(); - raids.startup(); - mounts.loadFromXml(); loadPlayersRecord(); @@ -4927,7 +4924,6 @@ void Game::shutdown() g_databaseTasks.shutdown(); g_dispatcher.shutdown(); map.spawns.clear(); - raids.clear(); cleanup(); @@ -5976,9 +5972,6 @@ bool Game::reload(ReloadTypes_t reloadType) return true; } - case RELOAD_TYPE_RAIDS: - return raids.reload() && raids.startup(); - case RELOAD_TYPE_SPELLS: { if (!g_spells->reload()) { std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl; @@ -6013,7 +6006,6 @@ bool Game::reload(ReloadTypes_t reloadType) g_creatureEvents->removeInvalidEvents(); /* Npcs::reload(); - raids.reload() && raids.startup(); Item::items.reload(); mounts.reload(); g_config.reload(); @@ -6038,7 +6030,6 @@ bool Game::reload(ReloadTypes_t reloadType) g_monsters.reload(); g_moveEvents->reload(); Npcs::reload(); - raids.reload() && raids.startup(); g_talkActions->reload(); Item::items.reload(); g_weapons->reload(); diff --git a/src/game.h b/src/game.h index e73a25ddf6..d0ac439edd 100644 --- a/src/game.h +++ b/src/game.h @@ -9,7 +9,6 @@ #include "mounts.h" #include "player.h" #include "position.h" -#include "raids.h" #include "wildcardtree.h" class Monster; @@ -506,7 +505,6 @@ class Game Groups groups; Map map; Mounts mounts; - Raids raids; std::forward_list toDecayItems; diff --git a/src/globalevent.cpp b/src/globalevent.cpp index ac4c482081..c752afaf6c 100644 --- a/src/globalevent.cpp +++ b/src/globalevent.cpp @@ -115,7 +115,7 @@ void GlobalEvents::startup() const { execute(GLOBALEVENT_STARTUP); } void GlobalEvents::timer() { - time_t now = time(nullptr); + auto now = OTSYS_TIME(); int64_t nextScheduledTime = std::numeric_limits::max(); @@ -138,7 +138,7 @@ void GlobalEvents::timer() continue; } - nextExecutionTime = 86400; + nextExecutionTime = 86400000; if (nextExecutionTime < nextScheduledTime) { nextScheduledTime = nextExecutionTime; } @@ -150,7 +150,7 @@ void GlobalEvents::timer() if (nextScheduledTime != std::numeric_limits::max()) { timerEventId = g_scheduler.addEvent( - createSchedulerTask(std::max(1000, nextScheduledTime * 1000), [this]() { timer(); })); + createSchedulerTask(std::max(1000, nextScheduledTime), [this]() { timer(); })); } } @@ -279,7 +279,7 @@ bool GlobalEvent::configureEvent(const pugi::xml_node& node) difference += 86400; } - nextExecution = current_time + difference; + nextExecution = (current_time + difference) * 1000; eventType = GLOBALEVENT_TIMER; } else if ((attr = node.attribute("type"))) { const char* value = attr.value(); @@ -297,6 +297,7 @@ bool GlobalEvent::configureEvent(const pugi::xml_node& node) } else if ((attr = node.attribute("interval"))) { interval = std::max(SCHEDULER_MINTICKS, pugi::cast(attr.value())); nextExecution = OTSYS_TIME() + interval; + eventType = GLOBALEVENT_TIMER; } else { std::cout << "[Error - GlobalEvent::configureEvent] No interval for globalevent with name " << name << std::endl; diff --git a/src/luascript.cpp b/src/luascript.cpp index c0e492dd9a..0e2c0ae57e 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -41,6 +41,7 @@ extern Chat* g_chat; extern Game g_game; +extern GlobalEvents* g_globalEvents; extern Monsters g_monsters; extern ConfigManager g_config; extern Vocations g_vocations; @@ -2080,7 +2081,6 @@ void LuaScriptInterface::registerFunctions() registerEnum(RELOAD_TYPE_MOVEMENTS); registerEnum(RELOAD_TYPE_NPCS); registerEnum(RELOAD_TYPE_QUESTS); - registerEnum(RELOAD_TYPE_RAIDS); registerEnum(RELOAD_TYPE_SCRIPTS); registerEnum(RELOAD_TYPE_SPELLS); registerEnum(RELOAD_TYPE_TALKACTIONS); @@ -2254,7 +2254,7 @@ void LuaScriptInterface::registerFunctions() registerMethod("Game", "createTile", LuaScriptInterface::luaGameCreateTile); registerMethod("Game", "createMonsterType", LuaScriptInterface::luaGameCreateMonsterType); - registerMethod("Game", "startRaid", LuaScriptInterface::luaGameStartRaid); + registerMethod("Game", "startEvent", LuaScriptInterface::luaGameStartEvent); registerMethod("Game", "getClientVersion", LuaScriptInterface::luaGameGetClientVersion); @@ -4980,25 +4980,18 @@ int LuaScriptInterface::luaGameCreateMonsterType(lua_State* L) return 1; } -int LuaScriptInterface::luaGameStartRaid(lua_State* L) +int LuaScriptInterface::luaGameStartEvent(lua_State* L) { - // Game.startRaid(raidName) - const std::string& raidName = getString(L, 1); - - Raid* raid = g_game.raids.getRaidByName(raidName); - if (!raid || !raid->isLoaded()) { - lua_pushnumber(L, RETURNVALUE_NOSUCHRAIDEXISTS); - return 1; - } + // Game.startEvent(event) + const std::string& eventName = getString(L, 1); - if (g_game.raids.getRunning()) { - lua_pushnumber(L, RETURNVALUE_ANOTHERRAIDISALREADYEXECUTING); - return 1; + const auto& eventMap = g_globalEvents->getEventMap(GLOBALEVENT_TIMER); + try { + const auto& event = eventMap.at(eventName); + lua_pushboolean(L, event.executeEvent()); + } catch (const std::out_of_range&) { + lua_pushnil(L); } - - g_game.raids.setRunning(raid); - raid->startRaid(); - lua_pushnumber(L, RETURNVALUE_NOERROR); return 1; } @@ -17381,6 +17374,8 @@ int LuaScriptInterface::luaGlobalEventType(lua_State* L) global->setEventType(GLOBALEVENT_SHUTDOWN); } else if (tmpStr == "record") { global->setEventType(GLOBALEVENT_RECORD); + } else if (tmpStr == "timer") { + global->setEventType(GLOBALEVENT_TIMER); } else { std::cout << "[Error - CreatureEvent::configureLuaEvent] Invalid type for global event: " << typeName << std::endl; diff --git a/src/luascript.h b/src/luascript.h index 86b072139b..ed7ddc535d 100644 --- a/src/luascript.h +++ b/src/luascript.h @@ -542,7 +542,7 @@ class LuaScriptInterface static int luaGameCreateTile(lua_State* L); static int luaGameCreateMonsterType(lua_State* L); - static int luaGameStartRaid(lua_State* L); + static int luaGameStartEvent(lua_State* L); static int luaGameGetClientVersion(lua_State* L); diff --git a/src/raids.cpp b/src/raids.cpp deleted file mode 100644 index 053f40af30..0000000000 --- a/src/raids.cpp +++ /dev/null @@ -1,572 +0,0 @@ -// Copyright 2023 The Forgotten Server Authors. All rights reserved. -// Use of this source code is governed by the GPL-2.0 License that can be found in the LICENSE file. - -#include "otpch.h" - -#include "raids.h" - -#include "configmanager.h" -#include "game.h" -#include "monster.h" -#include "pugicast.h" -#include "scheduler.h" - -extern Game g_game; -extern ConfigManager g_config; - -Raids::Raids() { scriptInterface.initState(); } - -Raids::~Raids() -{ - for (Raid* raid : raidList) { - delete raid; - } -} - -bool Raids::loadFromXml() -{ - if (isLoaded()) { - return true; - } - - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file("data/raids/raids.xml"); - if (!result) { - printXMLError("Error - Raids::loadFromXml", "data/raids/raids.xml", result); - return false; - } - - for (auto raidNode : doc.child("raids").children()) { - std::string name, file; - uint32_t interval, margin; - - pugi::xml_attribute attr; - if ((attr = raidNode.attribute("name"))) { - name = attr.as_string(); - } else { - std::cout << "[Error - Raids::loadFromXml] Name tag missing for raid" << std::endl; - continue; - } - - if ((attr = raidNode.attribute("file"))) { - file = attr.as_string(); - } else { - file = fmt::format("raids/{:s}.xml", name); - std::cout << "[Warning - Raids::loadFromXml] File tag missing for raid " << name - << ". Using default: " << file << std::endl; - } - - interval = pugi::cast(raidNode.attribute("interval2").value()) * 60; - if (interval == 0) { - std::cout << "[Error - Raids::loadFromXml] interval2 tag missing or zero (would divide by 0) for raid: " - << name << std::endl; - continue; - } - - if ((attr = raidNode.attribute("margin"))) { - margin = pugi::cast(attr.value()) * 60 * 1000; - } else { - std::cout << "[Warning - Raids::loadFromXml] margin tag missing for raid: " << name << std::endl; - margin = 0; - } - - bool repeat; - if ((attr = raidNode.attribute("repeat"))) { - repeat = booleanString(attr.as_string()); - } else { - repeat = false; - } - - Raid* newRaid = new Raid(name, interval, margin, repeat); - if (newRaid->loadFromXml("data/raids/" + file)) { - raidList.push_back(newRaid); - } else { - std::cout << "[Error - Raids::loadFromXml] Failed to load raid: " << name << std::endl; - delete newRaid; - } - } - - loaded = true; - return true; -} - -static constexpr int32_t MAX_RAND_RANGE = 10000000; - -bool Raids::startup() -{ - if (!isLoaded() || isStarted()) { - return false; - } - - setLastRaidEnd(OTSYS_TIME()); - - checkRaidsEvent = - g_scheduler.addEvent(createSchedulerTask(CHECK_RAIDS_INTERVAL * 1000, [this]() { checkRaids(); })); - - started = true; - return started; -} - -void Raids::checkRaids() -{ - if (!getRunning()) { - uint64_t now = OTSYS_TIME(); - - for (auto it = raidList.begin(), end = raidList.end(); it != end; ++it) { - Raid* raid = *it; - if (now >= (getLastRaidEnd() + raid->getMargin())) { - if (((MAX_RAND_RANGE * CHECK_RAIDS_INTERVAL) / raid->getInterval()) >= - static_cast(uniform_random(0, MAX_RAND_RANGE))) { - setRunning(raid); - raid->startRaid(); - - if (!raid->canBeRepeated()) { - raidList.erase(it); - } - break; - } - } - } - } - - checkRaidsEvent = - g_scheduler.addEvent(createSchedulerTask(CHECK_RAIDS_INTERVAL * 1000, [this]() { checkRaids(); })); -} - -void Raids::clear() -{ - g_scheduler.stopEvent(checkRaidsEvent); - checkRaidsEvent = 0; - - for (Raid* raid : raidList) { - raid->stopEvents(); - delete raid; - } - raidList.clear(); - - loaded = false; - started = false; - running = nullptr; - lastRaidEnd = 0; - - scriptInterface.reInitState(); -} - -bool Raids::reload() -{ - clear(); - return loadFromXml(); -} - -Raid* Raids::getRaidByName(const std::string& name) -{ - for (Raid* raid : raidList) { - if (caseInsensitiveEqual(raid->getName(), name)) { - return raid; - } - } - return nullptr; -} - -Raid::~Raid() -{ - for (RaidEvent* raidEvent : raidEvents) { - delete raidEvent; - } -} - -bool Raid::loadFromXml(const std::string& filename) -{ - if (isLoaded()) { - return true; - } - - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(filename.c_str()); - if (!result) { - printXMLError("Error - Raid::loadFromXml", filename, result); - return false; - } - - for (auto eventNode : doc.child("raid").children()) { - RaidEvent* event; - if (caseInsensitiveEqual(eventNode.name(), "announce")) { - event = new AnnounceEvent(); - } else if (caseInsensitiveEqual(eventNode.name(), "singlespawn")) { - event = new SingleSpawnEvent(); - } else if (caseInsensitiveEqual(eventNode.name(), "areaspawn")) { - event = new AreaSpawnEvent(); - } else if (caseInsensitiveEqual(eventNode.name(), "script")) { - event = new ScriptEvent(&g_game.raids.getScriptInterface()); - } else { - continue; - } - - if (event->configureRaidEvent(eventNode)) { - raidEvents.push_back(event); - } else { - std::cout << "[Error - Raid::loadFromXml] In file (" << filename << "), eventNode: " << eventNode.name() - << std::endl; - delete event; - } - } - - // sort by delay time - std::sort(raidEvents.begin(), raidEvents.end(), - [](const RaidEvent* lhs, const RaidEvent* rhs) { return lhs->getDelay() < rhs->getDelay(); }); - - loaded = true; - return true; -} - -void Raid::startRaid() -{ - RaidEvent* raidEvent = getNextRaidEvent(); - if (raidEvent) { - state = RAIDSTATE_EXECUTING; - nextEventEvent = g_scheduler.addEvent( - createSchedulerTask(raidEvent->getDelay(), [=, this]() { executeRaidEvent(raidEvent); })); - } -} - -void Raid::executeRaidEvent(RaidEvent* raidEvent) -{ - if (raidEvent->executeEvent()) { - nextEvent++; - RaidEvent* newRaidEvent = getNextRaidEvent(); - - if (newRaidEvent) { - uint32_t ticks = static_cast( - std::max(RAID_MINTICKS, newRaidEvent->getDelay() - raidEvent->getDelay())); - nextEventEvent = - g_scheduler.addEvent(createSchedulerTask(ticks, [=, this]() { executeRaidEvent(newRaidEvent); })); - } else { - resetRaid(); - } - } else { - resetRaid(); - } -} - -void Raid::resetRaid() -{ - nextEvent = 0; - state = RAIDSTATE_IDLE; - g_game.raids.setRunning(nullptr); - g_game.raids.setLastRaidEnd(OTSYS_TIME()); -} - -void Raid::stopEvents() -{ - if (nextEventEvent != 0) { - g_scheduler.stopEvent(nextEventEvent); - nextEventEvent = 0; - } -} - -RaidEvent* Raid::getNextRaidEvent() -{ - if (nextEvent < raidEvents.size()) { - return raidEvents[nextEvent]; - } - return nullptr; -} - -bool RaidEvent::configureRaidEvent(const pugi::xml_node& eventNode) -{ - pugi::xml_attribute delayAttribute = eventNode.attribute("delay"); - if (!delayAttribute) { - std::cout << "[Error] Raid: delay tag missing." << std::endl; - return false; - } - - delay = std::max(RAID_MINTICKS, pugi::cast(delayAttribute.value())); - return true; -} - -bool AnnounceEvent::configureRaidEvent(const pugi::xml_node& eventNode) -{ - if (!RaidEvent::configureRaidEvent(eventNode)) { - return false; - } - - pugi::xml_attribute messageAttribute = eventNode.attribute("message"); - if (!messageAttribute) { - std::cout << "[Error] Raid: message tag missing for announce event." << std::endl; - return false; - } - message = messageAttribute.as_string(); - - pugi::xml_attribute typeAttribute = eventNode.attribute("type"); - if (typeAttribute) { - std::string tmpStrValue = boost::algorithm::to_lower_copy(typeAttribute.as_string()); - if (tmpStrValue == "warning") { - messageType = MESSAGE_STATUS_WARNING; - } else if (tmpStrValue == "event") { - messageType = MESSAGE_EVENT_ADVANCE; - } else if (tmpStrValue == "default") { - messageType = MESSAGE_EVENT_DEFAULT; - } else if (tmpStrValue == "description") { - messageType = MESSAGE_INFO_DESCR; - } else if (tmpStrValue == "smallstatus") { - messageType = MESSAGE_STATUS_SMALL; - } else if (tmpStrValue == "blueconsole" || tmpStrValue == "redconsole") { - std::cout << "[Notice] Raid: Deprecated type tag for announce event. Using default: " - << static_cast(messageType) << std::endl; - } else { - std::cout << "[Notice] Raid: Unknown type tag missing for announce event. Using default: " - << static_cast(messageType) << std::endl; - } - } else { - messageType = MESSAGE_EVENT_ADVANCE; - std::cout << "[Notice] Raid: type tag missing for announce event. Using default: " - << static_cast(messageType) << std::endl; - } - return true; -} - -bool AnnounceEvent::executeEvent() -{ - g_game.broadcastMessage(message, messageType); - return true; -} - -bool SingleSpawnEvent::configureRaidEvent(const pugi::xml_node& eventNode) -{ - if (!RaidEvent::configureRaidEvent(eventNode)) { - return false; - } - - pugi::xml_attribute attr; - if ((attr = eventNode.attribute("name"))) { - monsterName = attr.as_string(); - } else { - std::cout << "[Error] Raid: name tag missing for singlespawn event." << std::endl; - return false; - } - - if ((attr = eventNode.attribute("x"))) { - position.x = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: x tag missing for singlespawn event." << std::endl; - return false; - } - - if ((attr = eventNode.attribute("y"))) { - position.y = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: y tag missing for singlespawn event." << std::endl; - return false; - } - - if ((attr = eventNode.attribute("z"))) { - position.z = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: z tag missing for singlespawn event." << std::endl; - return false; - } - return true; -} - -bool SingleSpawnEvent::executeEvent() -{ - Monster* monster = Monster::createMonster(monsterName); - if (!monster) { - std::cout << "[Error] Raids: Cant create monster " << monsterName << std::endl; - return false; - } - - if (!g_game.placeCreature(monster, position, false, true)) { - delete monster; - std::cout << "[Error] Raids: Cant place monster " << monsterName << std::endl; - return false; - } - return true; -} - -bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node& eventNode) -{ - if (!RaidEvent::configureRaidEvent(eventNode)) { - return false; - } - - pugi::xml_attribute attr; - if ((attr = eventNode.attribute("radius"))) { - int32_t radius = pugi::cast(attr.value()); - Position centerPos; - - if ((attr = eventNode.attribute("centerx"))) { - centerPos.x = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: centerx tag missing for areaspawn event." << std::endl; - return false; - } - - if ((attr = eventNode.attribute("centery"))) { - centerPos.y = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: centery tag missing for areaspawn event." << std::endl; - return false; - } - - if ((attr = eventNode.attribute("centerz"))) { - centerPos.z = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: centerz tag missing for areaspawn event." << std::endl; - return false; - } - - fromPos.x = std::max(0, centerPos.getX() - radius); - fromPos.y = std::max(0, centerPos.getY() - radius); - fromPos.z = centerPos.z; - - toPos.x = std::min(0xFFFF, centerPos.getX() + radius); - toPos.y = std::min(0xFFFF, centerPos.getY() + radius); - toPos.z = centerPos.z; - } else { - if ((attr = eventNode.attribute("fromx"))) { - fromPos.x = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: fromx tag missing for areaspawn event." << std::endl; - return false; - } - - if ((attr = eventNode.attribute("fromy"))) { - fromPos.y = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: fromy tag missing for areaspawn event." << std::endl; - return false; - } - - if ((attr = eventNode.attribute("fromz"))) { - fromPos.z = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: fromz tag missing for areaspawn event." << std::endl; - return false; - } - - if ((attr = eventNode.attribute("tox"))) { - toPos.x = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: tox tag missing for areaspawn event." << std::endl; - return false; - } - - if ((attr = eventNode.attribute("toy"))) { - toPos.y = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: toy tag missing for areaspawn event." << std::endl; - return false; - } - - if ((attr = eventNode.attribute("toz"))) { - toPos.z = pugi::cast(attr.value()); - } else { - std::cout << "[Error] Raid: toz tag missing for areaspawn event." << std::endl; - return false; - } - } - - for (auto monsterNode : eventNode.children()) { - const char* name; - - if ((attr = monsterNode.attribute("name"))) { - name = attr.value(); - } else { - std::cout << "[Error] Raid: name tag missing for monster node." << std::endl; - return false; - } - - uint32_t minAmount; - if ((attr = monsterNode.attribute("minamount"))) { - minAmount = pugi::cast(attr.value()); - } else { - minAmount = 0; - } - - uint32_t maxAmount; - if ((attr = monsterNode.attribute("maxamount"))) { - maxAmount = pugi::cast(attr.value()); - } else { - maxAmount = 0; - } - - if (maxAmount == 0 && minAmount == 0) { - if ((attr = monsterNode.attribute("amount"))) { - minAmount = pugi::cast(attr.value()); - maxAmount = minAmount; - } else { - std::cout << "[Error] Raid: amount tag missing for monster node." << std::endl; - return false; - } - } - - spawnList.emplace_back(name, minAmount, maxAmount); - } - return true; -} - -bool AreaSpawnEvent::executeEvent() -{ - for (const MonsterSpawn& spawn : spawnList) { - uint32_t amount = uniform_random(spawn.minAmount, spawn.maxAmount); - for (uint32_t i = 0; i < amount; ++i) { - Monster* monster = Monster::createMonster(spawn.name); - if (!monster) { - std::cout << "[Error - AreaSpawnEvent::executeEvent] Can't create monster " << spawn.name << std::endl; - return false; - } - - bool success = false; - for (int32_t tries = 0; tries < MAXIMUM_TRIES_PER_MONSTER; tries++) { - Tile* tile = g_game.map.getTile(uniform_random(fromPos.x, toPos.x), uniform_random(fromPos.y, toPos.y), - uniform_random(fromPos.z, toPos.z)); - if (tile && !tile->isMoveableBlocking() && !tile->hasFlag(TILESTATE_PROTECTIONZONE) && - !tile->getTopCreature() && g_game.placeCreature(monster, tile->getPosition(), false, true)) { - success = true; - break; - } - } - - if (!success) { - delete monster; - } - } - } - return true; -} - -bool ScriptEvent::configureRaidEvent(const pugi::xml_node& eventNode) -{ - if (!RaidEvent::configureRaidEvent(eventNode)) { - return false; - } - - pugi::xml_attribute scriptAttribute = eventNode.attribute("script"); - if (!scriptAttribute) { - std::cout << "Error: [ScriptEvent::configureRaidEvent] No script file found for raid" << std::endl; - return false; - } - - if (!loadScript("data/raids/scripts/" + std::string(scriptAttribute.as_string()))) { - std::cout << "Error: [ScriptEvent::configureRaidEvent] Can not load raid script." << std::endl; - return false; - } - return true; -} - -bool ScriptEvent::executeEvent() -{ - // onRaid() - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - ScriptEvent::onRaid] Call stack overflow" << std::endl; - return false; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(scriptId, scriptInterface); - - scriptInterface->pushFunction(scriptId); - - return scriptInterface->callFunction(0); -} diff --git a/src/raids.h b/src/raids.h deleted file mode 100644 index d09a3b597d..0000000000 --- a/src/raids.h +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2023 The Forgotten Server Authors. All rights reserved. -// Use of this source code is governed by the GPL-2.0 License that can be found in the LICENSE file. - -#ifndef FS_RAIDS_H -#define FS_RAIDS_H - -#include "baseevents.h" -#include "const.h" -#include "luascript.h" -#include "position.h" - -enum RaidState_t -{ - RAIDSTATE_IDLE, - RAIDSTATE_EXECUTING, -}; - -struct MonsterSpawn -{ - MonsterSpawn(std::string name, uint32_t minAmount, uint32_t maxAmount) : - name(std::move(name)), minAmount(minAmount), maxAmount(maxAmount) - {} - - std::string name; - uint32_t minAmount; - uint32_t maxAmount; -}; - -// How many times it will try to find a tile to add the monster to before giving -// up -static constexpr int32_t MAXIMUM_TRIES_PER_MONSTER = 10; -static constexpr int32_t CHECK_RAIDS_INTERVAL = 60; -static constexpr int32_t RAID_MINTICKS = 1000; - -class Raid; -class RaidEvent; - -class Raids -{ -public: - Raids(); - ~Raids(); - - // non-copyable - Raids(const Raids&) = delete; - Raids& operator=(const Raids&) = delete; - - bool loadFromXml(); - bool startup(); - - void clear(); - bool reload(); - - bool isLoaded() const { return loaded; } - bool isStarted() const { return started; } - - Raid* getRunning() { return running; } - void setRunning(Raid* newRunning) { running = newRunning; } - - Raid* getRaidByName(const std::string& name); - - uint64_t getLastRaidEnd() const { return lastRaidEnd; } - void setLastRaidEnd(uint64_t newLastRaidEnd) { lastRaidEnd = newLastRaidEnd; } - - void checkRaids(); - - LuaScriptInterface& getScriptInterface() { return scriptInterface; } - -private: - LuaScriptInterface scriptInterface{"Raid Interface"}; - - std::list raidList; - Raid* running = nullptr; - uint64_t lastRaidEnd = 0; - uint32_t checkRaidsEvent = 0; - bool loaded = false; - bool started = false; -}; - -class Raid -{ -public: - Raid(std::string name, uint32_t interval, uint32_t marginTime, bool repeat) : - name(std::move(name)), interval(interval), margin(marginTime), repeat(repeat) - {} - ~Raid(); - - // non-copyable - Raid(const Raid&) = delete; - Raid& operator=(const Raid&) = delete; - - bool loadFromXml(const std::string& filename); - - void startRaid(); - - void executeRaidEvent(RaidEvent* raidEvent); - void resetRaid(); - - RaidEvent* getNextRaidEvent(); - void setState(RaidState_t newState) { state = newState; } - const std::string& getName() const { return name; } - - bool isLoaded() const { return loaded; } - uint64_t getMargin() const { return margin; } - uint32_t getInterval() const { return interval; } - bool canBeRepeated() const { return repeat; } - - void stopEvents(); - -private: - std::vector raidEvents; - std::string name; - uint32_t interval; - uint32_t nextEvent = 0; - uint64_t margin; - RaidState_t state = RAIDSTATE_IDLE; - uint32_t nextEventEvent = 0; - bool loaded = false; - bool repeat; -}; - -class RaidEvent -{ -public: - virtual ~RaidEvent() = default; - - virtual bool configureRaidEvent(const pugi::xml_node& eventNode); - - virtual bool executeEvent() = 0; - uint32_t getDelay() const { return delay; } - -private: - uint32_t delay; -}; - -class AnnounceEvent final : public RaidEvent -{ -public: - AnnounceEvent() = default; - - bool configureRaidEvent(const pugi::xml_node& eventNode) override; - - bool executeEvent() override; - -private: - std::string message; - MessageClasses messageType = MESSAGE_EVENT_ADVANCE; -}; - -class SingleSpawnEvent final : public RaidEvent -{ -public: - bool configureRaidEvent(const pugi::xml_node& eventNode) override; - - bool executeEvent() override; - -private: - std::string monsterName; - Position position; -}; - -class AreaSpawnEvent final : public RaidEvent -{ -public: - bool configureRaidEvent(const pugi::xml_node& eventNode) override; - - bool executeEvent() override; - -private: - std::list spawnList; - Position fromPos, toPos; -}; - -class ScriptEvent final : public RaidEvent, public Event -{ -public: - explicit ScriptEvent(LuaScriptInterface* interface) : Event(interface) {} - - bool configureRaidEvent(const pugi::xml_node& eventNode) override; - bool configureEvent(const pugi::xml_node&) override { return false; } - - bool executeEvent() override; - -private: - std::string_view getScriptEventName() const override { return "onRaid"; } -}; - -#endif // FS_RAIDS_H diff --git a/src/signals.cpp b/src/signals.cpp index 754b73a108..aac93115a7 100644 --- a/src/signals.cpp +++ b/src/signals.cpp @@ -15,7 +15,6 @@ #include "mounts.h" #include "movement.h" #include "npc.h" -#include "raids.h" #include "scheduler.h" #include "spells.h" #include "talkaction.h" @@ -72,10 +71,6 @@ void sighupHandler() Npcs::reload(); std::cout << "Reloaded npcs." << std::endl; - g_game.raids.reload(); - g_game.raids.startup(); - std::cout << "Reloaded raids." << std::endl; - g_monsters.reload(); std::cout << "Reloaded monsters." << std::endl; diff --git a/src/tools.cpp b/src/tools.cpp index ec448c426e..3041106b07 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -1184,12 +1184,6 @@ const char* getReturnMessage(ReturnValue value) case RETURNVALUE_YOUARENOTTHEOWNER: return "You are not the owner."; - case RETURNVALUE_NOSUCHRAIDEXISTS: - return "No such raid exists."; - - case RETURNVALUE_ANOTHERRAIDISALREADYEXECUTING: - return "Another raid is already executing."; - case RETURNVALUE_TRADEPLAYERFARAWAY: return "Trade player is too far away."; diff --git a/vc17/theforgottenserver.vcxproj b/vc17/theforgottenserver.vcxproj index 0d040e5932..fcaf899b80 100644 --- a/vc17/theforgottenserver.vcxproj +++ b/vc17/theforgottenserver.vcxproj @@ -220,7 +220,7 @@ - + @@ -305,7 +305,7 @@ - + From 3e0a539467a55949bd674222d2da57730babccd3 Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Sat, 22 Apr 2023 18:58:01 +0200 Subject: [PATCH 02/10] Fix merge conflict --- vc17/theforgottenserver.vcxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/vc17/theforgottenserver.vcxproj b/vc17/theforgottenserver.vcxproj index fcaf899b80..9ace9a8697 100644 --- a/vc17/theforgottenserver.vcxproj +++ b/vc17/theforgottenserver.vcxproj @@ -220,7 +220,6 @@ - @@ -305,7 +304,6 @@ - From c3fded005cbd4944a69f51e8fbd597edaccfc52d Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Sat, 22 Apr 2023 20:59:11 +0200 Subject: [PATCH 03/10] Code fixes --- data/scripts/globalevents/#testraid.lua | 1 - data/scripts/globalevents/raids.lua | 3 +-- data/scripts/globalevents/raids_xml.lua | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/data/scripts/globalevents/#testraid.lua b/data/scripts/globalevents/#testraid.lua index bbcd159564..6dcac09aaf 100644 --- a/data/scripts/globalevents/#testraid.lua +++ b/data/scripts/globalevents/#testraid.lua @@ -1,5 +1,4 @@ local raid = GlobalEvent("Testraid") -raid:type("timer") raid:interval(1800) local function event0() diff --git a/data/scripts/globalevents/raids.lua b/data/scripts/globalevents/raids.lua index 4c5e7327bf..0c1a26e0cb 100644 --- a/data/scripts/globalevents/raids.lua +++ b/data/scripts/globalevents/raids.lua @@ -4,7 +4,6 @@ local CHECK_RAIDS_INTERVAL = 60 local MAX_RAND_RANGE = 10000000 event:interval(CHECK_RAIDS_INTERVAL) -event:type("timer") local running = nil local lastRaidEnd = 0 @@ -17,7 +16,7 @@ function event.onTime(interval) local now = os.mtime() - raids = Raid.getRaids() + local raids = Raid.getRaids() for key, raid in pairs(raids) do local chance = (MAX_RAND_RANGE * CHECK_RAIDS_INTERVAL) / raid.interval if now >= lastRaidEnd + raid.margin and chance >= math.random(0, MAX_RAND_RANGE) then diff --git a/data/scripts/globalevents/raids_xml.lua b/data/scripts/globalevents/raids_xml.lua index b4d6723538..63c721e820 100644 --- a/data/scripts/globalevents/raids_xml.lua +++ b/data/scripts/globalevents/raids_xml.lua @@ -217,7 +217,6 @@ local function configureRaidEvent(node) end local event = GlobalEvent("load raids.xml") -event:type("startup") function event.onStartup() local doc = XMLDocument("data/raids/raids.xml") From 6a499e38c505125b6aa52d7acc08720cc404e9fa Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Sun, 23 Apr 2023 12:06:20 +0200 Subject: [PATCH 04/10] Move raids lib from scripts to core --- data/{scripts/lib => lib/core}/raids.lua | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename data/{scripts/lib => lib/core}/raids.lua (100%) diff --git a/data/scripts/lib/raids.lua b/data/lib/core/raids.lua similarity index 100% rename from data/scripts/lib/raids.lua rename to data/lib/core/raids.lua From 745d04e8518d4024c13dd97a3ae9f45889409b26 Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Sun, 23 Apr 2023 12:06:37 +0200 Subject: [PATCH 05/10] Fix timer event type --- data/lib/compat/compat.lua | 1 + src/globalevent.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/data/lib/compat/compat.lua b/data/lib/compat/compat.lua index 9caf9e5b5c..b0cf6f919a 100644 --- a/data/lib/compat/compat.lua +++ b/data/lib/compat/compat.lua @@ -237,6 +237,7 @@ do self:onThink(value) return elseif key == "onTime" then + self:type("timer") self:onTime(value) return elseif key == "onStartup" then diff --git a/src/globalevent.cpp b/src/globalevent.cpp index c752afaf6c..88f20c6253 100644 --- a/src/globalevent.cpp +++ b/src/globalevent.cpp @@ -297,7 +297,6 @@ bool GlobalEvent::configureEvent(const pugi::xml_node& node) } else if ((attr = node.attribute("interval"))) { interval = std::max(SCHEDULER_MINTICKS, pugi::cast(attr.value())); nextExecution = OTSYS_TIME() + interval; - eventType = GLOBALEVENT_TIMER; } else { std::cout << "[Error - GlobalEvent::configureEvent] No interval for globalevent with name " << name << std::endl; From be9f01b21161e39654bc6fc73af7710be40bcaed Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Sun, 23 Apr 2023 12:06:55 +0200 Subject: [PATCH 06/10] Avoid try-catch for flow control --- src/luascript.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/luascript.cpp b/src/luascript.cpp index 0e2c0ae57e..795e21de11 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -4986,10 +4986,9 @@ int LuaScriptInterface::luaGameStartEvent(lua_State* L) const std::string& eventName = getString(L, 1); const auto& eventMap = g_globalEvents->getEventMap(GLOBALEVENT_TIMER); - try { - const auto& event = eventMap.at(eventName); - lua_pushboolean(L, event.executeEvent()); - } catch (const std::out_of_range&) { + if (auto it = eventMap.find(eventName); it != eventMap.end()) { + pushBoolean(L, it->execute()); + } else { lua_pushnil(L); } return 1; From 864ef6f3646f70bd8e275cedf70295fb16108ab8 Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Tue, 25 Apr 2023 00:30:21 +0200 Subject: [PATCH 07/10] Send message when event is not found --- data/talkactions/scripts/force_event.lua | 8 ++++---- src/luascript.cpp | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/data/talkactions/scripts/force_event.lua b/data/talkactions/scripts/force_event.lua index 6af65adb99..4627552e27 100644 --- a/data/talkactions/scripts/force_event.lua +++ b/data/talkactions/scripts/force_event.lua @@ -10,10 +10,10 @@ function onSay(player, words, param) logCommand(player, words, param) local returnValue = Game.startEvent(param) - if returnValue ~= RETURNVALUE_NOERROR then - player:sendTextMessage(MESSAGE_INFO_DESCR, Game.getReturnMessage(returnValue)) - else + if returnValue then player:sendTextMessage(MESSAGE_INFO_DESCR, "Event started.") + else + player:sendTextMessage(MESSAGE_INFO_DESCR, "No such event exists.") end - return true + return returnValue end diff --git a/src/luascript.cpp b/src/luascript.cpp index 795e21de11..027ed36bb4 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -4987,9 +4987,10 @@ int LuaScriptInterface::luaGameStartEvent(lua_State* L) const auto& eventMap = g_globalEvents->getEventMap(GLOBALEVENT_TIMER); if (auto it = eventMap.find(eventName); it != eventMap.end()) { - pushBoolean(L, it->execute()); + it->execute(); + pushBoolean(L, true); } else { - lua_pushnil(L); + pushBoolean(L, false); } return 1; } From 8c6b0b9dee6dc8f75c56f62196a08b5c96f1776e Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Tue, 25 Apr 2023 23:39:26 +0200 Subject: [PATCH 08/10] Fix event execution function --- src/luascript.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/luascript.cpp b/src/luascript.cpp index 027ed36bb4..b9edcfb3b8 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -4987,8 +4987,7 @@ int LuaScriptInterface::luaGameStartEvent(lua_State* L) const auto& eventMap = g_globalEvents->getEventMap(GLOBALEVENT_TIMER); if (auto it = eventMap.find(eventName); it != eventMap.end()) { - it->execute(); - pushBoolean(L, true); + pushBoolean(L, it->second.executeEvent()); } else { pushBoolean(L, false); } From 857c07c2c01c94f3ab587ce4daf28904a1d4bf73 Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Tue, 25 Apr 2023 23:45:19 +0200 Subject: [PATCH 09/10] Fix force event messages --- data/talkactions/scripts/force_event.lua | 7 ++++--- src/luascript.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/data/talkactions/scripts/force_event.lua b/data/talkactions/scripts/force_event.lua index 4627552e27..300d539655 100644 --- a/data/talkactions/scripts/force_event.lua +++ b/data/talkactions/scripts/force_event.lua @@ -10,10 +10,11 @@ function onSay(player, words, param) logCommand(player, words, param) local returnValue = Game.startEvent(param) - if returnValue then - player:sendTextMessage(MESSAGE_INFO_DESCR, "Event started.") - else + if returnValue == nil then player:sendTextMessage(MESSAGE_INFO_DESCR, "No such event exists.") + return false end + + player:sendTextMessage(MESSAGE_INFO_DESCR, "Event started.") return returnValue end diff --git a/src/luascript.cpp b/src/luascript.cpp index b9edcfb3b8..75f7f2ec10 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -4989,7 +4989,7 @@ int LuaScriptInterface::luaGameStartEvent(lua_State* L) if (auto it = eventMap.find(eventName); it != eventMap.end()) { pushBoolean(L, it->second.executeEvent()); } else { - pushBoolean(L, false); + lua_pushnil(L); } return 1; } From 89992a2fa829f5951c9471d9a1997f517ff90f2c Mon Sep 17 00:00:00 2001 From: Ranieri Althoff Date: Tue, 10 Oct 2023 00:57:14 +0200 Subject: [PATCH 10/10] Move talkaction to revscriptsys --- .../scripts => scripts/talkactions}/force_event.lua | 7 ++++++- data/talkactions/talkactions.xml | 1 - 2 files changed, 6 insertions(+), 2 deletions(-) rename data/{talkactions/scripts => scripts/talkactions}/force_event.lua (77%) diff --git a/data/talkactions/scripts/force_event.lua b/data/scripts/talkactions/force_event.lua similarity index 77% rename from data/talkactions/scripts/force_event.lua rename to data/scripts/talkactions/force_event.lua index 300d539655..e9f9e68000 100644 --- a/data/talkactions/scripts/force_event.lua +++ b/data/scripts/talkactions/force_event.lua @@ -1,4 +1,6 @@ -function onSay(player, words, param) +local talk = TalkAction("/event", "!event") + +function talk.onSay(player, words, param) if not player:getGroup():getAccess() then return true end @@ -18,3 +20,6 @@ function onSay(player, words, param) player:sendTextMessage(MESSAGE_INFO_DESCR, "Event started.") return returnValue end + +talk:separator(" ") +talk:register() diff --git a/data/talkactions/talkactions.xml b/data/talkactions/talkactions.xml index c54e0494d5..b3990d6636 100644 --- a/data/talkactions/talkactions.xml +++ b/data/talkactions/talkactions.xml @@ -33,7 +33,6 @@ -