From 8e5d5f9b8927a2b80dad4adf7fed95259a0d2952 Mon Sep 17 00:00:00 2001 From: helixhorned Date: Sun, 13 Jan 2013 16:40:21 +0000 Subject: [PATCH] Lunatic: begin changing ones... git-svn-id: https://svn.eduke32.com/eduke32@3390 1a8010ca-5511-0410-912e-c29ae57300e0 --- polymer/eduke32/source/lunatic/control.lua | 32 +++++ polymer/eduke32/source/lunatic/defs.ilua | 30 +++-- polymer/eduke32/source/lunatic/lunacon.lua | 138 +++++++++++++++++---- 3 files changed, 162 insertions(+), 38 deletions(-) diff --git a/polymer/eduke32/source/lunatic/control.lua b/polymer/eduke32/source/lunatic/control.lua index 325d97ee3..4485e1317 100644 --- a/polymer/eduke32/source/lunatic/control.lua +++ b/polymer/eduke32/source/lunatic/control.lua @@ -1020,3 +1020,35 @@ function killit() -- TODO: guard against deletion of player sprite? error(true) end + + +-- Per-actor variable +-- TODO: serialization +local peractorvar_mt = { + __index = function(acv, idx) + check_sprite_idx(idx) + return acv._defval + end, + + __newindex = function(acv, idx, val) + check_sprite_idx(idx) + acv[idx] = val + end, + + -- Calling a per-actor variable causes its cleanup: + -- * All values for sprite not in the game world are cleared. + -- * All values equal to the default one are cleared. + __call = function(acv) + for i=0,ffiC.MAXSPRITES-1 do + if (ffiC.sprite[i].statnum == ffiC.MAXSTATUS or acv[i]==acv._defval) then + acv[i] = nil + end + end + end, + + __metatable = true, +} + +function peractorvar(initval) + return setmetatable({ _defval=initval }, peractorvar_mt) +end diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua index bdc5db486..81d403dd9 100644 --- a/polymer/eduke32/source/lunatic/defs.ilua +++ b/polymer/eduke32/source/lunatic/defs.ilua @@ -784,11 +784,14 @@ local player_mt = { addinventory = con._addinventory, - pstomp = con._pstomp, + stomp = con._pstomp, - wack = function(p) + -- XXX: is the correct spelling "whack"? + wack = function(p, no_return_to_center) p.horiz = p.horiz + 64 - p.return_to_center = 9 + if (not no_return_to_center) then + p.return_to_center = 9 + end local n = bit.arshift(128-bit.band(ffiC.krand(),255), 1) p.rotscrnang = n p.look_ang = n @@ -890,7 +893,7 @@ local function readintostr(fn) error("INTERNAL ERROR: kfilelength() returned negative length", ERRLEV) end - local str = ffi.new("char [?]", sz) -- XXX: what does it do on out of mem? + local str = ffi.new("char [?]", sz) local readlen = ffiC.kread(fd, str, sz) ffiC.kclose(fd); fd=-1 @@ -905,9 +908,10 @@ end -- The "require" function accessible to Lunatic code. -- Base modules in allowed_modules are wrapped so that they cannot be -- modified, user modules are searched in the EDuke32 search --- path. Also, our require never messes with the global environment, --- it only returns the module. -local function our_require(modname) +-- path. Also, our require +-- * never messes with the global environment, it only returns the module. +-- * allows passing varargs beyond the name to the module. +local function our_require(modname, ...) check_valid_modname(modname, 2) -- see whether it's a base module name first @@ -937,7 +941,7 @@ local function our_require(modname) table.insert(modname_stack, modname) -- Run the module code! - modfunc(modname) -- TODO: call protected and report errors here later + modfunc(modname, ...) -- TODO: call protected and report errors here later table.remove(modname_stack) @@ -959,9 +963,6 @@ local function our_require(modname) end --- _G tweaks -- pull in only 'safe' stuff -local G_ = {} -- our soon-to-be global environment - local module_mt = { __index = function (_, n) error("attempt to read undeclared variable '"..n.."'", 2) @@ -1000,6 +1001,10 @@ local function our_error(errmsg, level) error(errmsg, 2) end + +-- _G tweaks -- pull in only 'safe' stuff +local G_ = {} -- our soon-to-be global environment + G_.assert = assert G_.error = our_error G_.ipairs = ipairs @@ -1154,6 +1159,9 @@ do end concode = lunacon.compile(confn) + if (concode == nil) then + error("Failure compiling CON code, exiting.", 0) + end end -- change the environment of this chunk to the table G_ diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index ccbbcacc0..4079f1053 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -96,15 +96,18 @@ local g_iflevel = 0 local g_ifelselevel = 0 ---=== Code generation ===--- +local GVFLAG = { PERPLAYER=1, PERACTOR=2, PERX_MASK=3, } + -- CON --> mangled Lua function name, also existence check: local g_funcname = {} +-- [identifier] = { name=, flags= } +local g_gamevar = {} local g_have_file = {} -- [filename]=true local g_curcode = nil -- a table of string pieces or other "gencode" tables -local g_actor_code = {} -- [actornum]=gencode_table -local g_event_code = {} -- [eventnum]=gencode_table -local g_loadactor_code = {} -- [actornum]=gencode_table +-- [{actor, event, actor}num]=gencode_table +local g_code = { actor={}, event={}, loadactor={} } local function getlinecol(pos) end -- fwd-decl @@ -119,10 +122,11 @@ end local function reset_codegen() g_funcname = {} + g_gamevar = {} g_have_file = {} g_curcode = new_initial_codetab() - g_actor_code, g_event_code, g_loadactor_code = {}, {}, {} + g_code.actor, g_code.event, g_code.loadactor = {}, {}, {} g_recurslevel = -1 g_numerrors = 0 @@ -163,7 +167,7 @@ local function on_actor_end(usertype, tsamm, codetab) addcodef("gameactor(%d,%sfunction(_aci, _pli, _dist)", tilenum, str) add_code_and_end(codetab, "end)") - g_actor_code[tilenum] = codetab + g_code.actor[tilenum] = codetab end local BAD_ID_CHARS0 = "_/\\*?" -- allowed 1st identifier chars @@ -193,7 +197,7 @@ local function on_event_end(eventidx, codetab) addcodef("gameevent(%d, function (_aci, _pli, _dist)", eventidx) add_code_and_end(codetab, "end)") - g_event_code[eventidx] = codetab + g_code.event[eventidx] = codetab end ---------- @@ -318,6 +322,10 @@ local function do_define_label(identifier, num) end end else + if (g_gamevar[identifier]) then + warnprintf("symbol `%s' already used for game variable", identifier) + end + -- New definition of a label g_labeldef[identifier] = num g_labeltype[identifier] = LABEL.NUMBER @@ -646,6 +654,73 @@ local function cmd_music(volnum, ...) end +--- GAMEVARS / GAMEARRAYS + +local function cmd_gamevar(identifier, initval, flags) + local invalid_code = "local _INVALIDGV" + + if (bit.band(flags, bit.bnot(GVFLAG.PERX_MASK)) ~= 0) then + -- TODO: a couple of the presumably safe ones + errprintf("gamevar flags other than PERPLAYER or PERACTOR: NYI or forbidden") + return invalid_code + end + + if (flags==GVFLAG.PERPLAYER+GVFLAG.PERACTOR) then + errprintf("invalid gamevar flags: must be either PERPLAYER or PERACTOR, not both") + return invalid_code + end + + local ogv = g_gamevar[identifier] + + if (ogv ~= nil) then + if (ogv.flags ~= flags) then + errprintf("duplicate gamevar definition `%s' has different flags", identifier) + return invalid_code + else + warnprintf("duplicate gamevar definition `%s' ignored", identifier) + return "" + end + end + + local ltype = g_labeltype[identifier] + if (ltype ~= nil) then + warnprintf("symbol `%s' already used for a defined %s", identifier, LABEL[ltype]) + end + + local gv = { name=mangle_name(identifier, "V"), flags=flags } + g_gamevar[identifier] = gv + + -- TODO: Write gamevar system on the Lunatic side and hook it up. + -- TODO: per-player gamevars + if (flags==GVFLAG.PERACTOR) then + return format("local %s=_con.peractorvar(%d)", gv.name, initval) + else + return format("local %s=%d", gv.name, initval) + end +end + +local function lookup_gamevar(identifier) + local gv = g_gamevar[identifier] + + if (gv == nil) then + errprintf("symbol `%s' is not a game variable", identifier) + return "_INVALIDGV" + end + + if (gv.flags==GVFLAG.PERACTOR) then + return format("%s[_aci]", gv.name) + else + return gv.name + end +end + +local function maybe_gamevar_Cmt(subj, pos, identifier) + if (g_gamevar[identifier]) then + return true, lookup_gamevar(identifier) + end +end + + ----==== patterns ====---- ---- basic ones @@ -665,12 +740,10 @@ local alpha = Range("AZ", "az") -- locale? local alphanum = alpha + Range("09") --local alnumtok = alphanum + Set("{}/\\*-_.") -- see isaltok() in gamedef.c ---- basic lexical elements ("tokens") +--- Basic lexical elements ("tokens"). See the final grammar ("Grammar") for +--- their definitions. local t_maybe_minus = (Pat("-") * sp0)^-1; -local t_number = POS() * lpeg.C( - t_maybe_minus * ((Pat("0x") + "0X") * Range("09", "af", "AF")^1 * Pat("h")^-1 - + Range("09")^1) - ) / parse_number +local t_number = Var("t_number") -- Valid identifier names are disjunct from keywords! -- XXX: CON is more permissive with identifier name characters: local t_identifier = Var("t_identifier") @@ -687,12 +760,8 @@ local t_newline_term_str = match_until(anychar, newline) -- new-style inline arrays and structures: local t_arrayexp = Var("t_arrayexp") --- defines and constants can take the place of vars that are only read: --- NOTE: when one of t_identifier+t_define matches, we don't actually know --- whether it's the right one yet, since their syntax overlaps. -local t_rvar = t_arrayexp + t_identifier + t_define --- not so with written-to vars: -local t_wvar = t_arrayexp + t_identifier +local t_rvar = Var("t_rvar") +local t_wvar = Var("t_wvar") ---- helper patterns / pattern constructing functions @@ -784,7 +853,7 @@ local Co = { spriteflags = cmd(D,D), -- also see inner --- 4. Game Variables / Arrays - gamevar = cmd(I,D,D), + gamevar = cmd(I,D,D) / cmd_gamevar, gamearray = cmd(I,D), --- 5. Top level commands that are also run-time commands @@ -1096,7 +1165,7 @@ local Ci = { pkick = cmd() / format("_con._pkick(%s,%s)", PLS"", ACS""), pstomp = cmd() - / PLS":pstomp(_aci)", + / PLS":stomp(_aci)", resetactioncount = cmd() / ACS":reset_acount()", resetcount = cmd() @@ -1668,6 +1737,11 @@ local Grammar = Pat{ -- NOTE: NW demo (NWSNOW.CON) contains a Ctrl-Z char (decimal 26) whitespace = Set(" \t\r\26") + newline + Set("(),;") + comment + linecomment, + t_number = POS() * lpeg.C( + t_maybe_minus * ((Pat("0x") + "0X") * Range("09", "af", "AF")^1 * Pat("h")^-1 + + Range("09")^1) + ) / parse_number, + t_identifier_all = t_broken_identifier + t_good_identifier, -- NOTE: -conl.keyword alone would be wrong, e.g. "state breakobject": -- NOTE 2: The + "[" is so that stuff like @@ -1680,6 +1754,12 @@ local Grammar = Pat{ t_identifier = -NotKeyw(conl.keyword * (sp1 + "[")) * lpeg.C(t_identifier_all), t_define = (POS() * lpeg.C(t_maybe_minus) * t_identifier / lookup_defined_label) + t_number, + -- Defines and constants can take the place of vars that are only read. + -- XXX: now, when t_rvar fails, the t_define failure message is printed. + t_rvar = t_arrayexp + lpeg.Cmt(t_identifier, maybe_gamevar_Cmt) + t_define, + -- not so with written-to vars: + t_wvar = t_arrayexp + (t_identifier/lookup_gamevar), + t_move = POS()*t_identifier / function(...) return lookup_composite(LABEL.MOVE, ...) end + POS()*t_number / function(...) return check_composite_literal(LABEL.MOVE, ...) end, @@ -1780,6 +1860,17 @@ local function get_code_string(codetab) return table.concat(flatten_codetab(g_curcode), "\n") end +local function on_parse_begin() + g_iflevel = 0 + g_ifelselevel = 0 + g_have_file[g_filename] = true + + -- set up new state + -- TODO: pack into one "parser state" table? + g_lastkw, g_lastkwpos, g_numerrors = nil, nil, 0 + g_recurslevel = g_recurslevel+1 +end + ---=== EXPORTED FUNCTIONS ===--- @@ -1790,17 +1881,10 @@ function parse(contents) -- local local lastkw, lastkwpos, numerrors = g_lastkw, g_lastkwpos, g_numerrors local newlineidxs = g_newlineidxs - g_iflevel = 0 - g_ifelselevel = 0 - g_have_file[g_filename] = true + on_parse_begin() - -- set up new state - -- TODO: pack into one "parser state" table? - g_lastkw, g_lastkwpos, g_numerrors = nil, nil, 0 g_newlineidxs = setup_newlineidxs(contents) - g_recurslevel = g_recurslevel+1 - addcodef("-- BEGIN %s", g_filename) local idx = lpeg.match(Grammar, contents)