forgottenserver/data/scripts/xml/#raids.lua
Ramon Bernardo 2aab26709d
Fix reload XML files from lua (#4844)
* Fix reload XML files from lua

* Fix error message
2024-11-24 23:02:59 +01:00

245 lines
7.3 KiB
Lua

-- 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 function loadXMLRaids()
local doc = XMLDocument("data/raids/raids.xml")
if not doc then
io.write("[Warning - Scripts::XML::loadXMLRaids] Could not load raids.xml.\n")
return
end
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
loadXMLRaids()