Lunatic: don't register con.action/move/ai by name, make these return a cdata.

git-svn-id: https://svn.eduke32.com/eduke32@3916 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2013-06-30 20:38:39 +00:00
parent 16f2f4c475
commit 36f3a8e47c
6 changed files with 101 additions and 136 deletions

View file

@ -77,11 +77,14 @@ enum uactortypes_t {
#ifdef LUNATIC
struct action {
// These members MUST be in this exact order because FFI cdata of this type
// can be initialized by passing a table with numeric indices (con.action).
int16_t startframe, numframes;
int16_t viewtype, incval, delay;
};
struct move {
// These members MUST be in this exact order.
int16_t hvel, vvel;
};

View file

@ -94,7 +94,7 @@ end
function bcheck.top_level(funcname)
if (ffiC.g_elCallDepth > 0) then
error("Invalid use of "..funcname..": must be called from top level", 3)
error("Invalid use of "..funcname..": must be called from top level", errlev or 3)
end
end
@ -104,9 +104,9 @@ function bcheck.number(val, errlev)
end
end
function bcheck.type(val, typestr)
function bcheck.type(val, typestr, errlev)
if (type(val)~=typestr) then
error("invalid argument: must be a "..typestr, 3)
error("invalid argument: must be a "..typestr, errlev or 3)
end
end

View file

@ -26,6 +26,7 @@ local pairs = pairs
local print = print
local rawget = rawget
local rawset = rawset
local select = select
local tostring = tostring
local type = type
local unpack = unpack
@ -53,99 +54,73 @@ end
module(...)
---=== ACTION/MOVE/AI HELPERS ===---
local lastid = { action=0, move=0, ai=0 }
local def = {
action = { NO=ffi.new("con_action_t") },
move = { NO=ffi.new("con_move_t") },
ai = { NO=ffi.new("con_ai_t") },
}
local function forbidden() error("newindex forbidden", 2) end
local con_action_ct = ffi.typeof("const con_action_t")
local con_move_ct = ffi.typeof("const con_move_t")
local con_ai_ct = ffi.typeof("const con_ai_t")
AC = setmetatable({}, { __index=def.action, __newindex=forbidden })
MV = setmetatable({}, { __index=def.move, __newindex=forbidden })
AI = setmetatable({}, { __index=def.ai, __newindex=forbidden })
-- All-zero action and move with IDs. Mostly for CON support.
local literal_act = { [0]=con_action_ct(0), [1]=con_action_ct(1) }
local literal_mov = { [0]=con_move_ct(0), [1]=con_move_ct(1) }
local function check_name(name, what, errlev)
if (type(name)~="string" or #name > 63) then
error("bad argument #1 to "..what..": must be a string of length <= 63", errlev+1)
end
end
local literal_am = { action=literal_act, move=literal_mov }
local am_ctype = { action=con_action_ct, move=con_move_ct }
local function action_or_move(what, numargs, tab, name, ...)
local function def_action_or_move(what, tab)
if (lastid[what] <= -(2^31)) then
error("Too many "..what.."s defined", 3);
end
check_name(name, what, 3)
local args = {...}
if (#args > numargs) then
error("Too many arguments passed to "..what, 3)
end
bcheck.top_level(what, 4)
for i=1,#args do
local n = args[i]
if (type(n)~="number" or not (n >= -32768 and n <= 32767)) then
error("bad argument #".. i+1 .." to "..what..
": must be numbers in [-32768..32767]", 3)
end
if (type(tab) ~= "table") then
error("invalid argument to con."..what..": must be a table", 3)
end
-- missing fields are initialized to 0 by ffi.new
-- Named actions or moves have negative ids so that non-negative ones
-- can be used as (different) placeholders for all-zero ones.
lastid[what] = lastid[what]-1
-- ffi.new takes either for initialization: varargs, a table with numeric
-- indices, or a table with key-value pairs
-- Pass args table to ffi.new, which can take either: a table with numeric
-- indices, or a table with key-value pairs.
-- See http://luajit.org/ext_ffi_semantics.html#init_table
tab[name] = ffi.new("const con_"..what.."_t", lastid[what], args)
return am_ctype[what](lastid[what], tab)
end
---=== ACTION / MOVE / AI ===---
---=== ACTION/MOVE/AI FUNCTIONS ===---
function action(name, ...)
bcheck.top_level("action")
action_or_move("action", 5, def.action, name, ...)
function action(tab)
return def_action_or_move("action", tab)
end
function move(name, ...)
bcheck.top_level("move")
action_or_move("move", 2, def.move, name, ...)
function move(tab)
return def_action_or_move("move", tab)
end
-- Get action or move for an 'ai' definition.
local function get_action_or_move(what, val, argi)
if (val == nil) then
return {} -- ffi.new will init the struct to all zeros
elseif (type(val)=="string") then
local am = def[what][val]
if (am==nil) then
error("no "..what.." '"..val.."' defined", 3)
end
return am
elseif (ffi.istype("con_"..what.."_t", val)) then
return literal_am[what][0]
elseif (ffi.istype(am_ctype[what], val)) then
return val
elseif (type(val)=="number") then
if (val==0 or val==1) then
-- Create an action or move with an ID of 0 or 1 but all other
-- fields cleared.
return ffi.new("con_"..what.."_t", val)
return literal_am[what][val]
end
end
error("bad argument #"..argi.." to ai: must be string or (literal) "..what, 3)
error("bad argument #"..argi.." to ai: must be nil/nothing, 0, 1, or "..what, 3)
end
function ai(name, action, move, flags)
function ai(action, move, flags)
bcheck.top_level("ai")
if (lastid.ai <= -(2^31)) then
error("Too many AIs defined", 2);
end
check_name(name, "ai", 2)
lastid.ai = lastid.ai-1
local act = get_action_or_move("action", action, 2)
local mov = get_action_or_move("move", move, 3)
@ -158,7 +133,8 @@ function ai(name, action, move, flags)
flags = 0
end
def.ai[name] = ffi.new("const con_ai_t", lastid.ai, act, mov, flags)
lastid.ai = lastid.ai-1
return con_ai_ct(lastid.ai, act, mov, flags)
end

View file

@ -877,7 +877,6 @@ local g_tile = setmtonce({}, defs_c.GenStructMetatable("g_tile", "MAXTILES", til
-- ID-wrapped types con_action_t and con_move_t
local con = require("control")
local isenemytile = con.isenemytile
local MV, AC, AI = con.MV, con.AC, con.AI
-- Add game-side metamethods to "spritetype" and register it with "metatype"
local spr_mt_index_add = {
@ -887,14 +886,13 @@ local spr_mt_index_add = {
}
defs_c.finish_spritetype(spr_mt_index_add)
-- All-zero action and move
local nullac, nullmv = ffi.new("const struct action"), ffi.new("const struct move")
-- Check a literal numeric action or move value.
local function check_literal_am(am, typename)
if (type(am) ~= "number") then
error("bad argument: expected number or "..typename, 3)
end
-- Negative values are generated as con.action/con.move IDs.
if (not (am >= 0 and am <= 32767)) then
error("bad argument: expected number in [0 .. 32767]", 3)
end
@ -905,9 +903,16 @@ local actor_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(ACTOR_STRUCT)))
local player_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(DUKEPLAYER_STRUCT)))
local projectile_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(PROJECTILE_STRUCT)))
local weapondata_ptr_ct = ffi.typeof("$ *", ffi.typeof((strip_const(WEAPONDATA_STRUCT):gsub(" _"," "))))
local con_action_ct = ffi.typeof("con_action_t")
local con_move_ct = ffi.typeof("con_move_t")
local con_ai_ct = ffi.typeof("con_ai_t")
local con_action_ct = ffi.typeof("const con_action_t")
local con_move_ct = ffi.typeof("const con_move_t")
local con_ai_ct = ffi.typeof("const con_ai_t")
-- All-zero bare action and move.
local nullac, nullmv = ffi.new("const struct action"), ffi.new("const struct move")
-- All-zero action and move with IDs. Mostly for CON support.
local literal_act = { [0]=con_action_ct(0), [1]=con_action_ct(1) }
local literal_mov = { [0]=con_move_ct(0), [1]=con_move_ct(1) }
local function get_actor_idx(a)
local i = ffi.cast(actor_ptr_ct, a)-ffi.cast(actor_ptr_ct, ffiC.actor)
@ -920,11 +925,7 @@ local actor_mt = {
-- action
set_action = function(a, act)
a = ffi.cast(actor_ptr_ct, a)
if (type(act)=="string") then
act = AC[act];
end
-- TODO: disallow passing the FFI types altogether, in favor of
-- strings (also move, ai)?
if (ffi.istype(con_action_ct, act)) then
a.t_data[4] = act.id
a.ac = act.ac
@ -940,9 +941,7 @@ local actor_mt = {
has_action = function(a, act)
a = ffi.cast(actor_ptr_ct, a)
if (type(act)=="string") then
act = AC[act];
end
if (ffi.istype(con_action_ct, act)) then
return (a.t_data[4]==act.id)
else
@ -972,9 +971,7 @@ local actor_mt = {
-- move
set_move = function(a, mov, movflags)
a = ffi.cast(actor_ptr_ct, a)
if (type(mov)=="string") then
mov = MV[mov];
end
if (ffi.istype(con_move_ct, mov)) then
a.t_data[1] = mov.id
a.mv = mov.mv
@ -997,9 +994,7 @@ local actor_mt = {
has_move = function(a, mov)
a = ffi.cast(actor_ptr_ct, a)
if (type(mov)=="string") then
mov = MV[mov];
end
if (ffi.istype(con_move_ct, mov)) then
return (a.t_data[1]==mov.id)
else
@ -1013,15 +1008,12 @@ local actor_mt = {
local oa = a
a = ffi.cast(actor_ptr_ct, a)
if (type(ai)=="string") then
ai = AI[ai];
end
-- TODO: literal number AIs?
if (not ffi.istype(con_ai_ct, ai)) then
error("bad argument: expected ai", 2)
end
-- NOTE: compare with gameexec.c
-- NOTE: compare with gameexec.c, "CON_AI:"
a.t_data[5] = ai.id
oa:set_action(ai.act)
@ -1033,9 +1025,6 @@ local actor_mt = {
has_ai = function(a, ai)
a = ffi.cast(actor_ptr_ct, a)
if (type(ai)=="string") then
ai = AI[ai];
end
if (ffi.istype(con_ai_ct, ai)) then
return (a.t_data[5]==ai.id)
else
@ -1048,14 +1037,14 @@ local actor_mt = {
-- TODO: make a bcarray instead.
get_t_data = function(a, idx)
if (idx >= 10ULL) then
error("Invalid t_data index "..idx, 2)
error("invalid t_data index "..idx, 2)
end
return ffi.cast(actor_ptr_ct, a).t_data[idx]
end,
_set_t_data = function(a, idx, val)
if (idx >= 10ULL) then
error("Invalid t_data index "..idx, 2)
error("invalid t_data index "..idx, 2)
end
ffi.cast(actor_ptr_ct, a).t_data[idx] = val
end,
@ -1661,7 +1650,8 @@ local function our_require(modname, ...)
local ok, retval = coroutine.resume(modthread, omodname, ...)
if (not ok) then
errorf(ERRLEV-1, "Failed running \"%s\": %s", modname, retval)
errorf(ERRLEV-1, "Failed running \"%s\": %s\n%s", modname,
retval, debug.traceback(modthread))
end
table.remove(modname_stack)
@ -1795,11 +1785,15 @@ local function chain_func3(func1, func2)
end
end
-- Determines the last numeric index of a table using *ipairs*.
-- Determines the last numeric index of a table using *pairs*, so that in
-- arg-lists with "holes" (e.g. {1, 2, nil, function() end}) are handled
-- properly.
local function ourmaxn(tab)
local maxi = 0
for i in ipairs(tab) do
maxi = math.max(i, maxi)
for i in pairs(tab) do
if (type(i)=="number") then
maxi = math.max(i, maxi)
end
end
return maxi
end
@ -1836,7 +1830,7 @@ local function our_gameactor(args)
lastargi = 1/0
end
if (type(func) ~= "function") then
error("invalid gameactor call: must provide a function with last numeric arg or .func", 2)
error("invalid gameactor call: must provide a function with last numeric arg or .func".." "..type(func), 2)
end
local flags = (lastargi > 2 and args[2]) or args.flags or 0
@ -1874,25 +1868,21 @@ local function our_gameactor(args)
end
end
-- TODO: literal number action other than 0?
local act = ((lastargi > 4 and args[4]) or args.action) or (replacep and "NO" or nil)
local act = ((lastargi > 4 and args[4]) or args.action) or (replacep and literal_act[0] or nil)
if (replacep or act ~= nil) then
if (type(act)=="string") then
act = AC[act]
end
if (not ffi.istype(con_action_ct, act)) then
error("invalid 'action' argument (#4) to gameactor: must be a string or action", 2)
if (type(act)=="number" and (act==0 or act==1)) then
act = literal_act[act]
elseif (not ffi.istype(con_action_ct, act)) then
error("invalid 'action' argument (#4) to gameactor: must be an action", 2)
end
end
-- TODO: literal number move other than 0?
local mov = ((lastargi > 5 and args[5]) or args.move) or (replacep and "NO" or nil)
local mov = ((lastargi > 5 and args[5]) or args.move) or (replacep and literal_mov[0] or nil)
if (replacep or mov ~= nil) then
if (type(mov)=="string") then
mov = MV[mov]
end
if (not ffi.istype(con_move_ct, mov)) then
error("invalid 'move' argument (#5) to gameactor: must be a string or move", 2)
if (type(mov)=="number" and (mov==0 or mov==1)) then
mov = literal_mov[mov]
elseif (not ffi.istype(con_move_ct, mov)) then
error("invalid 'move' argument (#5) to gameactor: must be a move", 2)
end
end

View file

@ -203,6 +203,7 @@ local function new_initial_codetab()
"-- NOTE to the reader: This require's result is Lunatic-private API! DO NOT USE!",
"local _dummy,_S=require'end_gamevars'",
"local _V,_A=_V,_A",
"local _C,_M,_I={},{},{}", -- actions, moves, ais
-- Static ivec3s so that no allocations need to be made.
"local _IVEC = { _xmath.ivec3(), _xmath.ivec3() }",
@ -414,11 +415,6 @@ function on.actor_end(pos, usertype, tsamm, codetab)
local str = flags..","
for i=2,math.min(#tsamm,4) do
if ((i==3 or i==4) and tsamm[i]=="0") then
-- HACK, gameactor() currently doesn't support literals for actions
-- and moves.
tsamm[i] = "'NO'"
end
str = str .. tostring(tsamm[i])..","
end
if (#tsamm >= 5) then
@ -605,8 +601,9 @@ end
local LABEL = { MOVE=2, AI=3, ACTION=5, [2]="move", [3]="ai", [5]="action",
NUMBER=1, [1]="number" }
-- Function names in the 'con' module
-- Function names in the 'con' module:
local LABEL_FUNCNAME = { [2]="move", [3]="ai", [5]="action" }
local LABEL_PREFIX = { [2]="M", [3]="I", [5]="C" } -- _C, _M, _I in the gen'd code
local g_labeldef = {} -- Lua numbers for numbers, strings for composites
local g_labeltype = {}
@ -705,7 +702,8 @@ local function check_sysvar_def_attempt(identifier)
end
end
local function do_define_label(identifier, num)
local Define = {}
function Define.label(identifier, num)
if (check_sysvar_def_attempt(identifier)) then
return
end
@ -774,9 +772,6 @@ function lookup.composite(labeltype, pos, identifier)
end
end
-- Generate a quoted identifier name.
val = format("%q", identifier)
return val
end
@ -788,7 +783,7 @@ local function check_reserved_bits(flags, allowedbits, suffix)
end
end
local function do_define_composite(labeltype, identifier, ...)
function Define.composite(labeltype, identifier, ...)
local oldtype = g_labeltype[identifier]
local oldval = g_labeldef[identifier]
@ -831,9 +826,11 @@ local function do_define_composite(labeltype, identifier, ...)
args[i] = format("%d", args[i])
end
addcodef("_con.%s(%q,%s)", LABEL_FUNCNAME[labeltype], identifier, table.concat(args, ","))
local refcode = mangle_name(identifier, LABEL_PREFIX[labeltype])
addcodef(isai and "%s=_con.%s(%s)" or "%s=_con.%s{%s}", -- ai has parens
refcode, LABEL_FUNCNAME[labeltype], table.concat(args, ","))
g_labeldef[identifier] = ""
g_labeldef[identifier] = refcode
g_labeltype[identifier] = labeltype
end
@ -1488,7 +1485,7 @@ local Couter = {
includedefault = cmd()
/ Cmd.NYI("`includedefault'"),
define = cmd(I,D)
/ do_define_label,
/ Define.label,
--- 2. Defines and Meta-Settings
dynamicremap = cmd()
@ -1558,17 +1555,17 @@ local Couter = {
--- 5. Top level commands that are also run-time commands
move = sp1 * tok.identifier * (sp1 * tok.define)^-2 -- hvel, vvel
/ function(...) do_define_composite(LABEL.MOVE, ...) end,
/ function(...) Define.composite(LABEL.MOVE, ...) end,
-- startframe, numframes, viewtype, incval, delay:
action = sp1 * tok.identifier * (sp1 * tok.define)^-5
/ function(...) do_define_composite(LABEL.ACTION, ...) end,
/ function(...) Define.composite(LABEL.ACTION, ...) end,
-- action, move, flags...:
ai = sp1 * tok.identifier * (sp1 * tok.action *
(sp1 * tok.move * (sp1 * tok.define)^0)^-1
)^-1
/ function(...) do_define_composite(LABEL.AI, ...) end,
/ function(...) Define.composite(LABEL.AI, ...) end,
--- 6. Deprecated TLCs
betaname = newline_term_string,
@ -3220,6 +3217,7 @@ local Grammar = Pat{
* sp1 * Var("single_stmt") * (lpeg.Cc(nil) / on.while_end),
stmt_common = Keyw("{") * sp1 * "}" / "" -- space separation of commands in CON is for a reason!
-- XXX: this do...end can lead to exceeding Lua nesting limits, see nightstrike's tan.con
+ lpeg.Ct(Keyw("{")/"do" * sp1 * stmt_list * sp1 * (Keyw("}")/"end"))
+ con_inner_command + Var("switch_stmt") + lpeg.Ct(Var("while_stmt")),
@ -3564,7 +3562,8 @@ if (string.dump) then
-- msgfile:write(format("-- GENERATED CODE for \"%s\":\n", filename))
if (func == nil) then
msgfile:write(format("-- INVALID Lua CODE: %s\n", errmsg))
msgfile:write(format("-- %s%s: INVALID Lua CODE: %s\n",
g_directory, filename, errmsg))
end
if (lineinfo) then

View file

@ -395,15 +395,12 @@ local hs = stat.new()
local con = require("con")
local AC, MV = con.AC, con.MV
con.action("TROOPSTAND",0,1,5,1,1)
con.action("TROOPFLINTCH", 50, 1, 1, 1, 6)
con.move("SHRUNKVELS", 32)
con.ai("AITEMP", AC.TROOPFLINTCH, MV.SHRUNKVELS, 0) -- TODO: test
-- This should work as well. In fact, it's exactly the same, but I prefer the
-- above version for clarity. NOTE: we'll need to think whether it's OK to
-- redefine composites (moves/actions/ais) during gameplay (probably not).
-- Or will we allow only one definition per label ever?
con.ai("AITEMP", "TROOPFLINTCH", "SHRUNKVELS", 0)
local AC, MV = {}, {}
AC.TROOPSTAND = con.action{0,1,5,1,1}
AC.TROOPFLINTCH = con.action{50, 1, 1, 1, 6}
MV.SHRUNKVELS = con.move{hvel=32}
con.ai(AC.TROOPFLINTCH, MV.SHRUNKVELS, 0) -- unused; TODO: test
local TROOPSTRENGTH = 30
@ -415,7 +412,7 @@ gameactor
{
D.LIZTROOP, AF.chain_end+AF.enemy, 2*TROOPSTRENGTH,
action = "TROOPSTAND",
action = AC.TROOPSTAND,
func = function(i, playeri, dist)
spriteext[i]:make_animated()
@ -446,8 +443,8 @@ gameactor
if (dist < 4096) then
-- Duke Vader / Anakin Nukewalker?
actor[i]:set_action("TROOPFLINTCH")
actor[i]:set_move("SHRUNKVELS")
actor[i]:set_action(AC.TROOPFLINTCH)
actor[i]:set_move(MV.SHRUNKVELS)
if (dist < 1024) then
con.killit()