mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-23 22:37:55 +02:00
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there. * Declared existing Entity APIs. * Added basic script context caching * Started Lua script module * Started Lua spell effect API * Started script state persistence * Started battle info callback binding * CommitPackage removed * Extracted spells::Caster to own header; Expanded Spell API. * implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C * !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented * Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key) * Re-enabled VERM macros. * !?GM0 added * !?TM implemented * Added !!MF:N * Started !?OB, !!BM, !!HE, !!OW, !!UN * Added basic support of w-variables * Added support for ERM indirect variables * Made !?FU regular trigger * !!re (ERA loop receiver) implemented * Fixed ERM receivers with zero args.
This commit is contained in:
24
scripts/lib/Metatype.lua
Normal file
24
scripts/lib/Metatype.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
local T =
|
||||
{
|
||||
ARTIFACT = 1,
|
||||
ARTIFACT_INSTANCE = 2,
|
||||
CREATURE = 3,
|
||||
CREATURE_INSTANCE = 4,
|
||||
FACTION = 5,
|
||||
HERO_CLASS = 6,
|
||||
HERO_TYPE = 7,
|
||||
HERO_INSTANCE = 8,
|
||||
MAP_OBJECT_GROUP = 9,
|
||||
MAP_OBJECT_TYPE = 10,
|
||||
MAP_OBJECT_INSTANCE = 11,
|
||||
SKILL = 12,
|
||||
SPELL = 13
|
||||
}
|
||||
|
||||
local M = setmetatable({},
|
||||
{
|
||||
__newindex = function() error("Metatype table is immutable") end,
|
||||
__index = T
|
||||
})
|
||||
|
||||
return M
|
||||
252
scripts/lib/erm.lua
Normal file
252
scripts/lib/erm.lua
Normal file
@@ -0,0 +1,252 @@
|
||||
DATA = DATA or {}
|
||||
local DATA = DATA
|
||||
local GAME = GAME
|
||||
|
||||
local TurnStarted = require("events.TurnStarted")
|
||||
|
||||
DATA.ERM = DATA.ERM or {}
|
||||
|
||||
local ERM_D = DATA.ERM
|
||||
|
||||
ERM_D.F = ERM_D.F or {}
|
||||
ERM_D.MDATA = ERM_D.MDATA or {}
|
||||
ERM_D.Q = ERM_D.Q or {}
|
||||
ERM_D.v = ERM_D.v or {}
|
||||
ERM_D.z = ERM_D.z or {}
|
||||
|
||||
local INT_MT =
|
||||
{
|
||||
__index = function(t, key)
|
||||
return rawget(t, key) or 0
|
||||
end
|
||||
}
|
||||
|
||||
local STR_MT =
|
||||
{
|
||||
__index = function(t, key)
|
||||
return rawget(t, key) or ""
|
||||
end
|
||||
}
|
||||
|
||||
setmetatable(DATA.ERM.Q, INT_MT)
|
||||
setmetatable(DATA.ERM.v, INT_MT)
|
||||
setmetatable(DATA.ERM.z, STR_MT)
|
||||
|
||||
local ERM =
|
||||
{
|
||||
w = {},
|
||||
M = {},
|
||||
subs = {}
|
||||
}
|
||||
|
||||
local ERM_MT =
|
||||
{
|
||||
__index = DATA.ERM
|
||||
}
|
||||
|
||||
setmetatable(ERM, ERM_MT)
|
||||
|
||||
local MACROS_MT =
|
||||
{
|
||||
__index = function(M, key)
|
||||
address = rawget(ERM.MDATA, key)
|
||||
assert(address, "Macro "..key.." is not defined")
|
||||
return ERM[address.name][address.index]
|
||||
end,
|
||||
__newindex = function(M, key, value)
|
||||
address = rawget(ERM.MDATA, key)
|
||||
assert(address, "Macro "..key.." is not defined")
|
||||
ERM[address.name][address.index] = value
|
||||
end
|
||||
}
|
||||
|
||||
setmetatable(ERM.M, MACROS_MT)
|
||||
|
||||
function ERM:addMacro(name, varName, varIndex)
|
||||
assert(varName == "v" or varName == "z", "Macro for "..varName.. "[...] is not allowed")
|
||||
rawset(self.MDATA, name, {name = varName, index = varIndex})
|
||||
end
|
||||
|
||||
function ERM:getZVar(p1)
|
||||
if type(p1) == "number" then
|
||||
return self.z[tostring(p1)]
|
||||
else
|
||||
return p1
|
||||
end
|
||||
end
|
||||
|
||||
local HERO_VAR_MT =
|
||||
{
|
||||
__index = function(t, key)
|
||||
ERM_D.WDATA = ERM_D.WDATA or {}
|
||||
local wKey = ERM_D.wKey
|
||||
|
||||
if not wKey then
|
||||
error("Hero variable set is not selected")
|
||||
end
|
||||
|
||||
ERM_D.WDATA[wKey] = ERM_D.WDATA[wKey] or {}
|
||||
|
||||
return ERM_D.WDATA[wKey][key] or 0
|
||||
end,
|
||||
__newindex = function(t, key, value)
|
||||
ERM_D.WDATA = ERM_D.WDATA or {}
|
||||
local wKey = ERM_D.wKey
|
||||
|
||||
if not wKey then
|
||||
error("Hero variable set is not selected")
|
||||
end
|
||||
|
||||
ERM_D.WDATA[wKey] = ERM_D.WDATA[wKey] or {}
|
||||
ERM_D.WDATA[wKey][key] = value
|
||||
end
|
||||
}
|
||||
|
||||
setmetatable(ERM.w, HERO_VAR_MT)
|
||||
|
||||
local function dailyUpdate(event)
|
||||
ERM.Q.c = GAME:getDate(0)
|
||||
end
|
||||
|
||||
table.insert(ERM.subs, TurnStarted.subscribeAfter(EVENT_BUS, dailyUpdate))
|
||||
|
||||
local Receivers = {}
|
||||
|
||||
local function createReceiverLoader(name)
|
||||
local loader = function(...)
|
||||
Receivers[name] = Receivers[name] or require("core:erm."..name)
|
||||
return Receivers[name]:new(ERM, ...)
|
||||
end
|
||||
return loader
|
||||
end
|
||||
|
||||
--[[
|
||||
|
||||
AR
|
||||
BA
|
||||
BF
|
||||
BG
|
||||
BH
|
||||
CA
|
||||
CD
|
||||
CE
|
||||
CM
|
||||
DL
|
||||
CO
|
||||
EA
|
||||
EX
|
||||
GE
|
||||
HL
|
||||
HO
|
||||
HT
|
||||
LE
|
||||
MO
|
||||
MR
|
||||
MW
|
||||
OB
|
||||
PM
|
||||
PO
|
||||
QW
|
||||
SS
|
||||
TL
|
||||
TR
|
||||
|
||||
]]
|
||||
|
||||
local supportedReceivers = {"BM", "BU", "DO", "FU", "HE", "IF", "DO", "MA", "MF", "OW", "TM", "UN", "VR"}
|
||||
|
||||
for _, v in pairs(supportedReceivers) do ERM[v] = createReceiverLoader(v) end
|
||||
|
||||
local Triggers = {}
|
||||
|
||||
local function createTriggerLoader(name)
|
||||
local loader = function(id_list, id_key)
|
||||
|
||||
local t = Triggers[id_key]
|
||||
|
||||
if not t then
|
||||
local p = require("core:erm."..name.."_T")
|
||||
t = p:new{ERM=ERM, id=id_list}
|
||||
Triggers[id_key] = t
|
||||
end
|
||||
|
||||
return t
|
||||
end
|
||||
return loader
|
||||
end
|
||||
|
||||
--[[
|
||||
!?AE
|
||||
!?BA
|
||||
!?BF
|
||||
!?BG
|
||||
!?BR
|
||||
!?CM client only?
|
||||
!?CO
|
||||
!?DL client only?
|
||||
!?GE
|
||||
!?HE
|
||||
!?HL
|
||||
!?HM
|
||||
!?LE (!$LE)
|
||||
!?MG
|
||||
!?MM client only?
|
||||
!?MR
|
||||
!?MW
|
||||
!?OB (!$OB)
|
||||
!?SN client only?
|
||||
!?SN (ERA)
|
||||
!?TH client only?
|
||||
!?TL client only? depends on time limit feature
|
||||
]]
|
||||
|
||||
local supportedTriggers = {"PI", "FU", "GM", "MF", "TM"}
|
||||
|
||||
local TriggerLoaders = {}
|
||||
|
||||
for _, v in pairs(supportedTriggers) do TriggerLoaders[v] = createTriggerLoader(v) end
|
||||
|
||||
local function makeIdKey(name, id_list)
|
||||
if id_list == nil then
|
||||
id_list = {}
|
||||
elseif type(id_list) ~= "table" then
|
||||
id_list = {id_list}
|
||||
end
|
||||
|
||||
return name .. table.concat(id_list, "|")
|
||||
end
|
||||
|
||||
function ERM:addTrigger(t)
|
||||
local name = t.name
|
||||
local fn = t.fn
|
||||
local id_list = t.id or {}
|
||||
|
||||
local id_key = makeIdKey(name, id_list)
|
||||
|
||||
local trigger = TriggerLoaders[name](id_list, id_key)
|
||||
|
||||
trigger:addHandler(fn)
|
||||
end
|
||||
|
||||
function ERM:callFunction(id, x)
|
||||
local id_key = makeIdKey("FU", id)
|
||||
|
||||
local t = Triggers[id_key]
|
||||
if t then
|
||||
t:call({}, x)
|
||||
end
|
||||
end
|
||||
|
||||
function ERM:callInstructions(cb)
|
||||
if not DATA.ERM.instructionsCalled then
|
||||
cb()
|
||||
local id_key = makeIdKey("PI")
|
||||
local pi = Triggers[id_key]
|
||||
if pi then
|
||||
pi:call()
|
||||
end
|
||||
DATA.ERM.instructionsCalled = true
|
||||
end
|
||||
end
|
||||
|
||||
return ERM
|
||||
104
scripts/lib/erm/BM.lua
Normal file
104
scripts/lib/erm/BM.lua
Normal file
@@ -0,0 +1,104 @@
|
||||
require("battle.Unit")
|
||||
|
||||
local battle = BATTLE
|
||||
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
|
||||
local BM = ReceiverBase:new()
|
||||
|
||||
function BM:new(ERM, unitId)
|
||||
assert(unitId ~= nil, "!!BM requires unit identifier")
|
||||
return ReceiverBase.new(self,{ERM = ERM, unitId = unitId})
|
||||
end
|
||||
|
||||
function BM:A(x, p1)
|
||||
if type(p1) == "nil" then
|
||||
local unit = battle:getUnitById(self.unitId)
|
||||
|
||||
return unit:getAttack(false)
|
||||
else
|
||||
error("!!BM:A set is not implemented")
|
||||
end
|
||||
end
|
||||
|
||||
function BM:B(x, ...)
|
||||
error("!!BM:B is not implemented")
|
||||
end
|
||||
|
||||
function BM:C(x, ...)
|
||||
error("!!BM:C is not implemented")
|
||||
end
|
||||
|
||||
function BM:D(x, ...)
|
||||
error("!!BM:D is not implemented")
|
||||
end
|
||||
|
||||
function BM:E(x, ...)
|
||||
error("!!BM:E is not implemented")
|
||||
end
|
||||
|
||||
function BM:F(x, ...)
|
||||
error("!!BM:F is not implemented")
|
||||
end
|
||||
|
||||
function BM:G(x, ...)
|
||||
error("!!BM:G is not implemented")
|
||||
end
|
||||
|
||||
function BM:H(x, ...)
|
||||
error("!!BM:H is not implemented")
|
||||
end
|
||||
|
||||
function BM:J(x, ...)
|
||||
error("!!BM:J is not implemented")
|
||||
end
|
||||
|
||||
function BM:K(x, ...)
|
||||
error("!!BM:K is not implemented")
|
||||
end
|
||||
|
||||
function BM:L(x, ...)
|
||||
error("!!BM:L is not implemented")
|
||||
end
|
||||
|
||||
function BM:M(x, ...)
|
||||
error("!!BM:M is not implemented")
|
||||
end
|
||||
|
||||
function BM:N(x, ...)
|
||||
error("!!BM:N is not implemented")
|
||||
end
|
||||
|
||||
function BM:O(x, ...)
|
||||
error("!!BM:O is not implemented")
|
||||
end
|
||||
|
||||
function BM:P(x, ...)
|
||||
error("!!BM:P is not implemented")
|
||||
end
|
||||
|
||||
function BM:Q(x, ...)
|
||||
error("!!BM:Q is not implemented")
|
||||
end
|
||||
|
||||
function BM:R(x, ...)
|
||||
error("!!BM:R is not implemented")
|
||||
end
|
||||
|
||||
function BM:S(x, ...)
|
||||
error("!!BM:S is not implemented")
|
||||
end
|
||||
|
||||
function BM:T(x, ...)
|
||||
error("!!BM:T is not implemented")
|
||||
end
|
||||
|
||||
function BM:U(x, ...)
|
||||
error("!!BM:U is not implemented")
|
||||
end
|
||||
|
||||
function BM:V(x, ...)
|
||||
error("!!BM:V is not implemented")
|
||||
end
|
||||
|
||||
return BM
|
||||
130
scripts/lib/erm/BU.lua
Normal file
130
scripts/lib/erm/BU.lua
Normal file
@@ -0,0 +1,130 @@
|
||||
require("battle.Unit")
|
||||
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
|
||||
local BU = ReceiverBase:new()
|
||||
|
||||
function BU:new(ERM)
|
||||
return ReceiverBase.new(self,{ERM = ERM})
|
||||
end
|
||||
|
||||
local BattleLogMessage = require("netpacks.BattleLogMessage")
|
||||
local BattleUnitsChanged = require("netpacks.BattleUnitsChanged")
|
||||
|
||||
local battle = BATTLE
|
||||
|
||||
function BU:C(x, p1)
|
||||
assert(type(p1) == "nil", "!!BU:C can only check value")
|
||||
|
||||
local ret = battle:isFinished()
|
||||
|
||||
if type(ret) == "nil" then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
function BU:D(x, hex, p1)
|
||||
assert(type(p1) == "nil", "!!BU:D can only check value")
|
||||
|
||||
local unit = battle:getUnitByPos(hex, false)
|
||||
|
||||
if unit then
|
||||
if unit:isAlive() then
|
||||
return nil, -2
|
||||
else
|
||||
return nil, unit:unitId()
|
||||
end
|
||||
else
|
||||
return nil, -1
|
||||
end
|
||||
end
|
||||
|
||||
function BU:E(x, hex, p1)
|
||||
assert(type(p1) == "nil", "!!BU:E can only check value")
|
||||
|
||||
local unit = battle:getUnitByPos(hex, false)
|
||||
|
||||
if unit and unit:isAlive() then
|
||||
return nil, unit:unitId()
|
||||
else
|
||||
return nil, -1
|
||||
end
|
||||
end
|
||||
|
||||
local SPECIAL_FIELDS = {}
|
||||
|
||||
SPECIAL_FIELDS[0] = 0
|
||||
SPECIAL_FIELDS[22] = 1
|
||||
SPECIAL_FIELDS[9] = 2
|
||||
SPECIAL_FIELDS[18] = 3
|
||||
SPECIAL_FIELDS[20] = 4
|
||||
SPECIAL_FIELDS[19] = 5
|
||||
SPECIAL_FIELDS[17] = 6
|
||||
SPECIAL_FIELDS[14] = 7
|
||||
SPECIAL_FIELDS[15] = 8
|
||||
SPECIAL_FIELDS[16] = 9
|
||||
|
||||
|
||||
function BU:G(x, p1)
|
||||
assert(type(p1) == "nil", "!!BU:G? is not implemented")
|
||||
|
||||
local bfield = SPECIAL_FIELDS[battle:getBattlefieldType()]
|
||||
|
||||
if bfield then
|
||||
return bfield
|
||||
else
|
||||
return -1
|
||||
end
|
||||
end
|
||||
|
||||
function BU:M(x, message)
|
||||
local pack = BattleLogMessage.new()
|
||||
pack:addText(message)
|
||||
SERVER:addToBattleLog(pack)
|
||||
end
|
||||
|
||||
function BU:O(x, ...)
|
||||
error("!!BU:O is not implemented")
|
||||
end
|
||||
|
||||
function BU:R(x, ...)
|
||||
error("!!BU:R is not implemented")
|
||||
end
|
||||
|
||||
function BU:S(x, typ, count, hex, side, slot)
|
||||
local pack = BattleUnitsChanged.new()
|
||||
|
||||
local id = battle:getNextUnitId()
|
||||
|
||||
pack:add(id,
|
||||
{
|
||||
newUnitInfo =
|
||||
{
|
||||
["count"] = count,
|
||||
["type"] = typ,
|
||||
["side"] = side,
|
||||
["position"] = hex,
|
||||
["summoned"] = (slot == -1),
|
||||
}
|
||||
})
|
||||
|
||||
SERVER:changeUnits(pack)
|
||||
end
|
||||
|
||||
function BU:T(x)
|
||||
local tacticDistance = battle:getTacticDistance()
|
||||
|
||||
if tacticDistance == 0 then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
function BU:V(x, ...)
|
||||
error("!!BU:V is not implemented")
|
||||
end
|
||||
|
||||
return BU
|
||||
55
scripts/lib/erm/DO.lua
Normal file
55
scripts/lib/erm/DO.lua
Normal file
@@ -0,0 +1,55 @@
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
|
||||
local DO = ReceiverBase:new()
|
||||
|
||||
function DO:new(ERM, fun, start, stop, inc)
|
||||
assert(fun and start and stop and inc, "!!DO requires 4 arguments")
|
||||
return ReceiverBase.new(self,
|
||||
{
|
||||
ERM=ERM,
|
||||
fun=fun,
|
||||
start=start,
|
||||
stop=stop,
|
||||
inc=inc,
|
||||
})
|
||||
end
|
||||
|
||||
function DO:P(x, ...)
|
||||
local argc = select('#', ...)
|
||||
local newx = {}
|
||||
local ret = {}
|
||||
|
||||
do
|
||||
local __iter, limit, step = tonumber(self.start), tonumber(self.stop), tonumber(self.inc)
|
||||
|
||||
while (step > 0 and __iter <= limit) or (step <= 0 and __iter >= limit) do
|
||||
|
||||
for idx = 1, argc do
|
||||
local v = select(idx, ...)
|
||||
|
||||
if v ~= nil then
|
||||
newx[tostring(idx)] = v
|
||||
end
|
||||
end
|
||||
|
||||
newx['16'] = __iter
|
||||
self.ERM:callFunction(self.fun, newx)
|
||||
__iter = newx['16']
|
||||
__iter = __iter + step
|
||||
end
|
||||
end
|
||||
|
||||
for idx = 1, argc do
|
||||
local v = select(idx, ...)
|
||||
if v == nil then
|
||||
ret[idx] = newx[tostring(idx)]
|
||||
else
|
||||
ret[idx] = nil
|
||||
end
|
||||
end
|
||||
|
||||
return unpack(ret)
|
||||
end
|
||||
|
||||
|
||||
return DO
|
||||
44
scripts/lib/erm/FU.lua
Normal file
44
scripts/lib/erm/FU.lua
Normal file
@@ -0,0 +1,44 @@
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
|
||||
local FU = ReceiverBase:new()
|
||||
|
||||
function FU:new(ERM, fun)
|
||||
assert(fun, "!!FU requires 1 argument")
|
||||
return ReceiverBase.new(self,
|
||||
{
|
||||
ERM=ERM,
|
||||
fun=fun,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
function FU:P(x, ...)
|
||||
local argc = select('#', ...)
|
||||
local newx = {}
|
||||
local ret = {}
|
||||
|
||||
for idx = 1, argc do
|
||||
local v = (select(idx, ...))
|
||||
idx = tostring(idx)
|
||||
|
||||
if v ~= nil then
|
||||
newx[idx] = v
|
||||
end
|
||||
end
|
||||
|
||||
self.ERM:callFunction(self.fun, newx)
|
||||
|
||||
for idx = 1, argc do
|
||||
local v = (select(idx, ...))
|
||||
if v == nil then
|
||||
ret[idx] = newx[tostring(idx)]
|
||||
else
|
||||
ret[idx] = nil
|
||||
end
|
||||
end
|
||||
|
||||
return unpack(ret)
|
||||
end
|
||||
|
||||
|
||||
return FU
|
||||
15
scripts/lib/erm/FU_T.lua
Normal file
15
scripts/lib/erm/FU_T.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
local TriggerBase = require("core:erm.TriggerBase")
|
||||
local _
|
||||
|
||||
local FU = TriggerBase:new()
|
||||
|
||||
function FU:call(p1, x)
|
||||
self.ERM.activeEvent = nil
|
||||
self.ERM.activeTrigger = self
|
||||
for _, fn in ipairs(self.fn) do
|
||||
fn(self.e, self.y, x)
|
||||
end
|
||||
self.ERM.activeTrigger = nil
|
||||
end
|
||||
|
||||
return FU
|
||||
21
scripts/lib/erm/GM_T.lua
Normal file
21
scripts/lib/erm/GM_T.lua
Normal file
@@ -0,0 +1,21 @@
|
||||
local TriggerBase = require("core:erm.TriggerBase")
|
||||
local GameResumed = require("events.GameResumed")
|
||||
|
||||
local trigger = TriggerBase:new()
|
||||
|
||||
function trigger:new(o)
|
||||
o = TriggerBase.new(self, o)
|
||||
|
||||
local id1 = o.id[1]
|
||||
|
||||
if id1 == 0 or id1 == "0" then
|
||||
o.sub = GameResumed.subscribeAfter(EVENT_BUS, function(event)
|
||||
o:call(event)
|
||||
end)
|
||||
else
|
||||
error ("Identifier "..tostring(id1) .. " not supported by !?GM")
|
||||
end
|
||||
return o
|
||||
end
|
||||
|
||||
return trigger
|
||||
181
scripts/lib/erm/HE.lua
Normal file
181
scripts/lib/erm/HE.lua
Normal file
@@ -0,0 +1,181 @@
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
|
||||
local HE = ReceiverBase:new()
|
||||
|
||||
function HE:new(ERM, p1, p2, p3)
|
||||
assert(p1 ~= nil, "!!HE requires hero identifier")
|
||||
|
||||
if p2 and p3 then
|
||||
--by coordinates
|
||||
error("!!HEx/y/l: form is not implemented")
|
||||
else
|
||||
-- assume p1 is identifier
|
||||
local hero = GAME:getHeroWithSubid(p1)
|
||||
|
||||
if not hero then
|
||||
logError("Hero with id ".. tostring(p1) .. " not found")
|
||||
end
|
||||
|
||||
return ReceiverBase.new(self,
|
||||
{
|
||||
id=p1,
|
||||
ERM=ERM,
|
||||
hero=hero
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function HE:A(x, ...)
|
||||
--artifacts
|
||||
logError("!!HE:A is not implemented")
|
||||
end
|
||||
|
||||
HE:p1Dispatcher("B")
|
||||
|
||||
function HE:B0(x, ...)
|
||||
--name
|
||||
logError("!!HE:B is not implemented")
|
||||
end
|
||||
|
||||
function HE:B1(x, ...)
|
||||
--bio
|
||||
logError("!!HE:B is not implemented")
|
||||
end
|
||||
|
||||
function HE:B2(x, ...)
|
||||
--class
|
||||
logError("!!HE:B is not implemented")
|
||||
end
|
||||
|
||||
function HE:B3(x, _)
|
||||
--get default bio
|
||||
logError("!!HE:B is not implemented")
|
||||
end
|
||||
|
||||
function HE:C(x, ...)
|
||||
|
||||
local argc = select("#", ...)
|
||||
|
||||
if argc == 14 then
|
||||
return self:C14(x, ...)
|
||||
elseif argc == 4 then
|
||||
local N = select(1, ...)
|
||||
|
||||
return nil, self["C"..tostring(N)](self, x, select(2, ...))
|
||||
else
|
||||
logError("!!HE:C extended form is not implemented")
|
||||
end
|
||||
end
|
||||
|
||||
function HE:C0(x, slot, typ, count)
|
||||
--change creatures by slot
|
||||
|
||||
if typ~=nil or count ~=nil then
|
||||
logError("!!HE:C0 set is not implemented")
|
||||
return
|
||||
end
|
||||
|
||||
local stack = self.hero:getStack(slot)
|
||||
|
||||
if not stack then
|
||||
return nil, -1, 0
|
||||
else
|
||||
return nil, stack:getType():getIndex(), stack:getCount()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function HE:C1(x, ...)
|
||||
--change creatures by type
|
||||
end
|
||||
|
||||
function HE:C05(x, ...)
|
||||
--change creatures by slot adv
|
||||
end
|
||||
|
||||
function HE:C15(x, ...)
|
||||
--change creatures by type adv
|
||||
end
|
||||
|
||||
|
||||
function HE:C2(x, ...)
|
||||
--add creatures
|
||||
end
|
||||
|
||||
function HE:C14(x, ...)
|
||||
-- change creatures with Query
|
||||
end
|
||||
|
||||
function HE:D(x, ...)
|
||||
logError("!!HE:D not implemented")
|
||||
end
|
||||
function HE:E(x, ...)
|
||||
logError("!!HE:E is not implemented")
|
||||
end
|
||||
function HE:F(x, ...)
|
||||
logError("!!HE:F is not implemented")
|
||||
end
|
||||
function HE:G(x, ...)
|
||||
logError("!!HE:G is not implemented")
|
||||
end
|
||||
function HE:H(x, ...)
|
||||
logError("!!HE:H is not implemented")
|
||||
end
|
||||
function HE:I(x, ...)
|
||||
logError("!!HE:I is not implemented")
|
||||
end
|
||||
function HE:K(x, ...)
|
||||
logError("!!HE:K is not implemented")
|
||||
end
|
||||
function HE:L(x, ...)
|
||||
logError("!!HE:L is not implemented")
|
||||
end
|
||||
function HE:M(x, ...)
|
||||
logError("!!HE:M is not implemented")
|
||||
end
|
||||
function HE:N(x, ...)
|
||||
logError("!!HE:N is not implemented")
|
||||
end
|
||||
|
||||
function HE:O(x, owner)
|
||||
if owner~=nil then
|
||||
logError("!!HE:O set is not implemented")
|
||||
return
|
||||
else
|
||||
if not self.hero then
|
||||
return 255
|
||||
end
|
||||
return self.hero:getOwner()
|
||||
end
|
||||
end
|
||||
|
||||
function HE:P(x, ...)
|
||||
logError("!!HE:P is not implemented")
|
||||
end
|
||||
function HE:R(x, ...)
|
||||
logError("!!HE:R is not implemented")
|
||||
end
|
||||
function HE:S(x, ...)
|
||||
logError("!!HE:S is not implemented")
|
||||
end
|
||||
function HE:T(x, ...)
|
||||
logError("!!HE:T is not implemented")
|
||||
end
|
||||
function HE:U(x, ...)
|
||||
logError("!!HE:U is not implemented")
|
||||
end
|
||||
function HE:V(x, ...)
|
||||
logError("!!HE:V is not implemented")
|
||||
end
|
||||
function HE:W(x, ...)
|
||||
logError("!!HE:A is not implemented")
|
||||
end
|
||||
function HE:X(x, ...)
|
||||
logError("!!HE:X is not implemented")
|
||||
end
|
||||
function HE:Y(x, ...)
|
||||
logError("!!HE:Y is ot implemented")
|
||||
end
|
||||
|
||||
|
||||
return HE
|
||||
85
scripts/lib/erm/IF.lua
Normal file
85
scripts/lib/erm/IF.lua
Normal file
@@ -0,0 +1,85 @@
|
||||
local InfoWindow = require("netpacks.InfoWindow")
|
||||
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
|
||||
local IF = ReceiverBase:new()
|
||||
|
||||
function IF:new(ERM)
|
||||
return ReceiverBase.new(self,{ERM = ERM})
|
||||
end
|
||||
|
||||
--[[
|
||||
according to the ERM help:
|
||||
"%%" -> "%"
|
||||
"%F#" -> current value of # flag.
|
||||
"%Vf"..."%Vt" -> current value of corresponding variable.
|
||||
"%W1"..."%W100" -> current value of corresponding hero variable.
|
||||
"%X1"..."%X16" -> current value of corresponding function parameter.
|
||||
"%Y1"..."%Y100" -> current value of corresponding local variable.
|
||||
"%Z1"..."%Z500" -> current value of corresponding string variable.
|
||||
"%$macro$" -> macro name of corresponding variable
|
||||
"%Dd" -> current day of week
|
||||
"%Dw" -> current week
|
||||
"%Dm" -> current month
|
||||
"%Da" -> current day from beginning of the game
|
||||
"%Gc" -> the color of current gamer in text
|
||||
]]
|
||||
|
||||
function IF:M(x, message)
|
||||
local pack = InfoWindow.new()
|
||||
pack:setPlayer(GAME:getCurrentPlayer())
|
||||
|
||||
local _self = self
|
||||
|
||||
local onMatch1 = function (key1, key2)
|
||||
|
||||
if key1 == 'X' then
|
||||
return x[key2]
|
||||
end
|
||||
|
||||
if key1 == 'V' then
|
||||
return _self.ERM.v[key2]
|
||||
end
|
||||
|
||||
if key1 == 'Z' then
|
||||
return _self.ERM.z[key2]
|
||||
end
|
||||
|
||||
if key1 == 'F' then
|
||||
return _self.ERM.F[key2]
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local onMatch2 = function (key1, key2)
|
||||
|
||||
if key1 == 'V' then
|
||||
return _self.ERM.Q[key2]
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
message = string.gsub(message, "%%([FVWXYZ])([%d]+)", onMatch1)
|
||||
message = string.gsub(message, "%%(V)(%w)", onMatch2)
|
||||
|
||||
message = string.gsub(message, "(%%)(%%)", "%1")
|
||||
|
||||
pack:addText(message)
|
||||
|
||||
SERVER:commitPackage(pack)
|
||||
end
|
||||
|
||||
function IF:W(x, heroIndex)
|
||||
if heroIndex == nil then
|
||||
return tonumber(DATA.ERM.wKey or -2)
|
||||
else
|
||||
if heroIndex == -1 then
|
||||
error("IF:W-1 not implemented")
|
||||
end
|
||||
DATA.ERM.wKey = tostring(heroIndex)
|
||||
end
|
||||
end
|
||||
|
||||
return IF
|
||||
233
scripts/lib/erm/MA.lua
Normal file
233
scripts/lib/erm/MA.lua
Normal file
@@ -0,0 +1,233 @@
|
||||
local logError = logError
|
||||
local bit = bit
|
||||
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
local Metatype = require ("core:Metatype")
|
||||
local EntitiesChanged = require("netpacks.EntitiesChanged")
|
||||
local Bonus = require("Bonus")
|
||||
local BonusBearer = require("BonusBearer")
|
||||
local BonusList = require("BonusList")
|
||||
|
||||
local RES = {[0] = "wood", [1] = "mercury", [2] = "ore", [3] = "sulfur", [4] = "crystal", [5] = "gems", [6] = "gold", [7] = "mithril"}
|
||||
|
||||
local SERVICES = SERVICES
|
||||
local creatures = SERVICES:creatures()
|
||||
local SERVER = SERVER
|
||||
|
||||
local getCreatureByIndex = creatures.getByIndex
|
||||
local function creatureByIndex(index)
|
||||
return getCreatureByIndex(creatures, index)
|
||||
end
|
||||
|
||||
local function sendChanges(creatureIndex, data)
|
||||
local pack = EntitiesChanged.new()
|
||||
pack:update(Metatype.CREATURE, creatureIndex, data)
|
||||
SERVER:commitPackage(pack)
|
||||
end
|
||||
|
||||
local MA = ReceiverBase:new()
|
||||
|
||||
function MA:new(ERM)
|
||||
return ReceiverBase.new(self,{ERM = ERM})
|
||||
end
|
||||
|
||||
local function checkCreatureIndex(creatureIndex)
|
||||
assert(creatureIndex ~= nil, "!!MA requires creature identifier")
|
||||
|
||||
if type(creatureIndex) == "string" then
|
||||
error("Identifier resolving is not implemented")
|
||||
end
|
||||
|
||||
return creatureIndex
|
||||
end
|
||||
|
||||
local function createModifier(scope, jsonKey, getterKey)
|
||||
|
||||
local f = function (self, x, creatureIndex, p1)
|
||||
local creatureIndex = checkCreatureIndex(creatureIndex)
|
||||
|
||||
if p1 == nil then
|
||||
local creature = creatureByIndex(creatureIndex)
|
||||
return nil, creature[getterKey](creature)
|
||||
else
|
||||
local packData = {config = {}}
|
||||
|
||||
local config = packData.config
|
||||
|
||||
for _, v in ipairs(scope) do
|
||||
config[v] = {}
|
||||
config = config[v]
|
||||
end
|
||||
|
||||
config[jsonKey] = p1
|
||||
|
||||
sendChanges(creatureIndex, packData)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return f
|
||||
end
|
||||
|
||||
MA.A = createModifier({}, "attack", "getBaseAttack")
|
||||
MA.B = createModifier({}, "spellPoints" ,"getBaseSpellPoints")
|
||||
MA.D = createModifier({}, "defense" ,"getBaseDefense")
|
||||
MA.E = createModifier({"damage"}, "max", "getBaseDamageMax")
|
||||
MA.F = createModifier({}, "fightValue" ,"getFightValue")
|
||||
MA.G = createModifier({}, "growth" ,"getGrowth")
|
||||
MA.H = createModifier({"advMapAmount"}, "max" ,"getAdvMapAmountMax")
|
||||
MA.I = createModifier({}, "aiValue" ,"getAIValue")
|
||||
MA.L = createModifier({}, "level" , "getLevel")
|
||||
MA.M = createModifier({"damage"}, "min", "getBaseDamageMin")
|
||||
MA.N = createModifier({}, "shots" , "getBaseShots")
|
||||
MA.O = createModifier({}, "faction" ,"getFactionIndex")
|
||||
MA.P = createModifier({}, "hitPoints" ,"getBaseHitPoints")
|
||||
MA.R = createModifier({}, "horde" , "getHorde")
|
||||
MA.S = createModifier({}, "speed" , "getBaseSpeed")
|
||||
MA.V = createModifier({"advMapAmount"}, "min","getAdvMapAmountMin")
|
||||
|
||||
function MA:C(x, creatureIndex, resIndex, cost)
|
||||
local creatureIndex = checkCreatureIndex(creatureIndex)
|
||||
|
||||
if cost == nil then
|
||||
local creature = creatureByIndex(creatureIndex)
|
||||
return nil, nil, creature:getCost(resIndex)
|
||||
else
|
||||
local packData = {config = {cost = {[RES[resIndex]] = cost}}}
|
||||
sendChanges(creatureIndex, packData)
|
||||
end
|
||||
end
|
||||
|
||||
function MA:U(x, creatureIndex, upgradeIndex)
|
||||
-- -2 - no upgrade
|
||||
-- -1 - usual upgrade
|
||||
logError("!!MA:U is not implemented")
|
||||
end
|
||||
|
||||
local FLAG_NAMES =
|
||||
{
|
||||
--[1] = "doubleWide", --0
|
||||
[2] = "FLYING", --1
|
||||
[4] = "SHOOTER", --2
|
||||
[8] = "TWO_HEX_ATTACK_BREATH", --3
|
||||
-- [16] = "alive", --4
|
||||
[32] = "CATAPULT", --5
|
||||
[64] = "SIEGE_WEAPON", --6
|
||||
[128] = "KING1", --7
|
||||
[256] = "KING2", --8
|
||||
[512] = "KING3", --9
|
||||
[1024] = "MIND_IMMUNITY", --10
|
||||
--[2048] = "laser shot", --11
|
||||
[4096] = "NO_MELEE_PENALTY", --12
|
||||
-- [8192] - unused --13
|
||||
[16384] = "FIRE_IMMUNITY", --14
|
||||
[32768] = "ADDITIONAL_ATTACK", -- val=1 --15
|
||||
[65536] = "NO_RETALIATION", --16
|
||||
[131072] = "NO_MORALE", --17
|
||||
[262144] = "UNDEAD", --18
|
||||
[524288] = "ATTACKS_ALL_ADJACENT", --19
|
||||
-- [1048576] - AOE spell-like attack --20
|
||||
-- [2097152] - war machine? --21
|
||||
-- [4194304] = "summoned", --22
|
||||
-- [8388608] = "cloned", --23
|
||||
}
|
||||
|
||||
local FLAGS = {}
|
||||
|
||||
for k, v in pairs(FLAG_NAMES) do
|
||||
local bonusType = Bonus[v]
|
||||
assert(bonusType ~= nil, "Invalid Bonus type: "..v)
|
||||
FLAGS[k] = bonusType
|
||||
end
|
||||
|
||||
local FLAGS_REV = {}
|
||||
|
||||
for mask, bonusType in pairs(FLAGS) do
|
||||
FLAGS_REV[bonusType] = mask
|
||||
end
|
||||
|
||||
function MA:X(x, creatureIndex, flagsMask)
|
||||
local creatureIndex = checkCreatureIndex(creatureIndex)
|
||||
local creature = creatureByIndex(creatureIndex)
|
||||
local creatureBonuses = creature:accessBonuses()
|
||||
local all = creatureBonuses:getBonuses()
|
||||
|
||||
local currentMask = 0
|
||||
|
||||
local toRemove = {}
|
||||
|
||||
do
|
||||
local idx = 1
|
||||
local bonus = all[idx]
|
||||
|
||||
while bonus do
|
||||
local bonusType = bonus:getType()
|
||||
local mask = FLAGS_REV[bonusType]
|
||||
|
||||
if mask ~= nil then
|
||||
if flagsMask ~= nil then
|
||||
if bit.band(mask, flagsMask) == 0 then
|
||||
table.insert(toRemove, bonus:toJsonNode())
|
||||
end
|
||||
end
|
||||
currentMask = bit.bor(currentMask, mask)
|
||||
end
|
||||
|
||||
|
||||
idx = idx + 1
|
||||
bonus = all[idx]
|
||||
end
|
||||
end
|
||||
|
||||
if flagsMask == nil then
|
||||
local ret = currentMask
|
||||
|
||||
if creature:isDoubleWide() then
|
||||
ret = bit.bor(ret, 1)
|
||||
end
|
||||
|
||||
return nil, ret
|
||||
else
|
||||
flagsMask = tonumber(flagsMask)
|
||||
|
||||
local hasChanges = false
|
||||
local packData = {config = {}}
|
||||
|
||||
local reqDoubleWide = bit.band(flagsMask, 1) > 0
|
||||
|
||||
if reqDoubleWide ~= creature:isDoubleWide() then
|
||||
packData.config.doubleWide, hasChanges = reqDoubleWide, true
|
||||
end
|
||||
|
||||
local toAdd = {}
|
||||
|
||||
for mask, name in pairs(FLAG_NAMES) do
|
||||
if (bit.band(currentMask, mask) == 0) and (bit.band(flagsMask, mask) ~= 0) then
|
||||
local bonus =
|
||||
{
|
||||
duration = {[1] = "PERMANENT"},
|
||||
source = "CREATURE_ABILITY",
|
||||
type = name
|
||||
}
|
||||
|
||||
--special case
|
||||
if name == "ADDITIONAL_ATTACK" then
|
||||
bonus.val = 1
|
||||
end
|
||||
|
||||
table.insert(toAdd, bonus)
|
||||
end
|
||||
end
|
||||
|
||||
hasChanges = hasChanges or #toAdd > 0 or #toRemove > 0
|
||||
|
||||
if hasChanges then
|
||||
packData.bonuses = {toAdd = toAdd, toRemove = toRemove}
|
||||
sendChanges(creatureIndex, packData)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return MA
|
||||
36
scripts/lib/erm/MF.lua
Normal file
36
scripts/lib/erm/MF.lua
Normal file
@@ -0,0 +1,36 @@
|
||||
require("battle.Unit")
|
||||
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
|
||||
local MF = ReceiverBase:new()
|
||||
|
||||
function MF:new(ERM)
|
||||
return ReceiverBase.new(self,{ERM = ERM})
|
||||
end
|
||||
|
||||
function MF:D(x)
|
||||
return self.ERM.activeEvent:getInitalDamage()
|
||||
end
|
||||
|
||||
function MF:E(x, ...)
|
||||
error("!!MF:E is not implemented")
|
||||
end
|
||||
|
||||
function MF:F(x, p1)
|
||||
if p1 then
|
||||
self.ERM.activeEvent:setDamage(p1)
|
||||
return nil
|
||||
else
|
||||
return self.ERM.activeEvent:getDamage()
|
||||
end
|
||||
end
|
||||
|
||||
function MF:N(x)
|
||||
return self.ERM.activeEvent:getTarget():unitId()
|
||||
end
|
||||
|
||||
function MF:W(x, ...)
|
||||
error("!!MF:W is not implemented")
|
||||
end
|
||||
|
||||
return MF
|
||||
15
scripts/lib/erm/MF_T.lua
Normal file
15
scripts/lib/erm/MF_T.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
local TriggerBase = require("core:erm.TriggerBase")
|
||||
local ApplyDamage = require("events.ApplyDamage")
|
||||
local eventBus = EVENT_BUS;
|
||||
|
||||
local trigger = TriggerBase:new()
|
||||
|
||||
function trigger:new(o)
|
||||
o = TriggerBase.new(self, o)
|
||||
o.sub = ApplyDamage.subscribeBefore(EVENT_BUS, function(event)
|
||||
o:call(event)
|
||||
end)
|
||||
return o
|
||||
end
|
||||
|
||||
return trigger
|
||||
109
scripts/lib/erm/OW.lua
Normal file
109
scripts/lib/erm/OW.lua
Normal file
@@ -0,0 +1,109 @@
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
local GAME = GAME
|
||||
local SetResources = require("netpacks.SetResources")
|
||||
local SERVER = SERVER
|
||||
|
||||
local OW = ReceiverBase:new()
|
||||
|
||||
function OW:new(ERM)
|
||||
return ReceiverBase.new(self,{ERM = ERM})
|
||||
end
|
||||
|
||||
function OW:A(x, ...)
|
||||
logError("!!OW:A is not implemented")
|
||||
end
|
||||
|
||||
function OW:C(x, p)
|
||||
if p1 == nil then
|
||||
return GAME:getCurrentPlayer()
|
||||
else
|
||||
error("Setting current player in not allowed")
|
||||
end
|
||||
end
|
||||
|
||||
function OW:D(x, ...)
|
||||
logError("!!OW:D is not implemented")
|
||||
end
|
||||
|
||||
function OW:G(x, ...)
|
||||
logError("!!OW:G is not implemented")
|
||||
end
|
||||
|
||||
function OW:H(x, ...)
|
||||
logError("!!OW:H is not implemented")
|
||||
end
|
||||
|
||||
function OW:I(x, ...)
|
||||
logError("!!OW:I is not implemented")
|
||||
end
|
||||
|
||||
function OW:K(x, ...)
|
||||
logError("!!OW:K is not implemented")
|
||||
end
|
||||
function OW:N(x, ...)
|
||||
logError("!!OW:N is not implemented")
|
||||
end
|
||||
|
||||
function OW:O(x, ...)
|
||||
logError("!!OW:O is not implemented")
|
||||
end
|
||||
|
||||
function OW:R(x, player, typ, quantity)
|
||||
local p = GAME:getPlayer(player)
|
||||
assert(p, "Player ".. tostring(player).." not accessible")
|
||||
|
||||
if quantity == nil then
|
||||
return nil, nil, p:getResourceAmount(typ)
|
||||
elseif type(quantity) == "table" then
|
||||
local hasD = false
|
||||
|
||||
local s = ''
|
||||
|
||||
local v
|
||||
|
||||
for n, k in ipairs(quantity) do
|
||||
if k == "d" then
|
||||
hasD = true
|
||||
else
|
||||
v = k
|
||||
end
|
||||
end
|
||||
|
||||
if not hasD then
|
||||
error ("OW:R Special variable expected ".. v .." found")
|
||||
end
|
||||
|
||||
if type(v) ~= "number" then
|
||||
error ("OW:R Numeric quantity expected".. tostring(v) .." found")
|
||||
end
|
||||
|
||||
quantity = v
|
||||
|
||||
local pack = SetResources.new()
|
||||
pack:setPlayer(player)
|
||||
pack:setAbs(false)
|
||||
pack:setAmount(typ, quantity)
|
||||
SERVER:commitPackage(pack)
|
||||
else
|
||||
local pack = SetResources.new()
|
||||
pack:setPlayer(player)
|
||||
pack:setAbs(true)
|
||||
pack:setAmount(typ, quantity)
|
||||
SERVER:commitPackage(pack)
|
||||
end
|
||||
end
|
||||
function OW:S(x, ...)
|
||||
logError("!!OW:S is not implemented")
|
||||
end
|
||||
function OW:T(x, ...)
|
||||
logError("!!OW:T is not implemented")
|
||||
end
|
||||
function OW:V(x, ...)
|
||||
logError("!!OW:V is not implemented")
|
||||
end
|
||||
function OW:W(x, ...)
|
||||
logError("!!OW:W is not implemented")
|
||||
end
|
||||
|
||||
|
||||
return OW
|
||||
5
scripts/lib/erm/PI_T.lua
Normal file
5
scripts/lib/erm/PI_T.lua
Normal file
@@ -0,0 +1,5 @@
|
||||
local TriggerBase = require("core:erm.TriggerBase")
|
||||
|
||||
local trigger = TriggerBase:new()
|
||||
|
||||
return trigger
|
||||
18
scripts/lib/erm/ReceiverBase.lua
Normal file
18
scripts/lib/erm/ReceiverBase.lua
Normal file
@@ -0,0 +1,18 @@
|
||||
local ReceiverBase = {}
|
||||
|
||||
function ReceiverBase:new(o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function ReceiverBase:p1Dispatcher(code)
|
||||
self[code] = function(self, x, ...)
|
||||
local N = select(1, ...)
|
||||
return nil, self[code..tostring(N)](self, x, select(2, ...))
|
||||
end
|
||||
end
|
||||
|
||||
return ReceiverBase
|
||||
|
||||
57
scripts/lib/erm/TM.lua
Normal file
57
scripts/lib/erm/TM.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
local TM = ReceiverBase:new()
|
||||
local bit = bit
|
||||
local band, bor, bxor = bit.band, bit.bor, bit.bxor
|
||||
local ALL_PLAYERS = 0xFF
|
||||
|
||||
local DATA = DATA
|
||||
DATA.ERM.timers = DATA.ERM.timers or {}
|
||||
|
||||
local timers = DATA.ERM.timers
|
||||
|
||||
local function newTimer(timerId)
|
||||
return
|
||||
{
|
||||
id = timerId,
|
||||
dayFirst = 1,
|
||||
dayLast = 1,
|
||||
interval = 0,
|
||||
players = 0
|
||||
}
|
||||
end
|
||||
|
||||
local function getTimer(timerId)
|
||||
timerId = tostring(timerId)
|
||||
timers[timerId] = timers[timerId] or newTimer(timerId)
|
||||
return timers[timerId]
|
||||
end
|
||||
|
||||
function TM:new(ERM, timerId)
|
||||
assert(timerId ~= nil, "!!TM requires timer identifier")
|
||||
return ReceiverBase.new(self,
|
||||
{
|
||||
timerId = timerId,
|
||||
timer = getTimer(timerId),
|
||||
ERM = ERM
|
||||
})
|
||||
end
|
||||
|
||||
function TM:D(x, playerMask)
|
||||
--disable by mask
|
||||
self.timer.players = band(self.timer.players, bnot(playerMask), ALL_PLAYERS)
|
||||
end
|
||||
|
||||
function TM:E(x, playerMask)
|
||||
--enable by mask
|
||||
self.timer.players = bor(self.timer.players, playerMask)
|
||||
end
|
||||
|
||||
function TM:S(x, dayFirst, dayLast, interval, playerMask)
|
||||
local t = self.timer
|
||||
t.dayFirst = dayFirst
|
||||
t.dayLast = dayLast
|
||||
t.interval = interval
|
||||
t.players = playerMask
|
||||
end
|
||||
|
||||
return TM
|
||||
51
scripts/lib/erm/TM_T.lua
Normal file
51
scripts/lib/erm/TM_T.lua
Normal file
@@ -0,0 +1,51 @@
|
||||
local TriggerBase = require("core:erm.TriggerBase")
|
||||
local PlayerGotTurn = require("events.PlayerGotTurn")
|
||||
local bit = bit
|
||||
local band, bor = bit.band, bit.bor
|
||||
local lshift, rshift = bit.lshift, bit.rshift
|
||||
|
||||
local TM_T = TriggerBase:new()
|
||||
|
||||
function TM_T:new(o)
|
||||
o = TriggerBase.new(self, o)
|
||||
o.sub = PlayerGotTurn.subscribeAfter(EVENT_BUS, function(event)
|
||||
o:update(event)
|
||||
end)
|
||||
o.timerId = tostring(o.id[1])
|
||||
return o
|
||||
end
|
||||
|
||||
function TM_T:update(event)
|
||||
local timerData = DATA.ERM.timers[self.timerId]
|
||||
if not timerData then
|
||||
error("Timer" ..self.timerId .." is not set")
|
||||
end
|
||||
|
||||
local today = self.ERM.Q.c -- this assumes that ERM updates before this
|
||||
local activePlayer = event:getPlayer()
|
||||
|
||||
assert(type(activePlayer) == "number")
|
||||
|
||||
--TODO: special player constants
|
||||
|
||||
if activePlayer < 0 or activePlayer > 8 then
|
||||
return
|
||||
end
|
||||
|
||||
if band(timerData.players, lshift(1, activePlayer)) == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
if (today < timerData.dayFirst) or ((today > timerData.dayLast) and (timerData.dayLast ~= 0)) then
|
||||
return
|
||||
end
|
||||
--
|
||||
local d = today - timerData.dayFirst
|
||||
--
|
||||
if d % timerData.interval == 0 then
|
||||
self:call(event)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return TM_T
|
||||
37
scripts/lib/erm/TriggerBase.lua
Normal file
37
scripts/lib/erm/TriggerBase.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
local TriggerBase = {}
|
||||
|
||||
function TriggerBase:new(o)
|
||||
o = o or {}
|
||||
o.id = o.id or {}
|
||||
o.fn = o.fn or {}
|
||||
o.y = o.y or {}
|
||||
o.e = o.e or {}
|
||||
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function TriggerBase:call(event)
|
||||
self.ERM.activeEvent = event
|
||||
self.ERM.activeTrigger = self
|
||||
for _, fn in ipairs(self.fn) do
|
||||
fn(self.e, self.y)
|
||||
end
|
||||
self.ERM.activeTrigger = nil
|
||||
self.ERM.activeEvent = nil
|
||||
end
|
||||
|
||||
function TriggerBase:addHandler(fn)
|
||||
table.insert(self.fn, fn)
|
||||
end
|
||||
|
||||
function TriggerBase:checkSub(sub, sub_name)
|
||||
if type(sub) == "string" then
|
||||
error(sub_name .. " subscription failed: "..sub)
|
||||
elseif type(sub) ~= "userdata" then
|
||||
error(sub_name .. " subscription failed")
|
||||
end
|
||||
end
|
||||
|
||||
return TriggerBase
|
||||
58
scripts/lib/erm/UN.lua
Normal file
58
scripts/lib/erm/UN.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
local Metatype = require ("core:Metatype")
|
||||
local EntitiesChanged = require("netpacks.EntitiesChanged")
|
||||
|
||||
local SERVICES = SERVICES
|
||||
local creatures = SERVICES:creatures()
|
||||
local SERVER = SERVER
|
||||
|
||||
|
||||
local function sendChanges(metatype, index, data)
|
||||
local pack = EntitiesChanged.new()
|
||||
pack:update(metatype, index, data)
|
||||
SERVER:commitPackage(pack)
|
||||
end
|
||||
|
||||
local UN = ReceiverBase:new()
|
||||
|
||||
function UN:new(ERM)
|
||||
return ReceiverBase.new(self,{ERM=ERM})
|
||||
end
|
||||
|
||||
UN:p1Dispatcher("G")
|
||||
|
||||
function UN:G0(x, ...)
|
||||
error ("UN:G0 is not implemented")
|
||||
end
|
||||
|
||||
function UN:G1(x, creatureIndex, N, v)
|
||||
return nil, nil, (self["G1"..tostring(N)](self, x, creatureIndex, v))
|
||||
end
|
||||
|
||||
function UN:G10(x, creatureIndex, v)
|
||||
if v == nil then
|
||||
return creatures:getByIndex(creatureIndex):getSingularName()
|
||||
else
|
||||
v = self.ERM:getZVar(v)
|
||||
local packData = {config = {name={singular=v}}}
|
||||
sendChanges(Metatype.CREATURE, creatureIndex, packData)
|
||||
end
|
||||
end
|
||||
function UN:G11(x, creatureIndex, v)
|
||||
if v == nil then
|
||||
return creatures:getByIndex(creatureIndex):getPluralName()
|
||||
else
|
||||
v = self.ERM:getZVar(v)
|
||||
local packData = {config = {name={plural=v}}}
|
||||
sendChanges(Metatype.CREATURE, creatureIndex, packData)
|
||||
end
|
||||
end
|
||||
function UN:G12(x, creatureIndex, v)
|
||||
error ("UN:G1/*/2/* is not implemented")
|
||||
end
|
||||
|
||||
function UN:G2(x, ...)
|
||||
error ("UN:G2 is not implemented")
|
||||
end
|
||||
|
||||
return UN
|
||||
22
scripts/lib/erm/VR.lua
Normal file
22
scripts/lib/erm/VR.lua
Normal file
@@ -0,0 +1,22 @@
|
||||
local ReceiverBase = require("core:erm.ReceiverBase")
|
||||
|
||||
local VR = ReceiverBase:new()
|
||||
|
||||
function VR:new(ERM, v)
|
||||
assert(v ~= nil, "!!VR requires variable identifier")
|
||||
return ReceiverBase.new(self, {v=v, ERM=ERM})
|
||||
end
|
||||
|
||||
local match = string.match
|
||||
|
||||
local function trim(s)
|
||||
return match(s,'^()%s*$') and '' or match(s,'^%s*(.*%S)')
|
||||
end
|
||||
|
||||
function VR:H(flagIndex)
|
||||
local v = trim(self.v)
|
||||
self.ERM.F[flagIndex] = v ~= ''
|
||||
end
|
||||
|
||||
|
||||
return VR
|
||||
375
scripts/lib/verm.lua
Normal file
375
scripts/lib/verm.lua
Normal file
@@ -0,0 +1,375 @@
|
||||
|
||||
local _G=_G
|
||||
local ipairs=ipairs
|
||||
local select=select
|
||||
local pairs=pairs
|
||||
local type = type
|
||||
local unpack=unpack
|
||||
local logError = logError
|
||||
|
||||
local DATA = DATA
|
||||
|
||||
--/////////////////////////
|
||||
|
||||
local function table_print (tt, done)
|
||||
done = done or {}
|
||||
if type(tt) == "table" then
|
||||
local sb = {}
|
||||
table.insert(sb, "{");
|
||||
for key, value in pairs (tt) do
|
||||
if type (value) == "table" and not done [value] then
|
||||
done [value] = true
|
||||
table.insert(sb, key .. ":{");
|
||||
table.insert(sb, table_print (value, done))
|
||||
table.insert(sb, "}");
|
||||
else
|
||||
table.insert(sb, string.format(
|
||||
"%s:\"%s\"", tostring (key), tostring(value)))
|
||||
end
|
||||
table.insert(sb, ",");
|
||||
end
|
||||
table.insert(sb, "}");
|
||||
|
||||
m = getmetatable(tt);
|
||||
if m and m.__index then
|
||||
table.insert(sb, table_print (m.__index, done));
|
||||
end
|
||||
|
||||
return table.concat(sb)
|
||||
else
|
||||
return tt .. ""
|
||||
end
|
||||
end
|
||||
|
||||
local function to_string( tbl )
|
||||
if "nil" == type( tbl ) then
|
||||
return tostring(nil)
|
||||
elseif "table" == type( tbl ) then
|
||||
return table_print(tbl)
|
||||
elseif "string" == type( tbl ) then
|
||||
return tbl
|
||||
else
|
||||
return tostring(tbl)
|
||||
end
|
||||
end
|
||||
|
||||
--/////////////////////////
|
||||
|
||||
local VERM = {}
|
||||
|
||||
local function createEnv(parent, current)
|
||||
return setmetatable(
|
||||
current or {},
|
||||
{
|
||||
__parent = parent,
|
||||
__index = parent,
|
||||
__newindex = function(t, k ,v)
|
||||
if type(k) ~= "string" then
|
||||
error("String key for env. table required, but got:"..to_string(k))
|
||||
end
|
||||
|
||||
local function setOnFirstHit(t, k, v)
|
||||
local vv = rawget(t, k)
|
||||
if vv~= nil then rawset(t, k, v); return true end
|
||||
|
||||
local m = getmetatable(t)
|
||||
|
||||
if not m then return false end --assume top
|
||||
|
||||
local p = m.__parent
|
||||
|
||||
if not p then
|
||||
return false
|
||||
else
|
||||
return setOnFirstHit(p, k, v)
|
||||
end
|
||||
end
|
||||
|
||||
if not setOnFirstHit(t, k, v) then
|
||||
rawset(t, k, v)
|
||||
end
|
||||
end
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
local function isNIL(v)
|
||||
return (type(v) == "table") and (next(v) == nil)
|
||||
end
|
||||
|
||||
local function prognForm(e, ...)
|
||||
--eval each argument, return last result
|
||||
|
||||
local argc = select('#',...)
|
||||
|
||||
if argc == 0 then return {} end
|
||||
|
||||
for n = 1, argc - 1 do
|
||||
VERM:eval(e, (select(n,...)))
|
||||
end
|
||||
|
||||
return VERM:eval(e, (select(argc,...)))
|
||||
end
|
||||
|
||||
local function lambdaOrMacro(e, isMacro, args, ...)
|
||||
|
||||
--TODO: get rid of pack-unpack
|
||||
local body = {...}
|
||||
local oldEnv = e
|
||||
|
||||
local ret = function(e, ...)
|
||||
|
||||
-- we need a function that have parameters with names from `args` table
|
||||
-- pack parameters from '...' and bind to new environment
|
||||
|
||||
local newEnv = createEnv(oldEnv, {})
|
||||
|
||||
for i, v in ipairs(args) do
|
||||
local p = select(i,...)
|
||||
if isMacro then
|
||||
newEnv[v] = p
|
||||
else
|
||||
newEnv[v] = VERM:evalValue(e, p)
|
||||
end
|
||||
end
|
||||
if isMacro then
|
||||
local buffer = {}
|
||||
for _, v in ipairs(body) do
|
||||
table.insert(buffer, (VERM:eval(newEnv, v)))
|
||||
end
|
||||
return prognForm(newEnv, unpack(buffer))
|
||||
else
|
||||
return prognForm(newEnv, unpack(body))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
local function lambdaForm(e, args, ...)
|
||||
return lambdaOrMacro(e, false, args, ...)
|
||||
end
|
||||
|
||||
local function defunForm(e, name, args, ...)
|
||||
local ret = lambdaOrMacro(e, false, args, ...)
|
||||
e[name] = ret
|
||||
return ret
|
||||
end
|
||||
|
||||
local function defmacroForm(e, name, args, ...)
|
||||
local ret = lambdaOrMacro(e, true, args, ...)
|
||||
e[name] = ret
|
||||
return ret
|
||||
end
|
||||
|
||||
local function backquoteEval(e, v)
|
||||
if isNIL(v) then
|
||||
return v
|
||||
elseif type(v) == "table" then
|
||||
local car = v[1]
|
||||
|
||||
if car == "," then
|
||||
return VERM:evalValue(e, v[2])
|
||||
else
|
||||
local ret = {}
|
||||
|
||||
for _, v in ipairs(v) do
|
||||
table.insert(ret, (backquoteEval(e, v)))
|
||||
end
|
||||
return ret
|
||||
end
|
||||
else
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
local specialForms =
|
||||
{
|
||||
["<"] = function(e, lhs, rhs)
|
||||
lhs = VERM:evalValue(e, lhs)
|
||||
rhs = VERM:evalValue(e, rhs)
|
||||
if lhs < rhs then
|
||||
return lhs
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end,
|
||||
["<="] = function(e, lhs, rhs)
|
||||
lhs = VERM:evalValue(e, lhs)
|
||||
rhs = VERM:evalValue(e, rhs)
|
||||
if lhs <= rhs then
|
||||
return lhs
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end,
|
||||
[">"] = function(e, lhs, rhs)
|
||||
lhs = VERM:evalValue(e, lhs)
|
||||
rhs = VERM:evalValue(e, rhs)
|
||||
if lhs > rhs then
|
||||
return lhs
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end,
|
||||
[">="] = function(e, lhs, rhs)
|
||||
lhs = VERM:evalValue(e, lhs)
|
||||
rhs = VERM:evalValue(e, rhs)
|
||||
if lhs >= rhs then
|
||||
return lhs
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end,
|
||||
["="] = function(e, lhs, rhs)
|
||||
lhs = VERM:evalValue(e, lhs)
|
||||
rhs = VERM:evalValue(e, rhs)
|
||||
if lhs == rhs then
|
||||
return lhs
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end,
|
||||
["+"] = function(e, ...)
|
||||
local ret = 0
|
||||
for n=1,select('#',...) do
|
||||
local v = VERM:evalValue(e, (select(n,...)))
|
||||
ret = ret + v
|
||||
end
|
||||
return ret
|
||||
end,
|
||||
["*"] = function(e, ...)
|
||||
local ret = 1
|
||||
for n=1,select('#',...) do
|
||||
local v = VERM:evalValue(e, (select(n,...)))
|
||||
ret = ret * v
|
||||
end
|
||||
return ret
|
||||
end,
|
||||
["-"] = function(e, lhs, rhs)
|
||||
lhs = VERM:evalValue(e, lhs)
|
||||
rhs = VERM:evalValue(e, rhs)
|
||||
return lhs - rhs
|
||||
end,
|
||||
["/"] = function(e, lhs, rhs)
|
||||
lhs = VERM:evalValue(e, lhs)
|
||||
rhs = VERM:evalValue(e, rhs)
|
||||
return lhs / rhs
|
||||
end,
|
||||
["%"] = function(e, lhs, rhs)
|
||||
lhs = VERM:evalValue(e, lhs)
|
||||
rhs = VERM:evalValue(e, rhs)
|
||||
return lhs % rhs
|
||||
end,
|
||||
-- ["comma-unlist"] = function(e, ...) end,
|
||||
["`"] = backquoteEval,
|
||||
-- ["get-func"] = function(e, ...) end,
|
||||
["'"] = function(e, v)
|
||||
return v
|
||||
end,
|
||||
["if"] = function(e, cond, v1, v2)
|
||||
cond = VERM:evalValue(e, cond)
|
||||
|
||||
if isNIL(cond) then
|
||||
return VERM:evalValue(e, v2)
|
||||
else
|
||||
return VERM:evalValue(e, v1)
|
||||
end
|
||||
end,
|
||||
-- ["set"] = function(e, ...) end,
|
||||
-- ["setf"] = function(e, ...) end,
|
||||
["setq"] = function(e, name, value)
|
||||
e[name] = VERM:evalValue(e, value)
|
||||
end,
|
||||
["lambda"] = lambdaForm,
|
||||
["defun"] = defunForm,
|
||||
["progn"] = prognForm,
|
||||
["defmacro"] = defmacroForm,
|
||||
["do"] = function(e, cond, body)
|
||||
local c = VERM:eval(e, cond)
|
||||
while not isNIL(c) do
|
||||
VERM:eval(e, body)
|
||||
c = VERM:eval(e, cond)
|
||||
end
|
||||
return {}
|
||||
end,
|
||||
["car"] = function(e, list)
|
||||
list = VERM:eval(e, list)
|
||||
return list[1] or {}
|
||||
end,
|
||||
["cdr"] = function(e, list)
|
||||
list = VERM:eval(e, list)
|
||||
local ret = {}
|
||||
for i, v in ipairs(list) do
|
||||
if i > 1 then
|
||||
table.insert(ret, v)
|
||||
end
|
||||
end
|
||||
|
||||
return ret
|
||||
end,
|
||||
["list"] = function(e, ...)
|
||||
local ret = {}
|
||||
for n=1,select('#',...) do
|
||||
local v = VERM:evalValue(e, (select(n,...)))
|
||||
table.insert(ret, v)
|
||||
end
|
||||
|
||||
return ret
|
||||
end,
|
||||
["setq-erm"] = function(e, var, varIndex, value)
|
||||
local v = VERM:evalValue(e, value)
|
||||
DATA.ERM[var][tostring(VERM:evalValue(e, varIndex))] = v
|
||||
return v
|
||||
end,
|
||||
}
|
||||
|
||||
function VERM:evalValue(e, v)
|
||||
if isNIL(v) then
|
||||
return v
|
||||
elseif type(v) == "table" then
|
||||
return self:eval(e, v)
|
||||
elseif type(v) == "string" then
|
||||
return e[v]
|
||||
elseif type(v) == "function" then
|
||||
error("evalValue do not accept functions")
|
||||
else
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
function VERM:eval(e, t)
|
||||
if type(t) ~= "table" then
|
||||
logError("Not valid form: ".. to_string(t))
|
||||
return {}
|
||||
end
|
||||
|
||||
local car = t[1]
|
||||
local origCar = car
|
||||
|
||||
if type(car) == "string" then
|
||||
car = e[car]
|
||||
end
|
||||
|
||||
if type(car) == "table" then
|
||||
car = self:eval(e, car)
|
||||
end
|
||||
|
||||
if type(car) == "function" then
|
||||
return car(e, unpack(t,2))
|
||||
else
|
||||
logError(to_string(t) .. " is not callable. Car()="..to_string(car))
|
||||
logError("Env:"..to_string(e))
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
function VERM:E(line)
|
||||
self:eval(self.topEnv, line)
|
||||
end
|
||||
|
||||
VERM.topEnv = createEnv(specialForms)
|
||||
|
||||
return VERM
|
||||
|
||||
Reference in New Issue
Block a user