Lunatic: make gameactor and gameevent be "table-called" functions.

git-svn-id: https://svn.eduke32.com/eduke32@3873 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2013-06-12 17:49:53 +00:00
parent bbcf6d6353
commit f6065e227a
5 changed files with 316 additions and 255 deletions

View file

@ -795,9 +795,11 @@ do
-- Callback function chaining control flags. -- Callback function chaining control flags.
our_SFLAG.replace_hard = 0x08000000 -- Replace actor code and flags our_SFLAG.replace_hard = 0x08000000 -- Replace actor code and flags
our_SFLAG.replace_soft = 0x10000000 -- Replace actor code, bitwise OR flags our_SFLAG.replace_soft = 0x10000000 -- Replace actor code, bitwise OR flags
our_SFLAG.replace = 0x08000000 -- Should only be used for gameevent
our_SFLAG.chain_beg = 0x20000000 our_SFLAG.chain_beg = 0x20000000
our_SFLAG.chain_end = 0x40000000 our_SFLAG.chain_end = 0x40000000
our_SFLAG._CHAIN_MASK = 0x78000000 our_SFLAG._CHAIN_MASK_ACTOR = 0x78000000
our_SFLAG._CHAIN_MASK_EVENT = 0x68000000
-- XXX: CON doesn't export BADGUYSTAYPUT or ROTFIXED SFLAGs, but they are considered -- XXX: CON doesn't export BADGUYSTAYPUT or ROTFIXED SFLAGs, but they are considered
-- external for Lunatic. -- external for Lunatic.
@ -853,7 +855,8 @@ actor = setmtonce({}, defs_c.GenStructMetatable("actor", "MAXSPRITES", actor_sta
-- Some bitwise NOTs of various actor flag masks. -- Some bitwise NOTs of various actor flag masks.
local BNOT = { local BNOT = {
USER_MASK = bit.bnot(actor.FLAGS.USER_MASK), USER_MASK = bit.bnot(actor.FLAGS.USER_MASK),
CHAIN_MASK = bit.bnot(actor.FLAGS._CHAIN_MASK), CHAIN_MASK_ACTOR = bit.bnot(actor.FLAGS._CHAIN_MASK_ACTOR),
CHAIN_MASK_EVENT = bit.bnot(actor.FLAGS._CHAIN_MASK_EVENT),
} }
local projectile = defs_c.creategtab(ffiC.ProjectileData, ffiC.MAXTILES, "projectile[]") local projectile = defs_c.creategtab(ffiC.ProjectileData, ffiC.MAXTILES, "projectile[]")
@ -1617,6 +1620,15 @@ local function chain_func3(func1, func2)
end end
end end
-- Determines the last numeric index of a table using *ipairs*.
local function ourmaxn(tab)
local maxi = 0
for i in ipairs(tab) do
maxi = math.max(i, maxi)
end
return maxi
end
-- Running for the very first time? -- Running for the very first time?
local g_firstRun = (ffiC.g_elCONSize == 0) local g_firstRun = (ffiC.g_elCONSize == 0)
@ -1624,10 +1636,17 @@ local g_firstRun = (ffiC.g_elCONSize == 0)
local actor_funcs = {} local actor_funcs = {}
local gameactor_internal = gameactor_internal -- included in lunatic.c local gameactor_internal = gameactor_internal -- included in lunatic.c
-- gameactor(tilenum [, flags [, strength [, act [, mov [, movflags]]]]], actor_func) -- gameactor{tilenum [, flags [, strength [, action [, move [, movflags]]]]], func}
local function our_gameactor(tilenum, ...) -- Every arg may be positional OR key=val (with the name indicated above as key),
-- but not both.
local function our_gameactor(args)
bcheck.top_level("gameactor") bcheck.top_level("gameactor")
if (type(args)~="table") then
error("invalid gameactor call: must be passed a table")
end
local tilenum = args[1]
if (type(tilenum) ~= "number") then if (type(tilenum) ~= "number") then
error("invalid argument #1 to gameactor: must be a number", 2) error("invalid argument #1 to gameactor: must be a number", 2)
end end
@ -1635,28 +1654,24 @@ local function our_gameactor(tilenum, ...)
error("invalid argument #1 to gameactor: must be a tile number [0..gv.MAXTILES-1]", 2) error("invalid argument #1 to gameactor: must be a tile number [0..gv.MAXTILES-1]", 2)
end end
local args = {...} local lastargi = ourmaxn(args)
-- Number of varargs; args may have nil's in the middle! local func = args[lastargi]
local numargs = select('#', ...) if (type(func) ~= "function") then
func = args.func
if (numargs == 0) then lastargi = 1/0
error("invalid call to gameactor: must have at least two arguments (tilenum, func)", 2)
end end
if (numargs >= 7) then -- sic, because tilenum isn't counted! if (type(func) ~= "function") then
error("invalid call to gameactor: must have at most 7 arguments", 2) error("invalid gameactor call: must provide a function with last numeric arg or .func", 2)
end
if (type(args[numargs]) ~= "function") then
error("invalid last argument to gameactor: must be a function", 2)
end end
local flags = (numargs > 1) and args[1] or 0 local flags = (lastargi > 2 and args[2]) or args.flags or 0
if (type(flags) ~= "number") then if (type(flags) ~= "number") then
error("invalid 'flags' argument to gameactor: must be a number", 2) error("invalid 'flags' argument (#2) to gameactor: must be a number", 2)
end end
local AF = actor.FLAGS local AF = actor.FLAGS
local chainflags = bit.band(flags, AF._CHAIN_MASK) local chainflags = bit.band(flags, AF._CHAIN_MASK_ACTOR)
flags = bit.band(flags, BNOT.CHAIN_MASK) flags = bit.band(flags, BNOT.CHAIN_MASK_ACTOR)
-- Default chaining behavior: don't, replace the old actor instead, but -- Default chaining behavior: don't, replace the old actor instead, but
-- unlike CON, also replace its flags. (CON ORs them instead.) -- unlike CON, also replace its flags. (CON ORs them instead.)
@ -1673,43 +1688,43 @@ local function our_gameactor(tilenum, ...)
local flags_rbits = bit.band(flags, BNOT.USER_MASK) local flags_rbits = bit.band(flags, BNOT.USER_MASK)
if (flags_rbits ~= 0) then if (flags_rbits ~= 0) then
error("invalid 'flags' argument to gameactor: must not set reserved bits (0x" error("invalid 'flags' argument (#2) to gameactor: must not set reserved bits (0x"
..(bit.tohex(flags_rbits))..")", 2) ..(bit.tohex(flags_rbits))..")", 2)
end end
local strength = (numargs > 2) and args[2] or (replacep and 0 or nil) local strength = ((lastargi > 3 and args[3]) or args.strength) or (replacep and 0 or nil)
if (replacep or strength~=nil) then if (replacep or strength~=nil) then
if (type(strength) ~= "number") then if (type(strength) ~= "number") then
error("invalid 'strength' argument to gameactor: must be a number", 2) error("invalid 'strength' argument (#3) to gameactor: must be a number", 2)
end end
end end
-- TODO: literal number action other than 0? -- TODO: literal number action other than 0?
local act = (numargs > 3) and args[3] or (replacep and "NO" or nil) local act = ((lastargi > 4 and args[4]) or args.action) or (replacep and "NO" or nil)
if (replacep or act ~= nil) then if (replacep or act ~= nil) then
if (type(act)=="string") then if (type(act)=="string") then
act = AC[act] act = AC[act]
end end
if (not ffi.istype(con_action_ct, act)) then if (not ffi.istype(con_action_ct, act)) then
error("invalid 'act' argument to gameactor: must be a string or action", 2) error("invalid 'action' argument (#4) to gameactor: must be a string or action", 2)
end end
end end
-- TODO: literal number move other than 0? -- TODO: literal number move other than 0?
local mov = (numargs > 4) and args[4] or (replacep and "NO" or nil) local mov = ((lastargi > 5 and args[5]) or args.move) or (replacep and "NO" or nil)
if (replacep or mov ~= nil) then if (replacep or mov ~= nil) then
if (type(mov)=="string") then if (type(mov)=="string") then
mov = MV[mov] mov = MV[mov]
end end
if (not ffi.istype(con_move_ct, mov)) then if (not ffi.istype(con_move_ct, mov)) then
error("invalid 'mov' argument to gameactor: must be a string or move", 2) error("invalid 'move' argument (#5) to gameactor: must be a string or move", 2)
end end
end end
local movflags = (numargs > 5) and args[5] or (replacep and 0 or nil) local movflags = ((lastargi > 6 and args[6]) or args.movflags) or (replacep and 0 or nil)
if (replacep or movflags ~= nil) then if (replacep or movflags ~= nil) then
if (type(movflags) ~= "number") then if (type(movflags) ~= "number") then
error("invalid 'movflags' argument to gameactor: must be a number", 2) error("invalid 'movflags' argument (#6) to gameactor: must be a number", 2)
end end
end end
@ -1726,7 +1741,6 @@ local function our_gameactor(tilenum, ...)
tile.flags = (chainflags==AF.replace_hard) and flags or bit.bor(tile.flags, flags) tile.flags = (chainflags==AF.replace_hard) and flags or bit.bor(tile.flags, flags)
end end
local func = args[numargs]
local newfunc = replacep and func local newfunc = replacep and func
or (chainflags==AF.chain_beg) and chain_func3(func, actor_funcs[tilenum]) or (chainflags==AF.chain_beg) and chain_func3(func, actor_funcs[tilenum])
or (chainflags==AF.chain_end) and chain_func3(actor_funcs[tilenum], func) or (chainflags==AF.chain_end) and chain_func3(actor_funcs[tilenum], func)
@ -1740,10 +1754,16 @@ end
local event_funcs = {} local event_funcs = {}
local gameevent_internal = gameevent_internal -- included in lunatic.c local gameevent_internal = gameevent_internal -- included in lunatic.c
-- gameevent(<event idx or string> [, flags], <event function>) -- gameevent{<event idx or string> [, flags], <event function>}
local function our_gameevent(event, flags, func) local function our_gameevent(args)
bcheck.top_level("gameevent") bcheck.top_level("gameevent")
if (type(args)~="table") then
error("invalid gameevent call: must be passed a table")
end
local event = args[1]
if (type(event) == "string") then if (type(event) == "string") then
if (event:sub(1,6) ~= "EVENT_") then if (event:sub(1,6) ~= "EVENT_") then
event = "EVENT_"..event event = "EVENT_"..event
@ -1763,27 +1783,28 @@ local function our_gameevent(event, flags, func)
local AF = actor.FLAGS local AF = actor.FLAGS
-- Event chaining: in Lunatic, chaining at the *end* is the default. -- Kind of CODEDUP from our_gameactor.
if (func==nil) then local lastargi = ourmaxn(args)
func = flags local func = args[lastargi]
flags = AF.chain_end if (type(func) ~= "function") then
else func = args.func
if (type(flags) ~= "number") then lastargi = 1/0
error("invalid 'flags' argument to gameevent: must be a number", 2)
end end
if (bit.band(flags, BNOT.CHAIN_MASK) ~= 0) then if (type(func) ~= "function") then
error("invalid gameevent call: must provide a function with last numeric arg or .func", 2)
end
-- Event chaining: in Lunatic, chaining at the *end* is the default.
local flags = (lastargi > 2 and args[2]) or args.flags or AF.chain_end
if (type(flags) ~= "number") then
error("invalid 'flags' argument (#2) to gameevent: must be a number", 2)
end
if (bit.band(flags, BNOT.CHAIN_MASK_EVENT) ~= 0) then
error("invalid 'flags' argument to gameevent: must not set reserved bits", 2) error("invalid 'flags' argument to gameevent: must not set reserved bits", 2)
end end
if (flags == 0) then
flags = AF.chain_end
end
end
if (type(func) ~= "function") then local newfunc = (flags==AF.replace) and func
error("invalid last argument to gameevent: must be a function", 2)
end
local newfunc = replacep and func
or (flags==AF.chain_beg) and chain_func3(func, event_funcs[event]) or (flags==AF.chain_beg) and chain_func3(func, event_funcs[event])
or (flags==AF.chain_end) and chain_func3(event_funcs[event], func) or (flags==AF.chain_end) and chain_func3(event_funcs[event], func)
or assert(false) or assert(false)

View file

@ -418,9 +418,9 @@ function on.actor_end(pos, usertype, tsamm, codetab)
str = str .. movflags.."," str = str .. movflags..","
end end
paddcodef(pos, "gameactor(%d,%sfunction(_aci, _pli, _dist)", tilenum, str) paddcodef(pos, "gameactor{%d,%sfunction(_aci, _pli, _dist)", tilenum, str)
addcode(get_cache_sap_code()) addcode(get_cache_sap_code())
add_code_and_end(codetab, "end)") add_code_and_end(codetab, "end}")
if (g_code.actor[tilenum] ~= nil) then if (g_code.actor[tilenum] ~= nil) then
pwarnprintf(pos, "redefined actor %d", tilenum) pwarnprintf(pos, "redefined actor %d", tilenum)
@ -476,22 +476,22 @@ end
function on.event_end(pos, eventidx, codetab) function on.event_end(pos, eventidx, codetab)
assert(type(codetab)=="table") assert(type(codetab)=="table")
-- 0x20000000: actor.FLAGS.chain_beg -- 0x20000000: actor.FLAGS.chain_beg
paddcodef(pos, "gameevent(%d,0x20000000,function (_aci, _pli, _dist)", eventidx) paddcodef(pos, "gameevent{%d,0x20000000,function (_aci, _pli, _dist)", eventidx)
addcode(get_cache_sap_code()) addcode(get_cache_sap_code())
addcode(codetab) addcode(codetab)
addcode("end)") addcode("end}")
g_code.event[eventidx] = codetab g_code.event[eventidx] = codetab
end end
function on.eventloadactor_end(pos, tilenum, codetab) function on.eventloadactor_end(pos, tilenum, codetab)
-- Translate eventloadactor into a chained EVENT_LOADACTOR block -- Translate eventloadactor into a chained EVENT_LOADACTOR block
paddcodef(pos, "gameevent('LOADACTOR', function (_aci, _pli, _dist)") paddcodef(pos, "gameevent{'LOADACTOR', function (_aci, _pli, _dist)")
addcode(get_cache_sap_code()) addcode(get_cache_sap_code())
addcodef("if (%s==%d) then", SPS".picnum", tilenum) addcodef("if (%s==%d) then", SPS".picnum", tilenum)
addcode(codetab) addcode(codetab)
addcode("end") addcode("end")
addcode("end)") addcode("end}")
if (g_code.loadactor[tilenum] ~= nil and g_warn["chained-loadactor"]) then if (g_code.loadactor[tilenum] ~= nil and g_warn["chained-loadactor"]) then
-- NOTE: C-CON redefines loadactor code if encountered multiple times. -- NOTE: C-CON redefines loadactor code if encountered multiple times.

View file

@ -178,7 +178,7 @@ checkfail('local i = actor[0].t_data[15]', "has no member named 't_data'")
-- no pointer arithmetic! -- no pointer arithmetic!
checkfail('local spr = sprite[0]; local x=spr+1', "attempt to perform arithmetic on") checkfail('local spr = sprite[0]; local x=spr+1', "attempt to perform arithmetic on")
checkfail('gameactor(1680, 0)', "invalid last argument to gameactor: must be a function") checkfail('gameactor{1680, 0}', "must provide a function with last numeric arg or .func")
checkfail("do local bt=require'test.test_bitar'; bt.QWE=1; end", "modifying module table forbidden") checkfail("do local bt=require'test.test_bitar'; bt.QWE=1; end", "modifying module table forbidden")
-- the cdata returned by player[] can't be made into a pointer! -- the cdata returned by player[] can't be made into a pointer!
@ -195,17 +195,20 @@ player[0].curr_weapon = 1
printf('ceilingbunch of sector 0: %d', sector[0].ceilingbunch) printf('ceilingbunch of sector 0: %d', sector[0].ceilingbunch)
gameevent(gv.EVENT_JUMP, gameevent{gv.EVENT_JUMP,
function(actori, playeri, dist) function(actori, playeri, dist)
printf("jump i=%d p=%d d=%d", actori, playeri, dist) printf("jump i=%d p=%d d=%d", actori, playeri, dist)
error("greetings from EVENT_JUMP") error("greetings from EVENT_JUMP")
end end
) }
gameevent
{
"PROCESSINPUT",
gameevent("PROCESSINPUT",
-- Input test. -- Input test.
-- NOTE: I don't think that exposing g_player[].sync (aka "input") is a good idea... -- NOTE: I don't think that exposing g_player[].sync (aka "input") is a good idea...
function(actori, playeri, dist) func = function(actori, playeri, dist)
local IB = player._INPUT_BITS local IB = player._INPUT_BITS
local input = player[playeri]._input local input = player[playeri]._input
if (bit.band(input.bits, IB.JUMP) ~= 0) then if (bit.band(input.bits, IB.JUMP) ~= 0) then
@ -216,10 +219,15 @@ gameevent("PROCESSINPUT",
input.bits = bit.bor(input.bits, IB.JETPACK) input.bits = bit.bor(input.bits, IB.JETPACK)
end end
end end
) }
-- test event chaining -- test event chaining
gameevent("JUMP", actor.FLAGS.chain_beg, gameevent
{
"JUMP",
flags = actor.FLAGS.chain_beg,
function(actori, playeri, dist) function(actori, playeri, dist)
local ps = player[playeri] local ps = player[playeri]
print("\n--- I'm first!") print("\n--- I'm first!")
@ -237,9 +245,12 @@ gameevent("JUMP", actor.FLAGS.chain_beg,
ps._pals[2] = 20 ps._pals[2] = 20
ps._pals.f = 30 ps._pals.f = 30
end end
) }
gameevent
{
gv.EVENT_ENTERLEVEL,
gameevent(gv.EVENT_ENTERLEVEL,
function() function()
if (gv._DEBUG_LUNATIC) then if (gv._DEBUG_LUNATIC) then
DBG_.testmembread() DBG_.testmembread()
@ -327,33 +338,40 @@ gameevent(gv.EVENT_ENTERLEVEL,
checkfail("gameevent('GAME', function() print('qwe') end)", checkfail("gameevent('GAME', function() print('qwe') end)",
"must be called from top level") "must be called from top level")
end end
) }
local geom = require "geom" local geom = require "geom"
gameevent("LOADACTOR", function(i) gameevent{"LOADACTOR", function(i)
local spr = sprite[i] local spr = sprite[i]
if (i==614 and spr.picnum==930) then if (i==614 and spr.picnum==930) then
-- "police line" ribbon in E1L1 -- "police line" ribbon in E1L1
-- clear the hitag so that it doesn't spawn as FALLER from premap -- clear the hitag so that it doesn't spawn as FALLER from premap
spr.hitag = 0 spr.hitag = 0
end end
end) end}
gameactor(930, nil, 1, function(i) -- "police line" ribbon gameactor
{
-- "police line" ribbon
930, nil, 1,
func = function(i)
local spr = sprite[i] local spr = sprite[i]
local r = math.random local r = math.random
local d = 20 local d = 20
-- NOTE: __add metamethod is called because of the RHS: -- NOTE: __add metamethod is called because of the RHS:
local v = spr + geom.vec3(r(-d,d), r(-d,d)) local v = spr + geom.vec3(r(-d,d), r(-d,d))
spr:setpos(v) spr:setpos(v)
end) end
}
local stat = require("stat") local stat = require("stat")
local hs = stat.new() local hs = stat.new()
local con = require("con") local con = require("con")
local AC, MV = con.AC, con.MV local AC, MV = con.AC, con.MV
con.action("TROOPSTAND",0,1,5,1,1) con.action("TROOPSTAND",0,1,5,1,1)
con.action("TROOPFLINTCH", 50, 1, 1, 1, 6) con.action("TROOPFLINTCH", 50, 1, 1, 1, 6)
con.move("SHRUNKVELS", 32) con.move("SHRUNKVELS", 32)
@ -366,12 +384,18 @@ con.ai("AITEMP", "TROOPFLINTCH", "SHRUNKVELS", 0)
local TROOPSTRENGTH = 30 local TROOPSTRENGTH = 30
local D = require("CON.DEFS")
local AF = actor.FLAGS local AF = actor.FLAGS
local CS = sprite.CSTAT local CS = sprite.CSTAT
-- Also test actor code chaining: strength is doubled. -- Also test actor code chaining: strength is doubled.
gameactor(1680, AF.chain_end+AF.enemy, 2*TROOPSTRENGTH, "TROOPSTAND", -- LIZTROOP gameactor
function(i, playeri, dist) {
D.LIZTROOP, AF.chain_end+AF.enemy, 2*TROOPSTRENGTH,
action = "TROOPSTAND",
func = function(i, playeri, dist)
spriteext[i]:make_animated() spriteext[i]:make_animated()
sprite[i].pal = math.random(32) sprite[i].pal = math.random(32)
@ -408,9 +432,12 @@ gameactor(1680, AF.chain_end+AF.enemy, 2*TROOPSTRENGTH, "TROOPSTAND", -- LIZTROO
end end
end end
end end
) }
gameevent
{
"DISPLAYROOMS",
gameevent("DISPLAYROOMS",
function() function()
local cam = gv.cam local cam = gv.cam
cam.pos.z = cam.pos.z + 64*16*math.sin(gv.totalclock/30) cam.pos.z = cam.pos.z + 64*16*math.sin(gv.totalclock/30)
@ -425,9 +452,13 @@ gameevent("DISPLAYROOMS",
end end
end end
end end
) }
gameevent("DISPLAYREST", function() gameevent
{
"DISPLAYREST",
function()
for i=0,10 do for i=0,10 do
for j=1,100 do for j=1,100 do
-- XXX: This is slower in the Polymodes than with classic! -- XXX: This is slower in the Polymodes than with classic!
@ -445,17 +476,25 @@ gameevent("DISPLAYREST", function()
show2dsector:set0(i) show2dsector:set0(i)
end end
end end
end) end
}
-- APLAYER gameactor
gameactor(1405, actor.FLAGS.chain_beg, function(aci, pli) {
D.APLAYER, actor.FLAGS.chain_beg,
function(aci, pli)
if (con._squished(aci, pli)) then if (con._squished(aci, pli)) then
printf("Squished LunaCON test") printf("Squished LunaCON test")
end end
end) end
}
gameevent("ANIMATESPRITES", gameevent
{
"ANIMATESPRITES",
function(aci) function(aci)
local tspr = atsprite[aci] local tspr = atsprite[aci]
if (tspr:getspr().picnum==1680) then if (tspr:getspr().picnum==1680) then
@ -466,7 +505,8 @@ function(aci)
tspr2.cstatbits:set(CS.TRANS_BITMASK) tspr2.cstatbits:set(CS.TRANS_BITMASK)
end end
end end
end) end
}
printf("EVENT_INIT = %d", gv.EVENT_INIT) -- tests default defines printf("EVENT_INIT = %d", gv.EVENT_INIT) -- tests default defines

View file

@ -36,7 +36,7 @@ require "end_gamevars"
local tag = tag local tag = tag
gameevent("JUMP", actor.FLAGS.chain_beg, gameevent{"JUMP", actor.FLAGS.chain_beg,
function(aci, pli) function(aci, pli)
local ps = player[pli] local ps = player[pli]
@ -74,4 +74,4 @@ function(aci, pli)
end end
insp = not insp insp = not insp
end) end}

View file

@ -47,7 +47,7 @@ local function rotatesprite_test()
rs(30,170, 32768, 2047*((gv.totalclock/240)%1), 142, 0,0,8+1024,0, 0,0,gv.xdim-1,gv.ydim-1) rs(30,170, 32768, 2047*((gv.totalclock/240)%1), 142, 0,0,8+1024,0, 0,0,gv.xdim-1,gv.ydim-1)
end end
gameevent(gv.EVENT_DISPLAYREST, rotatesprite_test) gameevent{gv.EVENT_DISPLAYREST, rotatesprite_test}
module(...) --==================== module(...) --====================