Rev 5036 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2594 | helixhorne | 1 | -- LunaCON CON to Lunatic translator |
2 | -- requires LPeg, http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html |
||
3 | |||
3256 | helixhorne | 4 | local require = require |
2594 | helixhorne | 5 | local lpeg = require("lpeg") |
6 | |||
3357 | helixhorne | 7 | local bit |
3256 | helixhorne | 8 | local math = require("math") |
9 | local string = require("string") |
||
10 | local table = require("table") |
||
11 | |||
3259 | helixhorne | 12 | |
3256 | helixhorne | 13 | local arg = arg |
14 | |||
15 | local assert = assert |
||
3854 | helixhorne | 16 | local error = error |
3388 | helixhorne | 17 | local ipairs = ipairs |
3523 | helixhorne | 18 | local loadstring = loadstring |
3256 | helixhorne | 19 | local pairs = pairs |
20 | local pcall = pcall |
||
21 | local print = print |
||
3535 | helixhorne | 22 | local setmetatable = setmetatable |
3256 | helixhorne | 23 | local tonumber = tonumber |
3315 | helixhorne | 24 | local tostring = tostring |
3256 | helixhorne | 25 | local type = type |
3324 | helixhorne | 26 | local unpack = unpack |
3256 | helixhorne | 27 | |
3343 | helixhorne | 28 | -- non-nil if running from EDuke32 |
29 | -- (read_into_string~=nil iff string.dump==nil) |
||
30 | local read_into_string = read_into_string |
||
3355 | helixhorne | 31 | local ffi, ffiC |
3343 | helixhorne | 32 | |
3533 | helixhorne | 33 | if (string.dump) then -- running stand-alone |
34 | local ljp = pcall(function() require("ffi") end) |
||
35 | -- "lbit" is the same module as LuaJIT's "bit" (LuaBitOp: |
||
36 | -- http://bitop.luajit.org/), but under a different name for (IMO) less |
||
37 | -- confusion. Useful for running with Rio Lua for cross-checking. |
||
38 | bit = ljp and require("bit") or require("lbit") |
||
2762 | helixhorne | 39 | require("strict") |
3355 | helixhorne | 40 | else |
3357 | helixhorne | 41 | bit = require("bit") |
3355 | helixhorne | 42 | ffi = require("ffi") |
43 | ffiC = ffi.C |
||
2762 | helixhorne | 44 | end |
2616 | helixhorne | 45 | |
2762 | helixhorne | 46 | |
3805 | helixhorne | 47 | |
3315 | helixhorne | 48 | module("lunacon") |
3256 | helixhorne | 49 | |
50 | |||
2762 | helixhorne | 51 | -- I think that the "too many pending calls/choices" is unavoidable in general. |
52 | -- This limit is of course still arbitrary, but writing long if/else cascades |
||
2616 | helixhorne | 53 | -- in CON isn't pretty either (though sometimes necessary because nested switches |
54 | -- don't work?) |
||
55 | -- See also: http://lua-users.org/lists/lua-l/2010-03/msg00086.html |
||
56 | lpeg.setmaxstack(1024); |
||
57 | |||
58 | |||
2594 | helixhorne | 59 | local Pat, Set, Range, Var = lpeg.P, lpeg.S, lpeg.R, lpeg.V |
3534 | helixhorne | 60 | local POS, Cc, Ctab = lpeg.Cp, lpeg.Cc, lpeg.Ct |
2594 | helixhorne | 61 | |
3375 | helixhorne | 62 | -- CON language definitions (among other things, all keywords pattern). |
3246 | helixhorne | 63 | local conl = require("con_lang") |
2594 | helixhorne | 64 | |
65 | |||
66 | local function match_until(matchsp, untilsp) -- (!untilsp matchsp)* in PEG |
||
67 | -- sp: string or pattern |
||
68 | return (matchsp - Pat(untilsp))^0 |
||
69 | end |
||
70 | |||
3881 | helixhorne | 71 | local format = string.format |
3805 | helixhorne | 72 | --[[ |
3881 | helixhorne | 73 | format = function(fmt, ...) |
3805 | helixhorne | 74 | local ok, res = pcall(string.format, fmt, ...) |
75 | if (not ok) then |
||
76 | error(string.format("FAILED format(%q, ...) | message: %s", fmt, res)) |
||
77 | end |
||
78 | return res |
||
79 | end |
||
80 | --]] |
||
3246 | helixhorne | 81 | |
2748 | helixhorne | 82 | local function printf(fmt, ...) |
3246 | helixhorne | 83 | print(format(fmt, ...)) |
2748 | helixhorne | 84 | end |
2594 | helixhorne | 85 | |
3944 | helixhorne | 86 | --- some constants |
2748 | helixhorne | 87 | |
3944 | helixhorne | 88 | local C = { |
89 | -- These two are not used except for predefined labels. |
||
90 | -- NOTE: in-game, MAXSPRITES may be 4096 for a V7 build! |
||
91 | MAXSTATUS = ffiC and ffiC.MAXSTATUS or 1024, |
||
92 | MAXSPRITES = ffiC and ffiC.MAXSPRITES or 16384, |
||
93 | |||
94 | MAXTILES = ffiC and ffiC.MAXTILES or 30720, |
||
95 | MAX_WEAPONS = ffiC and ffiC.MAX_WEAPONS or 12, |
||
96 | } |
||
97 | |||
2748 | helixhorne | 98 | ---=== semantic action functions ===--- |
99 | |||
2749 | helixhorne | 100 | local inf = 1/0 |
2762 | helixhorne | 101 | local NaN = 0/0 |
2749 | helixhorne | 102 | |
103 | -- Last keyword position, for error diagnosis. |
||
104 | local g_lastkwpos = nil |
||
105 | local g_lastkw = nil |
||
106 | local g_badids = {} -- maps bad id strings to 'true' |
||
107 | |||
108 | local g_recurslevel = -1 -- 0: base CON file, >0 included |
||
2756 | helixhorne | 109 | local g_filename = "???" |
2749 | helixhorne | 110 | local g_directory = "" -- with trailing slash if not empty |
3256 | helixhorne | 111 | local g_maxerrors = 20 |
2749 | helixhorne | 112 | local g_numerrors = 0 |
113 | |||
3439 | helixhorne | 114 | -- Default directory to search for GAME.CON etc. |
115 | -- Stand-alone LunaCON only. |
||
116 | local g_defaultDir = nil |
||
117 | |||
3255 | helixhorne | 118 | -- Warning options. Key names are the same as cmdline options, e.g. |
119 | -- -Wno-bad-identifier for disabling the "bad identifier" warning. |
||
3569 | helixhorne | 120 | local g_warn = { ["not-redefined"]=true, ["bad-identifier"]=false, |
3570 | helixhorne | 121 | ["number-conversion"]=true, ["system-gamevar"]=true, |
4299 | helixhorne | 122 | ["error-bad-getactorvar"]=false, ["chained-loadactor"]=true, |
123 | ["never-used-gamevar"]=false, ["never-read-gamevar"]=false, } |
||
3255 | helixhorne | 124 | |
3540 | helixhorne | 125 | -- Code generation and output options. |
3561 | helixhorne | 126 | local g_cgopt = { ["no"]=false, ["debug-lineinfo"]=false, ["gendir"]=nil, |
3842 | helixhorne | 127 | ["cache-sap"]=false, ["error-nostate"]=true, |
4266 | helixhorne | 128 | ["playervar"]=true, ["trapv"]=false, ["wrapv"]=false, |
4962 | helixhorne | 129 | ["bad-getactorvar-use-pli"]=true, |
4356 | helixhorne | 130 | ["error-nonlocal-userdef"]=true, |
131 | ["error-negative-tag-write"]=false, } |
||
3842 | helixhorne | 132 | |
4860 | helixhorne | 133 | if (string.dump) then |
134 | g_cgopt["names"] = false |
||
135 | end |
||
136 | |||
137 | -- For -fnames mode. |
||
138 | local g_actorTileToName = {} |
||
139 | |||
3561 | helixhorne | 140 | local function csapp() return g_cgopt["cache-sap"] end |
3392 | helixhorne | 141 | |
3949 | helixhorne | 142 | local function handle_cmdline_arg(str) |
143 | if (str:sub(1,1)=="-") then |
||
144 | if (#str == 1) then |
||
145 | printf("Warning: input from stdin not supported") |
||
146 | else |
||
147 | local ok = false |
||
148 | local kind = str:sub(2,2) |
||
149 | |||
150 | -- -W(no-)*: warnings |
||
151 | if (kind=="W" and #str >= 3) then |
||
152 | local val = true |
||
153 | local warnstr = str:sub(3) |
||
154 | |||
155 | if (warnstr == "all") then |
||
156 | -- Enable all warnings. |
||
157 | for wopt in pairs(g_warn) do |
||
158 | g_warn[wopt] = true |
||
159 | end |
||
160 | ok = true |
||
161 | else |
||
162 | -- Enable or disable a particular warning. |
||
163 | if (warnstr:sub(1,3)=="no-") then |
||
164 | val = false |
||
165 | warnstr = warnstr:sub(4) |
||
166 | end |
||
167 | |||
168 | if (type(g_warn[warnstr])=="boolean") then |
||
169 | g_warn[warnstr] = val |
||
170 | ok = true |
||
171 | end |
||
172 | end |
||
173 | |||
174 | -- -fno* special handling |
||
175 | elseif (str:sub(2)=="fno") then |
||
176 | -- Disable printing code entirely. |
||
177 | g_cgopt["no"] = true |
||
178 | ok = true |
||
179 | elseif (str:sub(2)=="fno=onlycheck") then |
||
180 | -- Disable printing code, only do syntax check of gen'd code. |
||
181 | g_cgopt["no"] = "onlycheck" |
||
182 | ok = true |
||
183 | |||
184 | -- -fgendir=<directory>: specify directory for generated code |
||
185 | elseif (str:sub(2,9)=="fgendir=" and #str >= 10) then |
||
186 | g_cgopt["gendir"] = str:sub(10) |
||
187 | ok = true |
||
188 | |||
189 | -- -f(no-)*: code generation options |
||
190 | elseif (kind=="f" and #str >= 3) then |
||
191 | local val = true |
||
192 | local cgstr = str:sub(3) |
||
193 | |||
194 | if (cgstr:sub(1,3)=="no-") then |
||
195 | val = false |
||
196 | cgstr = cgstr:sub(4) |
||
197 | end |
||
198 | |||
199 | if (type(g_cgopt[cgstr])=="boolean") then |
||
200 | g_cgopt[cgstr] = val |
||
201 | ok = true |
||
202 | end |
||
203 | |||
204 | -- -I<directory>: default search directory (only ONCE, not search path) |
||
205 | elseif (kind=="I" and #str >= 3) then |
||
206 | g_defaultDir = str:sub(3) |
||
207 | ok = true |
||
208 | end |
||
209 | |||
210 | if (not ffi and not ok) then |
||
211 | printf("Warning: Unrecognized option %s", str) |
||
212 | end |
||
213 | end |
||
214 | |||
215 | return true |
||
216 | end |
||
217 | end |
||
218 | |||
219 | -- Handle command line arguments. Has to happen before pattern construction, |
||
220 | -- because some of them depend on codegen options (specifically, -ftrapv, |
||
221 | -- -fwrapv). |
||
222 | if (string.dump) then |
||
223 | -- running stand-alone |
||
224 | local i = 1 |
||
225 | while (arg[i]) do |
||
226 | if (handle_cmdline_arg(arg[i])) then |
||
227 | table.remove(arg, i) -- remove processed cmdline arg |
||
228 | else |
||
229 | i = i+1 |
||
230 | end |
||
231 | end |
||
232 | else |
||
233 | -- running from EDuke32 |
||
234 | local i=0 |
||
235 | while (ffiC.g_argv[i] ~= nil) do |
||
236 | handle_cmdline_arg(ffi.string(ffiC.g_argv[i])) |
||
237 | i = i+1 |
||
238 | end |
||
239 | end |
||
240 | |||
4356 | helixhorne | 241 | if (g_cgopt["error-negative-tag-write"]) then |
242 | conl.setup_negative_tag_check("_st") |
||
243 | end |
||
244 | |||
3529 | helixhorne | 245 | -- Stack with *true* on top if the innermost block is a "whilevar*n". |
246 | local g_isWhile = {} |
||
247 | -- Sequence number of 'while' statements, used to implement CON "break" inside |
||
248 | -- whilevar*n, which really behaves like what sane languages call "continue"... |
||
249 | local g_whilenum = 0 |
||
2756 | helixhorne | 250 | |
2792 | helixhorne | 251 | ---=== Code generation ===--- |
3392 | helixhorne | 252 | local GVFLAG = { |
253 | PERPLAYER=1, PERACTOR=2, PERX_MASK=3, |
||
254 | SYSTEM = 0x00000800, |
||
255 | READONLY = 0x00001000, |
||
3431 | helixhorne | 256 | |
257 | NODEFAULT = 0x00000400, -- don't reset on actor spawn |
||
258 | NORESET = 0x00020000, -- don't reset when restoring map state |
||
3842 | helixhorne | 259 | |
260 | CON_PERPLAYER = 0x40000000, -- LunaCON internal |
||
3392 | helixhorne | 261 | } |
3390 | helixhorne | 262 | |
3431 | helixhorne | 263 | -- NOTE: This differs from enum GamevarFlags_t's GAMEVAR_USER_MASK |
264 | GVFLAG.USER_MASK = GVFLAG.PERX_MASK + GVFLAG.NODEFAULT + GVFLAG.NORESET |
||
265 | |||
3375 | helixhorne | 266 | -- CON --> mangled Lua function name, also existence check: |
267 | local g_funcname = {} |
||
3513 | helixhorne | 268 | -- while parsing a block, it is a table of "gencode" tables: |
269 | local g_switchCode = nil |
||
270 | -- Global number of switch statements: |
||
271 | local g_switchCount = 0 |
||
4112 | helixhorne | 272 | -- Number of session gamevars: |
273 | local g_numSessionVars = 0 |
||
3392 | helixhorne | 274 | -- [identifier] = { name=<mangled name / code>, flags=<gamevar flags> } |
3390 | helixhorne | 275 | local g_gamevar = {} |
3503 | helixhorne | 276 | -- [identifier] = { name=<mangled name / code>, size=<initial size> } |
277 | local g_gamearray = {} |
||
3375 | helixhorne | 278 | |
3568 | helixhorne | 279 | -- * nil if dynamic tile remapping disabled |
3847 | helixhorne | 280 | -- * {} if enabled but no remappings made |
281 | -- * else, a nonempty table { [name]=<g_dynTileList index> } |
||
3568 | helixhorne | 282 | local g_dyntilei = nil |
3847 | helixhorne | 283 | -- Analogously for sounds. |
284 | local g_dynsoundi = nil |
||
3568 | helixhorne | 285 | |
3325 | helixhorne | 286 | local g_have_file = {} -- [filename]=true |
2792 | helixhorne | 287 | local g_curcode = nil -- a table of string pieces or other "gencode" tables |
288 | |||
4152 | helixhorne | 289 | -- will be a table, see reset.codegen() |
3515 | helixhorne | 290 | local g_code = nil |
2792 | helixhorne | 291 | |
292 | |||
3561 | helixhorne | 293 | local function ACS(s) return (csapp() and "_a" or "actor[_aci]")..s end |
294 | local function SPS(s) return (csapp() and "_spr" or "sprite[_aci]")..s end |
||
295 | local function PLS(s) return (csapp() and "_ps" or "player[_pli]")..s end |
||
296 | local function PLSX(s) return "player[_pli]"..s end |
||
3392 | helixhorne | 297 | |
298 | |||
2748 | helixhorne | 299 | local function getlinecol(pos) end -- fwd-decl |
300 | |||
3325 | helixhorne | 301 | local function new_initial_codetab() |
3574 | helixhorne | 302 | -- NOTE: Keep this one line per line to not confuse the Lua->CON line |
303 | -- mapping system. |
||
3325 | helixhorne | 304 | return { |
3646 | helixhorne | 305 | -- Requires. |
3891 | helixhorne | 306 | "local require=require", |
3646 | helixhorne | 307 | "local _con, _bit, _math = require'con', require'bit', require'math'", |
3909 | helixhorne | 308 | "local _xmath = require'xmath'", |
3513 | helixhorne | 309 | |
3646 | helixhorne | 310 | -- Cache globals into locals. |
3937 | helixhorne | 311 | "local sector, sprite, wall, spriteext, _atsprite = sector, sprite, wall, spriteext, _atsprite", |
3821 | helixhorne | 312 | "local actor, player, projectile, g_tile = actor, player, projectile, g_tile", |
3646 | helixhorne | 313 | "local gameactor, gameevent, _gv = gameactor, gameevent, gv", |
314 | "local updatesector, updatesectorz, cansee = updatesector, updatesectorz, cansee", |
||
315 | "local print, printf = print, printf", |
||
316 | |||
317 | -- Cache a couple of often-used functions. |
||
3949 | helixhorne | 318 | "local _div, _mod, _mulTR, _mulWR = _con._div, _con._mod, _con._mulTR, _con._mulWR", |
3646 | helixhorne | 319 | "local _band, _bor, _bxor = _bit.band, _bit.bor, _bit.bxor", |
320 | "local _lsh, _rsh, _arsh = _bit.lshift, _bit.rshift, _bit.arshift", |
||
4149 | helixhorne | 321 | "local _setsprite,_ssp = _con._setsprite,_con._ssp", |
4290 | helixhorne | 322 | g_cgopt["error-nonlocal-userdef"] |
323 | and "local _gud=_con._get_userdef_check" or "local _gud=_con._get_userdef", |
||
4356 | helixhorne | 324 | "local _st=_con._err_if_negative", |
3646 | helixhorne | 325 | |
3891 | helixhorne | 326 | -- * CON "states" (subroutines) and |
327 | -- * Switch function table, indexed by global switch sequence number: |
||
328 | "local _F,_SW = {},{}", |
||
3558 | helixhorne | 329 | |
3891 | helixhorne | 330 | -- CON gamevars and gamearrays (see mangle_name()), set up for |
331 | -- restoration from savegames. |
||
332 | "module(...)", |
||
333 | "_V,_A={},{}", |
||
334 | "-- NOTE to the reader: This require's result is Lunatic-private API! DO NOT USE!", |
||
335 | "local _dummy,_S=require'end_gamevars'", |
||
4119 | helixhorne | 336 | -- XXX: Currently commented out because of gamevar restoration from loadmapstate. |
337 | -- "local _V,_A=_V,_A", |
||
3916 | helixhorne | 338 | "local _C,_M,_I={},{},{}", -- actions, moves, ais |
3891 | helixhorne | 339 | |
3558 | helixhorne | 340 | -- Static ivec3s so that no allocations need to be made. |
3909 | helixhorne | 341 | "local _IVEC = { _xmath.ivec3(), _xmath.ivec3() }", |
3574 | helixhorne | 342 | "local function _IV(num, x, y, z)", |
343 | " local v=_IVEC[num]; v.x=x; v.y=y; v.z=z; return v;", |
||
344 | "end", |
||
3325 | helixhorne | 345 | } |
346 | end |
||
347 | |||
3480 | helixhorne | 348 | -- CON global system gamevar |
3489 | helixhorne | 349 | local function CSV(var) return "_gv._csv"..var end |
3466 | helixhorne | 350 | |
3392 | helixhorne | 351 | -- Creates the table of predefined game variables. |
352 | -- KEEPINSYNC gamevars.c: Gv_AddSystemVars() |
||
353 | local function new_initial_gvartab() |
||
354 | local wmembers = conl.wdata_members |
||
355 | |||
3419 | helixhorne | 356 | local function GamevarCreationFunc(addflags) |
357 | return function(varname) |
||
4299 | helixhorne | 358 | -- 'used' is a bitmask: 1 is 'was read', 2 is 'was written to' |
359 | return { name=varname, flags=GVFLAG.SYSTEM+addflags, used=3 } |
||
3419 | helixhorne | 360 | end |
3406 | helixhorne | 361 | end |
362 | |||
3419 | helixhorne | 363 | local RW = GamevarCreationFunc(0) |
364 | local RO = GamevarCreationFunc(GVFLAG.READONLY) |
||
365 | local PRW = GamevarCreationFunc(GVFLAG.PERPLAYER) |
||
366 | local PRO = GamevarCreationFunc(GVFLAG.READONLY+GVFLAG.PERPLAYER) |
||
3406 | helixhorne | 367 | |
368 | local gamevar = { |
||
3516 | helixhorne | 369 | -- NOTE: THISACTOR can mean different things in some contexts. |
3406 | helixhorne | 370 | THISACTOR = RO "_aci", |
371 | |||
4031 | helixhorne | 372 | RETURN = RW "_gv.RETURN", |
3480 | helixhorne | 373 | HITAG = RW(CSV".HITAG"), |
374 | LOTAG = RW(CSV".LOTAG"), |
||
375 | TEXTURE = RW(CSV".TEXTURE"), |
||
3406 | helixhorne | 376 | |
3556 | helixhorne | 377 | -- This will warn when defining from CON, but it's the most |
378 | -- straightforward implementation. |
||
379 | LOGO_FLAGS = RW "_gv.g_logoFlags", |
||
380 | |||
3406 | helixhorne | 381 | xdim = RO "_gv.xdim", |
382 | ydim = RO "_gv.ydim", |
||
3409 | helixhorne | 383 | windowx1 = RO "_gv.windowx1", |
384 | windowy1 = RO "_gv.windowy1", |
||
385 | windowx2 = RO "_gv.windowx2", |
||
386 | windowy2 = RO "_gv.windowy2", |
||
3406 | helixhorne | 387 | |
3475 | helixhorne | 388 | yxaspect = RO "_gv._get_yxaspect()", |
3480 | helixhorne | 389 | viewingrange = RO "_gv._get_viewingrange()", |
3964 | helixhorne | 390 | -- TODO: gravitationalconstant, gametype_flags |
3475 | helixhorne | 391 | |
3406 | helixhorne | 392 | numsectors = RO "_gv.numsectors", |
393 | NUMSECTORS = RO "_gv.numsectors", |
||
394 | NUMWALLS = RO "_gv.numwalls", |
||
4475 | helixhorne | 395 | Numsprites = RO "_gv.Numsprites", |
3406 | helixhorne | 396 | |
3409 | helixhorne | 397 | randomseed = RW "_gv.randomseed", |
398 | totalclock = RO "_gv.totalclock", |
||
3431 | helixhorne | 399 | framerate = RO "_gv._currentFramerate()", |
400 | current_menu = RO "_gv._currentMenu()", |
||
3928 | helixhorne | 401 | rendmode = RO "_gv.rendmode", |
3409 | helixhorne | 402 | |
403 | screenpeek = RO "_gv.screenpeek", |
||
404 | |||
3406 | helixhorne | 405 | camerax = RW "_gv.cam.pos.x", |
406 | cameray = RW "_gv.cam.pos.y", |
||
407 | cameraz = RW "_gv.cam.pos.z", |
||
408 | cameraang = RW "_gv.cam.ang", |
||
409 | camerahoriz = RW "_gv.cam.horiz", |
||
410 | camerasect = RW "_gv.cam.sect", |
||
411 | cameradist = RW "_gv.cam.dist", |
||
412 | cameraclock = RW "_gv.cam.clock", |
||
3409 | helixhorne | 413 | |
414 | -- HUD weapon gamevars |
||
415 | currentweapon = RW "_gv.hudweap.cur", |
||
416 | weaponcount = RW "_gv.hudweap.count", |
||
417 | weapon_xoffset = RW "_gv.hudweap.gunposx", |
||
418 | looking_angSR1 = RW "_gv.hudweap.lookhalfang", |
||
419 | gun_pos = RW "_gv.hudweap.gunposy", |
||
420 | looking_arc = RW "_gv.hudweap.lookhoriz", |
||
421 | gs = RW "_gv.hudweap.shade", |
||
422 | |||
423 | -- Some per-player gamevars |
||
3561 | helixhorne | 424 | ZRANGE = PRW(PLSX".zrange"), |
425 | ANGRANGE = PRW(PLSX".angrange"), |
||
426 | AUTOAIMANGLE = PRW(PLSX".autoaimang"), |
||
3409 | helixhorne | 427 | |
3561 | helixhorne | 428 | PIPEBOMB_CONTROL = PRW(PLSX".pipebombControl"), |
429 | GRENADE_LIFETIME = PRW(PLSX".pipebombLifetime"), |
||
430 | GRENADE_LIFETIME_VAR = PRW(PLSX".pipebombLifetimeVar"), |
||
431 | TRIPBOMB_CONTROL = PRW(PLSX".tripbombControl"), |
||
432 | STICKYBOMB_LIFETIME = PRW(PLSX".tripbombLifetime"), |
||
433 | STICKYBOMB_LIFETIME_VAR = PRW(PLSX".tripbombLifetimeVar"), |
||
3409 | helixhorne | 434 | |
3817 | helixhorne | 435 | -- Some *writable* system gamevars relating to multiplayer. |
436 | -- TODO_MP. |
||
437 | RESPAWN_MONSTERS = RO "0", |
||
438 | RESPAWN_ITEMS = RO "0", |
||
439 | RESPAWN_INVENTORY = RO "0", |
||
440 | MONSTERS_OFF = RO "0", |
||
441 | MARKER = RO "0", |
||
442 | |||
3415 | helixhorne | 443 | -- These are not 100% authentic (they're only updated in certain |
444 | -- circumstances, see player.c: P_SetWeaponGamevars()). But IMO it's |
||
445 | -- more useful like this. |
||
3561 | helixhorne | 446 | WEAPON = PRO(PLSX".curr_weapon"), |
447 | WORKSLIKE = PRO(format(PLSX".weapon[%s].workslike", PLSX".curr_weapon")), |
||
3415 | helixhorne | 448 | |
4260 | helixhorne | 449 | VOLUME = RO "_gv._ud.volume_number", |
450 | LEVEL = RO "_gv._ud.level_number", |
||
3406 | helixhorne | 451 | } |
452 | |||
3563 | helixhorne | 453 | -- Reserved bits |
4567 | helixhorne | 454 | gamevar.LOGO_FLAGS.rbits = bit.bnot(0x001fffff) |
3563 | helixhorne | 455 | |
3944 | helixhorne | 456 | for w=0,C.MAX_WEAPONS-1 do |
3392 | helixhorne | 457 | for i=1,#wmembers do |
3574 | helixhorne | 458 | local member = wmembers[i]:gsub(".*_t ","") -- strip e.g. "const int32_t " |
459 | :gsub("^_","") -- strip potentially leading underscore |
||
3392 | helixhorne | 460 | local name = format("WEAPON%d_%s", w, member:upper()) |
3561 | helixhorne | 461 | gamevar[name] = PRW(format(PLSX".weapon[%d].%s", w, member)) |
3563 | helixhorne | 462 | |
463 | if (member=="flags") then |
||
464 | gamevar[name].rbits = bit.bnot(0x1ffff) |
||
465 | end |
||
3392 | helixhorne | 466 | end |
467 | end |
||
468 | |||
469 | return gamevar |
||
470 | end |
||
471 | |||
4152 | helixhorne | 472 | local reset = {} |
473 | |||
474 | function reset.codegen() |
||
3375 | helixhorne | 475 | g_funcname = {} |
3513 | helixhorne | 476 | g_switchCode = nil |
477 | g_switchCount = 0 |
||
4112 | helixhorne | 478 | g_numSessionVars = 0 |
3392 | helixhorne | 479 | g_gamevar = new_initial_gvartab() |
3562 | helixhorne | 480 | g_gamearray = { |
3940 | helixhorne | 481 | -- SYSTEM_GAMEARRAY |
3944 | helixhorne | 482 | tilesizx = { name="g_tile.sizx", size=C.MAXTILES, sysp=true }, |
483 | tilesizy = { name="g_tile.sizy", size=C.MAXTILES, sysp=true }, |
||
3562 | helixhorne | 484 | } |
3375 | helixhorne | 485 | |
3568 | helixhorne | 486 | g_dyntilei = nil |
3847 | helixhorne | 487 | g_dynsoundi = nil |
3568 | helixhorne | 488 | |
3325 | helixhorne | 489 | g_have_file = {} |
490 | g_curcode = new_initial_codetab() |
||
4373 | helixhorne | 491 | -- actor, event, loadactor: [{actor, event, actor}num] = gencode_table |
492 | -- |
||
493 | -- aflagsloc[actornum]: location of '(user)actor' token, 'spriteflags' or |
||
494 | -- 'sprite*' command; result of getLocation(<kind>, <pos>) |
||
495 | g_code = { actor={}, event={}, loadactor={}, aflagsloc={} } |
||
3325 | helixhorne | 496 | |
497 | g_recurslevel = -1 |
||
498 | g_numerrors = 0 |
||
2792 | helixhorne | 499 | end |
500 | |||
3940 | helixhorne | 501 | -- Is SYSTEM_GAMEARRAY? |
502 | local function issysgar(str) |
||
503 | return str:match("^g_tile.siz[xy]") |
||
504 | end |
||
505 | |||
2792 | helixhorne | 506 | local function addcode(x) |
3246 | helixhorne | 507 | assert(type(x)=="string" or type(x)=="table") |
2792 | helixhorne | 508 | g_curcode[#g_curcode+1] = x |
509 | end |
||
510 | |||
511 | local function addcodef(fmt, ...) |
||
3246 | helixhorne | 512 | addcode(format(fmt, ...)) |
2792 | helixhorne | 513 | end |
514 | |||
3604 | helixhorne | 515 | local function paddcodef(pos, fmt, ...) |
516 | addcodef(fmt.."--"..getlinecol(pos), ...) |
||
517 | end |
||
518 | |||
3373 | helixhorne | 519 | local function add_code_and_end(codetab, endstr) |
520 | assert(type(codetab)=="table") |
||
521 | addcode(codetab) |
||
522 | addcode(endstr) |
||
523 | end |
||
524 | |||
3561 | helixhorne | 525 | local function get_cache_sap_code() |
526 | return csapp() and "local _spr,_a,_ps=_con._getsap(_aci,_pli)" or "" |
||
527 | end |
||
528 | |||
3604 | helixhorne | 529 | -- fwd-decls |
4151 | helixhorne | 530 | local warnprintf, errprintf, pwarnprintf, perrprintf, contprintf |
4373 | helixhorne | 531 | local getLocation |
3515 | helixhorne | 532 | |
3513 | helixhorne | 533 | local on = {} |
534 | |||
3597 | helixhorne | 535 | -- Map from CON actor usertype to SFLAGs. |
536 | local MAP_ACTOR_FLAGS = { |
||
537 | [0] = 0, |
||
538 | [1] = conl.SFLAG.SFLAG_BADGUY, |
||
539 | [2] = conl.SFLAG.SFLAG_BADGUY + conl.SFLAG.SFLAG_BADGUYSTAYPUT, |
||
540 | [3] = conl.SFLAG.SFLAG_BADGUY + conl.SFLAG.SFLAG_BADGUYSTAYPUT, |
||
541 | } |
||
542 | for i=4,7 do |
||
543 | MAP_ACTOR_FLAGS[i] = MAP_ACTOR_FLAGS[i-4] + conl.SFLAG.SFLAG_ROTFIXED |
||
544 | end |
||
545 | |||
546 | |||
4860 | helixhorne | 547 | -- Table of functions doing various lookups (label, gamevar, ...) |
548 | local lookup = {} |
||
549 | |||
550 | -- For -fnames mode. |
||
551 | function on.fnames_tilenum_label(tilenum) |
||
552 | if (g_cgopt["names"] and type(tilenum)=="string") then |
||
553 | -- <tilenum> may be a string (define label) |
||
554 | -- HANDLE_RAWDEFINE |
||
555 | local pos, minus, label = tilenum:match("(.-):(.-):(.+)") |
||
556 | local realtilenum = lookup.defined_label(tonumber(pos), minus, label) |
||
557 | |||
558 | g_actorTileToName[realtilenum] = label |
||
559 | return true |
||
560 | end |
||
561 | end |
||
562 | |||
3604 | helixhorne | 563 | function on.actor_end(pos, usertype, tsamm, codetab) |
2792 | helixhorne | 564 | local tilenum = tsamm[1] |
3597 | helixhorne | 565 | local flags = 0 |
2792 | helixhorne | 566 | |
4860 | helixhorne | 567 | if (on.fnames_tilenum_label(tilenum)) then |
568 | return |
||
569 | end |
||
570 | |||
3597 | helixhorne | 571 | if (usertype ~= nil) then -- useractor |
572 | if (not (bit.band(usertype, bit.bnot(7)) == 0)) then |
||
3604 | helixhorne | 573 | perrprintf(pos, "invalid usertype: must be bitwise OR of 1, 2 and/or 4") |
3597 | helixhorne | 574 | else |
575 | flags = MAP_ACTOR_FLAGS[usertype] |
||
576 | end |
||
577 | end |
||
578 | |||
4374 | helixhorne | 579 | -- 0x08000000: actor.FLAGS.replace |
580 | flags = bit.bor(flags, 0x08000000) |
||
3829 | helixhorne | 581 | |
3597 | helixhorne | 582 | local str = flags.."," |
3315 | helixhorne | 583 | for i=2,math.min(#tsamm,4) do |
584 | str = str .. tostring(tsamm[i]).."," |
||
585 | end |
||
3597 | helixhorne | 586 | if (#tsamm >= 5) then |
587 | local movflags = bit.bor(unpack(tsamm, 5)) |
||
588 | str = str .. movflags.."," |
||
3315 | helixhorne | 589 | end |
590 | |||
3894 | helixhorne | 591 | paddcodef(pos, "gameactor{%d,%sfunction(_aci,_pli,_dist)", tilenum, str) |
3561 | helixhorne | 592 | addcode(get_cache_sap_code()) |
3873 | helixhorne | 593 | add_code_and_end(codetab, "end}") |
3373 | helixhorne | 594 | |
3515 | helixhorne | 595 | if (g_code.actor[tilenum] ~= nil) then |
3604 | helixhorne | 596 | pwarnprintf(pos, "redefined actor %d", tilenum) |
3515 | helixhorne | 597 | end |
3390 | helixhorne | 598 | g_code.actor[tilenum] = codetab |
4373 | helixhorne | 599 | g_code.aflagsloc[tilenum] = getLocation("definition of actor", pos) |
2792 | helixhorne | 600 | end |
601 | |||
3933 | helixhorne | 602 | -- NOTE: in C-CON, the slash and backslash can also be part of an identifier, |
603 | -- but this is likely to support file names in other places. |
||
604 | local BAD_ID_CHARS0 = "_*?" -- allowed 1st identifier chars |
||
4473 | helixhorne | 605 | local BAD_ID_CHARS1 = "_*-+?." -- allowed following identifier chars |
3375 | helixhorne | 606 | |
3855 | helixhorne | 607 | local function truetab(tab) |
608 | local ttab = {} |
||
609 | for i=1,#tab do |
||
610 | ttab[tab[i]] = true |
||
611 | end |
||
612 | return ttab |
||
613 | end |
||
614 | |||
615 | -- Lua 5.2 keywords. Not 5.1 because we use "goto" for codegen. |
||
616 | local LUA_KEYW = truetab { |
||
617 | "and", "break", "do", "else", "elseif", "end", |
||
618 | "false", "for", "function", "goto", "if", "in", |
||
619 | "local", "nil", "not", "or", "repeat", "return", |
||
620 | "then", "true", "until", "while" |
||
621 | } |
||
622 | |||
3518 | helixhorne | 623 | -- Return the Lua code by which the CON object <name> is referenced in the |
624 | -- translated code. |
||
3375 | helixhorne | 625 | local function mangle_name(name, prefix) |
3855 | helixhorne | 626 | if (name:match("^[A-Za-z_][A-Za-z_0-9]*$") and not LUA_KEYW[name]) then |
3646 | helixhorne | 627 | return format("_%s.%s", prefix, name) |
628 | else |
||
629 | return format("_%s[%q]", prefix, name) |
||
630 | end |
||
3375 | helixhorne | 631 | end |
632 | |||
3513 | helixhorne | 633 | function on.state_begin_Cmt(_subj, _pos, statename) |
3409 | helixhorne | 634 | -- We must register the state name early (Cmt) because otherwise, it won't |
635 | -- be found in a recursive state. XXX: The real issue seems to be the use |
||
636 | -- of "Cmt"s in other places, which messes up the sequence of running the |
||
637 | -- semantic actions. |
||
3375 | helixhorne | 638 | local ourname = mangle_name(statename, "F") |
639 | g_funcname[statename] = ourname |
||
3409 | helixhorne | 640 | return true, ourname |
3375 | helixhorne | 641 | end |
642 | |||
3604 | helixhorne | 643 | function on.state_end(pos, funcname, codetab) |
3894 | helixhorne | 644 | paddcodef(pos, "%s=function(_aci,_pli,_dist)", funcname) |
3561 | helixhorne | 645 | addcode(get_cache_sap_code()) |
3373 | helixhorne | 646 | add_code_and_end(codetab, "end") |
3255 | helixhorne | 647 | end |
648 | |||
3604 | helixhorne | 649 | function on.event_end(pos, eventidx, codetab) |
3526 | helixhorne | 650 | assert(type(codetab)=="table") |
3639 | helixhorne | 651 | -- 0x20000000: actor.FLAGS.chain_beg |
5022 | helixhorne | 652 | paddcodef(pos, "gameevent{%d,0x20000000,function(_aci,_pli,_dist)", eventidx) |
3561 | helixhorne | 653 | addcode(get_cache_sap_code()) |
3526 | helixhorne | 654 | addcode(codetab) |
3873 | helixhorne | 655 | addcode("end}") |
3373 | helixhorne | 656 | |
3390 | helixhorne | 657 | g_code.event[eventidx] = codetab |
3373 | helixhorne | 658 | end |
659 | |||
3604 | helixhorne | 660 | function on.eventloadactor_end(pos, tilenum, codetab) |
4860 | helixhorne | 661 | if (on.fnames_tilenum_label(tilenum)) then |
662 | return |
||
663 | end |
||
664 | |||
3515 | helixhorne | 665 | -- Translate eventloadactor into a chained EVENT_LOADACTOR block |
5022 | helixhorne | 666 | paddcodef(pos, "gameevent{'LOADACTOR',function(_aci,_pli,_dist)") |
3561 | helixhorne | 667 | addcode(get_cache_sap_code()) |
3515 | helixhorne | 668 | addcodef("if (%s==%d) then", SPS".picnum", tilenum) |
669 | addcode(codetab) |
||
670 | addcode("end") |
||
3873 | helixhorne | 671 | addcode("end}") |
3515 | helixhorne | 672 | |
3813 | helixhorne | 673 | if (g_code.loadactor[tilenum] ~= nil and g_warn["chained-loadactor"]) then |
3604 | helixhorne | 674 | -- NOTE: C-CON redefines loadactor code if encountered multiple times. |
675 | pwarnprintf(pos, "chained additional loadactor %d code", tilenum) |
||
3515 | helixhorne | 676 | end |
677 | g_code.loadactor[tilenum] = codetab |
||
678 | end |
||
679 | |||
2792 | helixhorne | 680 | ---------- |
681 | |||
2749 | helixhorne | 682 | local function linecolstr(pos) |
683 | local line, col = getlinecol(pos) |
||
3246 | helixhorne | 684 | return format("%d:%d", line, col) |
2749 | helixhorne | 685 | end |
686 | |||
3256 | helixhorne | 687 | local function increment_numerrors() |
688 | g_numerrors = g_numerrors+1 |
||
689 | if (g_numerrors == g_maxerrors) then |
||
690 | g_numerrors = inf |
||
691 | printf("Too many errors (%d), aborting...", g_maxerrors) |
||
692 | end |
||
693 | end |
||
694 | |||
3604 | helixhorne | 695 | function perrprintf(pos, fmt, ...) |
4151 | helixhorne | 696 | printf("%s %s: error: "..fmt, g_filename, |
697 | pos and linecolstr(pos) or "???", ...) |
||
3256 | helixhorne | 698 | increment_numerrors() |
2792 | helixhorne | 699 | end |
700 | |||
3604 | helixhorne | 701 | function errprintf(fmt, ...) |
4151 | helixhorne | 702 | perrprintf(g_lastkwpos, fmt, ...) |
2749 | helixhorne | 703 | end |
704 | |||
3604 | helixhorne | 705 | function pwarnprintf(pos, fmt, ...) |
4151 | helixhorne | 706 | printf("%s %s: warning: "..fmt, g_filename, |
707 | pos and linecolstr(pos) or "???", ...) |
||
2792 | helixhorne | 708 | end |
709 | |||
3604 | helixhorne | 710 | function warnprintf(fmt, ...) |
4151 | helixhorne | 711 | pwarnprintf(g_lastkwpos, fmt, ...) |
2756 | helixhorne | 712 | end |
713 | |||
4151 | helixhorne | 714 | -- Print a continuation line to an error or warning. |
715 | function contprintf(iserr, fmt, ...) |
||
716 | printf("%s %s: %s "..fmt, g_filename, |
||
717 | g_lastkwpos and linecolstr(g_lastkwpos) or "???", |
||
718 | iserr and " " or " ", ...) |
||
719 | end |
||
720 | |||
2792 | helixhorne | 721 | local function parse_number(pos, numstr) |
3571 | helixhorne | 722 | -- <numstr> is a full number string, potentially prefixed with a minus sign. |
3375 | helixhorne | 723 | local num = tonumber((numstr:gsub("h$", ""))) |
3571 | helixhorne | 724 | -- local onum = num |
725 | local hex = numstr:match("0[xX]([^h]*)h?") -- get hex digits, if any |
||
2748 | helixhorne | 726 | |
3439 | helixhorne | 727 | -- num==nil for Rio Lua, which doesn't handle large hex literals. |
728 | if (num==nil or not (num >= -0x80000000 and num <= 0xffffffff)) then |
||
3571 | helixhorne | 729 | -- number is <INT32_MIN or >UINT32_MAX or NaN |
730 | if (hex and #hex>8 and hex:sub(1,#hex-8):match("^[fF]$")) then |
||
731 | -- Too many hex digits, but they're all Fs. |
||
732 | pwarnprintf(pos, "number %s truncated to 32 bits", numstr) |
||
3909 | helixhorne | 733 | num = bit.tobit(num) |
3571 | helixhorne | 734 | else |
735 | perrprintf(pos, "number %s out of the range of a 32-bit integer", numstr) |
||
736 | -- Be careful not to write bound checks like |
||
737 | -- "if (i<LOWBOUND or i>HIGHBOUND) then error('...') end": |
||
738 | num = NaN |
||
739 | end |
||
740 | elseif (num >= 0x80000000) then |
||
4266 | helixhorne | 741 | num = bit.tobit(num) |
3571 | helixhorne | 742 | if (not hex and g_warn["number-conversion"]) then |
4266 | helixhorne | 743 | pwarnprintf(pos, "number %s converted to %d", numstr, num) |
3256 | helixhorne | 744 | end |
2748 | helixhorne | 745 | end |
746 | |||
3571 | helixhorne | 747 | -- printf("numstr:%s, num=%d (0x%s) '%s', resnum=%d (0x%s)", |
748 | -- numstr, onum, bit.tohex(onum), hex, num, bit.tohex(num)) |
||
2748 | helixhorne | 749 | return num |
750 | end |
||
751 | |||
3865 | helixhorne | 752 | -- Bound checking functions that generate a compilation error on failure. |
753 | local check = {} |
||
754 | |||
755 | function check.tile_idx(tilenum) |
||
3944 | helixhorne | 756 | if (not (tilenum >= 0 and tilenum < C.MAXTILES)) then |
3516 | helixhorne | 757 | errprintf("invalid tile number %d", tilenum) |
758 | return false |
||
759 | end |
||
3865 | helixhorne | 760 | return true |
761 | end |
||
2748 | helixhorne | 762 | |
3865 | helixhorne | 763 | function check.sound_idx(sidx) |
764 | if (not (sidx >= 0 and sidx < conl.MAXSOUNDS)) then |
||
765 | errprintf("invalid sound number %d", sidx) |
||
766 | return false |
||
767 | end |
||
3516 | helixhorne | 768 | return true |
769 | end |
||
770 | |||
771 | |||
3226 | helixhorne | 772 | -- Mapping of various "define" types to the respective number of members and |
773 | -- vice versa |
||
774 | local LABEL = { MOVE=2, AI=3, ACTION=5, [2]="move", [3]="ai", [5]="action", |
||
775 | NUMBER=1, [1]="number" } |
||
2749 | helixhorne | 776 | |
3916 | helixhorne | 777 | -- Function names in the 'con' module: |
3226 | helixhorne | 778 | local LABEL_FUNCNAME = { [2]="move", [3]="ai", [5]="action" } |
3916 | helixhorne | 779 | local LABEL_PREFIX = { [2]="M", [3]="I", [5]="C" } -- _C, _M, _I in the gen'd code |
2765 | helixhorne | 780 | |
3226 | helixhorne | 781 | local g_labeldef = {} -- Lua numbers for numbers, strings for composites |
782 | local g_labeltype = {} |
||
3870 | helixhorne | 783 | local g_labelspecial = {} -- [<label>] = true |
4151 | helixhorne | 784 | local g_labelloc = {} -- [<label>] = { filename, linenum, colnum } |
2749 | helixhorne | 785 | |
4153 | helixhorne | 786 | -- Get location table for use in continued warning/error reporting. |
4373 | helixhorne | 787 | --[[ local --]] |
788 | function getLocation(kind, pos) |
||
789 | local loc = { g_filename, getlinecol(pos or g_lastkwpos) } |
||
790 | loc[4] = kind |
||
791 | return loc |
||
4153 | helixhorne | 792 | end |
793 | |||
4152 | helixhorne | 794 | function reset.labels() |
3325 | helixhorne | 795 | g_badids = {} |
796 | |||
3226 | helixhorne | 797 | -- NO is also a valid `move', `ai' or `action', but they are handled |
3433 | helixhorne | 798 | -- separately in lookup.composite(). |
3406 | helixhorne | 799 | g_labeldef = { |
800 | NO = 0, |
||
801 | -- NOTE: these are read-only gamevars in C-CON |
||
802 | CLIPMASK0 = 65536+1, -- blocking |
||
803 | CLIPMASK1 = (256*65536)+64, -- hittable |
||
3537 | helixhorne | 804 | -- TODO_MP |
3431 | helixhorne | 805 | COOP = 0, |
3826 | helixhorne | 806 | MULTIMODE = 1, |
3431 | helixhorne | 807 | numplayers = 1, |
3432 | helixhorne | 808 | myconnectindex = 0, |
3944 | helixhorne | 809 | -- Predefined constants |
810 | MAXSTATUS = C.MAXSTATUS, |
||
811 | MAXSPRITES = C.MAXSPRITES, |
||
812 | MAX_WEAPONS = C.MAX_WEAPONS, |
||
3406 | helixhorne | 813 | } |
3226 | helixhorne | 814 | |
4152 | helixhorne | 815 | g_labeltype = {} |
816 | g_labelspecial = {} |
||
817 | g_labelloc = {} |
||
818 | |||
3431 | helixhorne | 819 | for varname,_ in pairs(g_labeldef) do |
820 | g_labeltype[varname] = LABEL.NUMBER |
||
3870 | helixhorne | 821 | g_labelspecial[varname] = true |
3431 | helixhorne | 822 | end |
3406 | helixhorne | 823 | |
3226 | helixhorne | 824 | -- Initialize default defines. |
3246 | helixhorne | 825 | for i=1,#conl.labels do |
826 | for label, val in pairs(conl.labels[i]) do |
||
2762 | helixhorne | 827 | g_labeldef[label] = val |
3226 | helixhorne | 828 | g_labeltype[label] = LABEL.NUMBER |
2762 | helixhorne | 829 | end |
830 | end |
||
831 | end |
||
832 | |||
3433 | helixhorne | 833 | function lookup.defined_label(pos, maybe_minus_str, identifier) |
2749 | helixhorne | 834 | local num = g_labeldef[identifier] |
835 | |||
836 | if (num == nil) then |
||
2792 | helixhorne | 837 | perrprintf(pos, "label \"%s\" is not defined", identifier) |
2763 | helixhorne | 838 | return -inf -- return a number for type cleanness |
2749 | helixhorne | 839 | end |
840 | |||
3226 | helixhorne | 841 | if (g_labeltype[identifier] ~= LABEL.NUMBER) then |
842 | perrprintf(pos, "label \"%s\" is not a `define'd number", identifier) |
||
2765 | helixhorne | 843 | return -inf |
844 | end |
||
845 | |||
3226 | helixhorne | 846 | assert(type(num)=="number") |
847 | |||
2765 | helixhorne | 848 | return (maybe_minus_str=="" and 1 or -1) * num |
2749 | helixhorne | 849 | end |
850 | |||
3847 | helixhorne | 851 | assert(not BAD_ID_CHARS1:find(":")) |
852 | function lookup.raw_defined_label(pos, maybe_minus_str, identifier) |
||
853 | return pos..":"..maybe_minus_str..":"..identifier |
||
854 | end |
||
855 | |||
856 | local dynmap = {} |
||
857 | -- When necessary, initialize dynamic {tile,sound} mapping list. |
||
858 | function dynmap.maybe_init(dyni, dynList) |
||
859 | if (dyni[1]==nil) then |
||
860 | dyni[1] = true |
||
861 | -- Init name -> g_dyn*List index mapping |
||
862 | for i=0,math.huge do |
||
863 | local str = dynList[i].str |
||
864 | if (str==nil) then |
||
865 | break |
||
866 | end |
||
867 | |||
868 | dyni[ffi.string(str)] = i |
||
869 | end |
||
870 | end |
||
871 | end |
||
872 | |||
873 | -- Potentially process one dynamic {tile,sound} remapping. |
||
874 | function dynmap.maybe_process(dyni, dynList, identifier, num) |
||
875 | if (dyni[identifier]) then |
||
876 | local di = dynList[dyni[identifier]] |
||
877 | |||
878 | if (ffiC._DEBUG_LUNATIC~=0 and di.staticval~=num) then |
||
879 | printf("REMAP %s (%d) --> %d", ffi.string(di.str), di.staticval, num) |
||
880 | end |
||
881 | di.dynvalptr[0] = num |
||
882 | end |
||
883 | end |
||
884 | |||
4152 | helixhorne | 885 | -- The 'check' table is also used to hold a couple of misc checkers. |
886 | |||
887 | function check.sysvar_def_attempt(identifier) |
||
3503 | helixhorne | 888 | if (identifier=="actorvar") then |
889 | errprintf("cannot define reserved symbol `actorvar'") |
||
890 | return true |
||
891 | end |
||
4140 | helixhorne | 892 | if (identifier=="_IS_NORESET_GAMEVAR") then |
893 | errprintf("cannot define reserved symbol `_IS_NORESET_GAMEVAR'") |
||
894 | return true |
||
895 | end |
||
3503 | helixhorne | 896 | end |
897 | |||
4153 | helixhorne | 898 | |
899 | local inform = {} |
||
900 | |||
901 | function inform.common(loc, iserr) |
||
4151 | helixhorne | 902 | if (loc) then |
4373 | helixhorne | 903 | contprintf(iserr, "Old definition is at %s %d:%d", loc[1], loc[2], loc[3]) |
4151 | helixhorne | 904 | else |
905 | contprintf(iserr, "Old definition is built-in") |
||
906 | end |
||
907 | end |
||
908 | |||
4153 | helixhorne | 909 | function inform.olddef_location(identifier, iserr) |
910 | inform.common(g_labelloc[identifier], iserr) |
||
911 | end |
||
912 | |||
913 | function inform.oldgv_location(identifier, iserr) |
||
914 | inform.common(g_gamevar[identifier].loc, iserr) |
||
915 | end |
||
916 | |||
917 | |||
918 | local Define = {} |
||
919 | |||
3916 | helixhorne | 920 | function Define.label(identifier, num) |
4152 | helixhorne | 921 | if (check.sysvar_def_attempt(identifier)) then |
3503 | helixhorne | 922 | return |
923 | end |
||
924 | |||
3226 | helixhorne | 925 | local oldtype = g_labeltype[identifier] |
2765 | helixhorne | 926 | local oldval = g_labeldef[identifier] |
2749 | helixhorne | 927 | |
2765 | helixhorne | 928 | if (oldval) then |
3226 | helixhorne | 929 | if (oldtype ~= LABEL.NUMBER) then |
4151 | helixhorne | 930 | errprintf("Refusing to overwrite `%s' label \"%s\" with a `define'd number.", |
3226 | helixhorne | 931 | LABEL[oldtype], identifier) |
4153 | helixhorne | 932 | inform.olddef_location(identifier, true) |
3226 | helixhorne | 933 | else |
3246 | helixhorne | 934 | -- conl.labels[...]: don't warn for wrong PROJ_ redefinitions |
3255 | helixhorne | 935 | if (g_warn["not-redefined"]) then |
3373 | helixhorne | 936 | if (oldval ~= num and conl.PROJ[identifier]==nil) then |
4151 | helixhorne | 937 | warnprintf("Label \"%s\" not redefined with new value %d (old: %d).", |
3255 | helixhorne | 938 | identifier, num, oldval) |
4153 | helixhorne | 939 | inform.olddef_location(identifier, false) |
3255 | helixhorne | 940 | end |
3226 | helixhorne | 941 | end |
2749 | helixhorne | 942 | end |
3226 | helixhorne | 943 | else |
3390 | helixhorne | 944 | if (g_gamevar[identifier]) then |
945 | warnprintf("symbol `%s' already used for game variable", identifier) |
||
4153 | helixhorne | 946 | inform.oldgv_location(identifier, false) |
3390 | helixhorne | 947 | end |
948 | |||
3944 | helixhorne | 949 | if (ffi and g_dyntilei and (num>=0 and num<C.MAXTILES)) then |
3847 | helixhorne | 950 | dynmap.maybe_init(g_dyntilei, ffiC.g_dynTileList) |
951 | dynmap.maybe_process(g_dyntilei, ffiC.g_dynTileList, identifier, num) |
||
3568 | helixhorne | 952 | end |
953 | |||
3226 | helixhorne | 954 | -- New definition of a label |
955 | g_labeldef[identifier] = num |
||
956 | g_labeltype[identifier] = LABEL.NUMBER |
||
4153 | helixhorne | 957 | g_labelloc[identifier] = getLocation() |
2749 | helixhorne | 958 | end |
959 | end |
||
960 | |||
4152 | helixhorne | 961 | function check.composite_literal(labeltype, pos, num) |
3226 | helixhorne | 962 | if (num==0 or num==1) then |
963 | return (num==0) and "0" or "1" |
||
964 | else |
||
965 | perrprintf(pos, "literal `%s' number must be either 0 or 1", LABEL[labeltype]) |
||
966 | return "_INVALIT" |
||
2765 | helixhorne | 967 | end |
968 | end |
||
969 | |||
3433 | helixhorne | 970 | function lookup.composite(labeltype, pos, identifier) |
2765 | helixhorne | 971 | if (identifier=="NO") then |
3226 | helixhorne | 972 | -- NO is a special case and is valid for move, action and ai, |
973 | -- being the same as passing a literal 0. |
||
974 | return "0" |
||
2765 | helixhorne | 975 | end |
976 | |||
977 | local val = g_labeldef[identifier] |
||
4025 | helixhorne | 978 | local typ = g_labeltype[identifier] |
2765 | helixhorne | 979 | |
980 | if (val == nil) then |
||
2792 | helixhorne | 981 | perrprintf(pos, "label \"%s\" is not defined", identifier) |
3226 | helixhorne | 982 | return "_NOTDEF" |
4025 | helixhorne | 983 | elseif (typ ~= labeltype) then |
984 | if (identifier=="randomangle" and labeltype==LABEL.MOVE and typ==LABEL.NUMBER) then |
||
985 | -- Be forgiving with a 1.3/1.5 GAME.CON type error. |
||
3325 | helixhorne | 986 | pwarnprintf(pos, "label \"randomangle\" is not a `move' value, assuming 0") |
987 | return "0" |
||
4025 | helixhorne | 988 | elseif (identifier=="BLIMPRESPAWNTIME" and labeltype==LABEL.ACTION and typ==LABEL.NUMBER) then |
989 | -- Be forgiving with a 1.3 GAME.CON type error. |
||
990 | pwarnprintf(pos, "label \"BLIMPRESPAWNTIME\" is not an `action' value, assuming 0") |
||
991 | return "0" |
||
3325 | helixhorne | 992 | else |
993 | perrprintf(pos, "label \"%s\" is not a%s `%s' value", identifier, |
||
994 | labeltype==LABEL.MOVE and "" or "n", LABEL[labeltype]) |
||
995 | return "_WRONGTYPE" |
||
996 | end |
||
2765 | helixhorne | 997 | end |
998 | |||
999 | return val |
||
1000 | end |
||
1001 | |||
4152 | helixhorne | 1002 | function check.reserved_bits(flags, allowedbits, suffix) |
3882 | helixhorne | 1003 | local rbits = bit.bnot(allowedbits) |
1004 | if (bit.band(flags, rbits) ~= 0) then |
||
1005 | warnprintf("set one or more reserved bits (0x%s) "..suffix, |
||
1006 | bit.tohex(bit.band(flags, rbits))) |
||
1007 | end |
||
1008 | end |
||
1009 | |||
4878 | helixhorne | 1010 | -- KEEPINSYNC control.lua |
4814 | helixhorne | 1011 | Define.ALLOWED_VIEWTYPE = truetab { 0, 1, 2, 3,4, 5, 7, 8, -5, -7 } |
3924 | helixhorne | 1012 | |
3916 | helixhorne | 1013 | function Define.composite(labeltype, identifier, ...) |
3226 | helixhorne | 1014 | local oldtype = g_labeltype[identifier] |
2765 | helixhorne | 1015 | local oldval = g_labeldef[identifier] |
1016 | |||
1017 | if (oldval) then |
||
3226 | helixhorne | 1018 | if (oldtype ~= labeltype) then |
4151 | helixhorne | 1019 | errprintf("Refusing to overwrite `%s' label \"%s\" with a `%s' value.", |
3226 | helixhorne | 1020 | LABEL[oldtype], identifier, LABEL[labeltype]) |
4153 | helixhorne | 1021 | inform.olddef_location(identifier, true) |
3226 | helixhorne | 1022 | else |
4151 | helixhorne | 1023 | warnprintf("Duplicate `%s' definition of \"%s\" ignored.", |
3226 | helixhorne | 1024 | LABEL[labeltype], identifier) |
4153 | helixhorne | 1025 | inform.olddef_location(identifier, false) |
2765 | helixhorne | 1026 | end |
1027 | return |
||
1028 | end |
||
1029 | |||
3541 | helixhorne | 1030 | -- Fill up omitted arguments denoting composites with zeros. |
3226 | helixhorne | 1031 | local isai = (labeltype == LABEL.AI) |
1032 | local args = {...} |
||
1033 | for i=#args+1,labeltype do |
||
3541 | helixhorne | 1034 | -- Passing nil/nothing as remaining args to con.ai will make the |
1035 | -- action/move the null one. |
||
3226 | helixhorne | 1036 | args[i] = (isai and i<=2) and "nil" or 0 |
2765 | helixhorne | 1037 | end |
1038 | |||
3226 | helixhorne | 1039 | if (isai) then |
1040 | assert(type(args[1])=="string") |
||
1041 | assert(type(args[2])=="string") |
||
1042 | |||
2765 | helixhorne | 1043 | -- OR together the flags |
3226 | helixhorne | 1044 | for i=#args,LABEL.AI+1, -1 do |
1045 | args[LABEL.AI] = bit.bor(args[LABEL.AI], args[i]) |
||
1046 | args[i] = nil |
||
2765 | helixhorne | 1047 | end |
3882 | helixhorne | 1048 | |
1049 | -- Check whether movflags use reserved bits. |
||
4152 | helixhorne | 1050 | check.reserved_bits(args[LABEL.AI], 4096+2047, "for ai's movflags") |
2765 | helixhorne | 1051 | end |
1052 | |||
3924 | helixhorne | 1053 | if (labeltype == LABEL.ACTION) then |
1054 | -- Sanity-check action members. |
||
3974 | helixhorne | 1055 | -- KEEPINSYNC with ACTOR_CHECK in control.lua for consistency. |
3924 | helixhorne | 1056 | if (not (args[2] >= 0)) then |
1057 | errprintf("action \"%s\" has negative number of frames", identifier) |
||
1058 | end |
||
1059 | if (Define.ALLOWED_VIEWTYPE[args[3]] == nil) then |
||
1060 | errprintf("action \"%s\" has disallowed viewtype %d", identifier, args[3]) |
||
1061 | end |
||
1062 | if (not (args[4] >= -1 and args[4] <= 1)) then |
||
1063 | warnprintf("action \"%s\" has incval different from -1, 0 or 1", identifier) |
||
1064 | end |
||
1065 | end |
||
1066 | |||
3226 | helixhorne | 1067 | -- Make a string out of that. |
1068 | for i=1+(isai and 2 or 0),#args do |
||
3246 | helixhorne | 1069 | args[i] = format("%d", args[i]) |
3226 | helixhorne | 1070 | end |
1071 | |||
3916 | helixhorne | 1072 | local refcode = mangle_name(identifier, LABEL_PREFIX[labeltype]) |
1073 | addcodef(isai and "%s=_con.%s(%s)" or "%s=_con.%s{%s}", -- ai has parens |
||
1074 | refcode, LABEL_FUNCNAME[labeltype], table.concat(args, ",")) |
||
3226 | helixhorne | 1075 | |
3916 | helixhorne | 1076 | g_labeldef[identifier] = refcode |
3226 | helixhorne | 1077 | g_labeltype[identifier] = labeltype |
4153 | helixhorne | 1078 | g_labelloc[identifier] = getLocation() |
2765 | helixhorne | 1079 | end |
1080 | |||
1081 | |||
2749 | helixhorne | 1082 | local function parse(contents) end -- fwd-decl |
1083 | |||
3809 | helixhorne | 1084 | local function do_include_file(dirname, filename, isroot) |
3343 | helixhorne | 1085 | assert(type(filename)=="string") |
2749 | helixhorne | 1086 | |
3343 | helixhorne | 1087 | if (g_have_file[filename] ~= nil) then |
3881 | helixhorne | 1088 | printf("[%d] Fatal error: infinite loop including \"%s\"", g_recurslevel, filename) |
3343 | helixhorne | 1089 | g_numerrors = inf |
1090 | return |
||
1091 | end |
||
1092 | |||
1093 | local contents |
||
1094 | |||
1095 | if (read_into_string) then |
||
1096 | -- running from EDuke32 |
||
1097 | contents = read_into_string(filename) |
||
1098 | else |
||
1099 | -- running stand-alone |
||
2749 | helixhorne | 1100 | local io = require("io") |
1101 | |||
1102 | local fd, msg = io.open(dirname..filename) |
||
3862 | helixhorne | 1103 | while (fd == nil and not isroot and filename:find("/")) do |
2749 | helixhorne | 1104 | -- strip up to and including first slash: |
3439 | helixhorne | 1105 | filename = filename:gsub("^.-/", "") |
2749 | helixhorne | 1106 | fd, msg = io.open(dirname..filename) |
3862 | helixhorne | 1107 | end |
2749 | helixhorne | 1108 | |
3862 | helixhorne | 1109 | -- As a last resort, try the "default directory" |
3933 | helixhorne | 1110 | if (fd==nil and not isroot and g_defaultDir) then |
3862 | helixhorne | 1111 | -- strip up to and including last slash (if any): |
1112 | filename = filename:gsub("^.*/", "") |
||
1113 | dirname = g_defaultDir.."/" |
||
1114 | fd, msg = io.open(dirname..filename) |
||
3809 | helixhorne | 1115 | end |
3439 | helixhorne | 1116 | |
3809 | helixhorne | 1117 | if (fd == nil) then |
1118 | printf("[%d] Fatal error: couldn't open %s", g_recurslevel, msg) |
||
1119 | g_numerrors = inf |
||
1120 | return |
||
2749 | helixhorne | 1121 | end |
1122 | |||
3343 | helixhorne | 1123 | contents = fd:read("*all") |
2749 | helixhorne | 1124 | fd:close() |
3343 | helixhorne | 1125 | end |
2749 | helixhorne | 1126 | |
3343 | helixhorne | 1127 | if (contents == nil) then |
1128 | -- maybe that file name turned out to be a directory or other |
||
1129 | -- special file accidentally |
||
1130 | printf("[%d] Fatal error: couldn't read from \"%s\"", |
||
1131 | g_recurslevel, dirname..filename) |
||
1132 | g_numerrors = inf |
||
1133 | return |
||
1134 | end |
||
2749 | helixhorne | 1135 | |
3343 | helixhorne | 1136 | printf("%s[%d] Translating file \"%s\"", (g_recurslevel==-1 and "\n---- ") or "", |
1137 | g_recurslevel+1, dirname..filename); |
||
2765 | helixhorne | 1138 | |
3343 | helixhorne | 1139 | local oldfilename = g_filename |
1140 | g_filename = filename |
||
1141 | parse(contents) |
||
1142 | g_filename = oldfilename |
||
1143 | end |
||
2749 | helixhorne | 1144 | |
3391 | helixhorne | 1145 | -- Table of various outer command handling functions. |
1146 | local Cmd = {} |
||
1147 | |||
3516 | helixhorne | 1148 | function Cmd.NYI(msg) |
1149 | return function() |
||
1150 | errprintf(msg.." not yet implemented") |
||
1151 | end |
||
1152 | end |
||
1153 | |||
1154 | function Cmd.nyi(msg) |
||
1155 | return function() |
||
1156 | warnprintf(msg.." not yet implemented") |
||
1157 | end |
||
1158 | end |
||
1159 | |||
3391 | helixhorne | 1160 | function Cmd.include(filename) |
3809 | helixhorne | 1161 | do_include_file(g_directory, filename, false) |
2749 | helixhorne | 1162 | end |
1163 | |||
2763 | helixhorne | 1164 | --- Per-module game data |
1165 | local g_data = {} |
||
3246 | helixhorne | 1166 | local EPMUL = conl.MAXLEVELS |
2749 | helixhorne | 1167 | |
4152 | helixhorne | 1168 | function reset.gamedata() |
2763 | helixhorne | 1169 | g_data = {} |
1170 | |||
1171 | -- [EPMUL*ep + lev] = { ptime=<num>, dtime=<num>, fn=<str>, name=<str> } |
||
2764 | helixhorne | 1172 | g_data.level = {} |
1173 | -- [ep] = <str> |
||
1174 | g_data.volname = {} |
||
1175 | -- [skillnum] = <str> |
||
1176 | g_data.skillname = {} |
||
1177 | -- [quotenum] = <str> |
||
1178 | g_data.quote = {} |
||
1179 | -- table of length 26 or 30 containg numbers |
||
1180 | g_data.startup = {} |
||
1181 | -- [soundnum] = { fn=<str>, params=<table of length 5> } |
||
1182 | g_data.sound = {} |
||
1183 | -- [volnum] = <table of length numlevels (<= MAXLEVELS) of <str>> |
||
1184 | g_data.music = {} |
||
2763 | helixhorne | 1185 | end |
1186 | |||
3806 | helixhorne | 1187 | -- TODO: PRE13 has no <dtstr> (3D Realms time). |
3391 | helixhorne | 1188 | function Cmd.definelevelname(vol, lev, fn, ptstr, dtstr, levname) |
3419 | helixhorne | 1189 | if (not (vol >= 0 and vol < conl.MAXVOLUMES)) then |
2763 | helixhorne | 1190 | errprintf("volume number exceeds maximum volume count.") |
2764 | helixhorne | 1191 | return |
2763 | helixhorne | 1192 | end |
1193 | |||
3419 | helixhorne | 1194 | if (not (lev >= 0 and lev < conl.MAXLEVELS)) then |
2763 | helixhorne | 1195 | errprintf("level number exceeds maximum number of levels per episode.") |
2764 | helixhorne | 1196 | return |
2763 | helixhorne | 1197 | end |
1198 | |||
1199 | -- TODO: Bcorrectfilename(fn) |
||
1200 | |||
1201 | local function secs(tstr) |
||
1202 | local m, s = string.match(tstr, ".+:.+") |
||
1203 | m, s = tonumber(m), tonumber(s) |
||
1204 | return (m and s) and m*60+s or 0 |
||
1205 | end |
||
1206 | |||
3373 | helixhorne | 1207 | local map = { |
1208 | ptime=secs(ptstr), dtime=secs(dtstr), fn="/"..fn, name=levname |
||
2763 | helixhorne | 1209 | } |
3373 | helixhorne | 1210 | |
1211 | if (ffi) then |
||
1212 | ffiC.C_DefineLevelName(vol, lev, map.fn, map.ptime, map.dtime, map.name) |
||
1213 | end |
||
1214 | |||
1215 | g_data.level[EPMUL*vol+lev] = map |
||
2763 | helixhorne | 1216 | end |
1217 | |||
4977 | hendricks2 | 1218 | function Cmd.undefinelevel(vol, lev) |
1219 | if (not (vol >= 0 and vol < conl.MAXVOLUMES)) then |
||
1220 | errprintf("volume number exceeds maximum volume count.") |
||
1221 | return |
||
1222 | end |
||
1223 | |||
1224 | if (not (lev >= 0 and lev < conl.MAXLEVELS)) then |
||
1225 | errprintf("level number exceeds maximum number of levels per episode.") |
||
1226 | return |
||
1227 | end |
||
1228 | |||
1229 | if (ffi) then |
||
1230 | ffiC.C_UndefineLevel(vol, lev) |
||
1231 | end |
||
1232 | end |
||
1233 | |||
3375 | helixhorne | 1234 | local function defineXname(what, ffiCfuncname, X, name) |
1235 | if (ffi) then |
||
1236 | ffiC[ffiCfuncname](X, name) |
||
1237 | if (#name > 32) then |
||
1238 | warnprintf("%s %d name truncated to 32 characters.", what, X) |
||
1239 | end |
||
1240 | end |
||
1241 | return name |
||
1242 | end |
||
1243 | |||
3391 | helixhorne | 1244 | function Cmd.defineskillname(skillnum, name) |
3419 | helixhorne | 1245 | if (not (skillnum >= 0 and skillnum < conl.MAXSKILLS)) then |
3375 | helixhorne | 1246 | errprintf("skill number is negative or exceeds maximum skill count.") |
2764 | helixhorne | 1247 | return |
1248 | end |
||
1249 | |||
3375 | helixhorne | 1250 | name = defineXname("skill", "C_DefineSkillName", skillnum, name) |
1251 | g_data.skillname[skillnum] = name |
||
2764 | helixhorne | 1252 | end |
1253 | |||
4977 | hendricks2 | 1254 | function Cmd.undefineskill(skillnum) |
1255 | if (not (skillnum >= 0 and skillnum < conl.MAXSKILLS)) then |
||
1256 | errprintf("skill number is negative or exceeds maximum skill count.") |
||
1257 | return |
||
1258 | end |
||
1259 | |||
1260 | if (ffi) then |
||
1261 | ffiC.C_UndefineSkill(skillnum) |
||
1262 | end |
||
1263 | end |
||
1264 | |||
3391 | helixhorne | 1265 | function Cmd.definevolumename(vol, name) |
3419 | helixhorne | 1266 | if (not (vol >= 0 and vol < conl.MAXVOLUMES)) then |
2764 | helixhorne | 1267 | errprintf("volume number is negative or exceeds maximum volume count.") |
1268 | return |
||
1269 | end |
||
1270 | |||
3375 | helixhorne | 1271 | name = defineXname("volume", "C_DefineVolumeName", vol, name) |
3373 | helixhorne | 1272 | g_data.volname[vol] = name |
2764 | helixhorne | 1273 | end |
1274 | |||
5033 | hendricks2 | 1275 | function Cmd.definevolumeflags(vol, flags) |
1276 | if (not (vol >= 0 and vol < conl.MAXVOLUMES)) then |
||
1277 | errprintf("volume number is negative or exceeds maximum volume count.") |
||
1278 | return |
||
1279 | end |
||
1280 | |||
1281 | if (ffi) then |
||
1282 | ffiC.C_DefineVolumeFlags(vol, flags) |
||
1283 | end |
||
1284 | end |
||
1285 | |||
4977 | hendricks2 | 1286 | function Cmd.undefinevolume(vol) |
1287 | if (not (vol >= 0 and vol < conl.MAXVOLUMES)) then |
||
1288 | errprintf("volume number is negative or exceeds maximum volume count.") |
||
1289 | return |
||
1290 | end |
||
1291 | |||
1292 | if (ffi) then |
||
1293 | ffiC.C_UndefineVolume(vol) |
||
1294 | end |
||
1295 | end |
||
1296 | |||
3516 | helixhorne | 1297 | function Cmd.definegamefuncname(idx, name) |
1298 | local NUMGAMEFUNCTIONS = (ffi and ffiC.NUMGAMEFUNCTIONS or 56) |
||
1299 | if (not (idx >= 0 and idx < NUMGAMEFUNCTIONS)) then |
||
1300 | errprintf("function number exceeds number of game functions.") |
||
1301 | return |
||
1302 | end |
||
1303 | |||
1304 | assert(type(name)=="string") |
||
1305 | -- XXX: in place of C-CON's "invalid character in function name" report: |
||
3656 | helixhorne | 1306 | name = name:gsub("[^A-Za-z0-9]", "_") |
3516 | helixhorne | 1307 | |
1308 | if (ffi) then |
||
1309 | ffiC.C_DefineGameFuncName(idx, name) |
||
1310 | end |
||
1311 | end |
||
1312 | |||
3826 | helixhorne | 1313 | function Cmd.definegametype(idx, flags, name) |
1314 | if (not (idx >= 0 and idx < conl.MAXGAMETYPES)) then |
||
1315 | errprintf("gametype number exceeds maximum gametype count.") |
||
1316 | return |
||
1317 | end |
||
1318 | |||
1319 | if (ffi) then |
||
1320 | ffiC.C_DefineGameType(idx, flags, name) |
||
1321 | end |
||
1322 | end |
||
1323 | |||
3373 | helixhorne | 1324 | -- strip whitespace from front and back |
1325 | local function stripws(str) |
||
1326 | return str:match("^%s*(.*)%s*$") |
||
1327 | end |
||
1328 | |||
3391 | helixhorne | 1329 | function Cmd.definequote(qnum, quotestr) |
3357 | helixhorne | 1330 | if (not (qnum >= 0 and qnum < conl.MAXQUOTES)) then |
1331 | errprintf("quote number is negative or exceeds limit of %d.", conl.MAXQUOTES-1) |
||
3656 | helixhorne | 1332 | return "" |
3357 | helixhorne | 1333 | end |
2764 | helixhorne | 1334 | |
3373 | helixhorne | 1335 | quotestr = stripws(quotestr) |
3357 | helixhorne | 1336 | |
3511 | helixhorne | 1337 | if (#quotestr >= conl.MAXQUOTELEN) then |
1338 | -- NOTE: Actually, C_DefineQuote takes care of this! That is, |
||
1339 | -- standalone, the string isn't truncated. |
||
1340 | warnprintf("quote %d truncated to %d characters.", qnum, conl.MAXQUOTELEN-1) |
||
1341 | end |
||
1342 | |||
3357 | helixhorne | 1343 | if (ffi) then |
1344 | ffiC.C_DefineQuote(qnum, quotestr) |
||
1345 | end |
||
1346 | |||
1347 | g_data.quote[qnum] = quotestr |
||
3656 | helixhorne | 1348 | return "" |
2764 | helixhorne | 1349 | end |
1350 | |||
3865 | helixhorne | 1351 | local PROJ = {} |
1352 | for key, val in pairs(conl.PROJ) do |
||
1353 | -- Strip "PROJ_" |
||
1354 | PROJ[key:sub(6)] = val |
||
1355 | end |
||
1356 | |||
3463 | helixhorne | 1357 | function Cmd.defineprojectile(tilenum, what, val) |
3865 | helixhorne | 1358 | local ok = check.tile_idx(tilenum) |
3463 | helixhorne | 1359 | |
3865 | helixhorne | 1360 | if (what==PROJ.WORKSLIKE) then |
4152 | helixhorne | 1361 | check.reserved_bits(val, 2^21-1, "for PROJ_WORKSLIKE") |
3865 | helixhorne | 1362 | elseif (what==PROJ.SOUND or what==PROJ.ISOUND or what==PROJ.BSOUND) then |
1363 | ok = ok and (val==-1 or check.sound_idx(val)) |
||
1364 | elseif (what==PROJ.SPAWNS or what==PROJ.DECAL or what==PROJ.TRAIL) then |
||
1365 | ok = ok and (val==-1 or check.tile_idx(val)) |
||
1366 | end |
||
1367 | |||
3516 | helixhorne | 1368 | if (ffi and ok) then |
3463 | helixhorne | 1369 | ffiC.C_DefineProjectile(tilenum, what, val) |
1370 | end |
||
1371 | end |
||
1372 | |||
4372 | helixhorne | 1373 | -- <override>: override-set flags? The default is to bitwise OR with existing. |
1374 | function Cmd.xspriteflags(tilenum, flags, override) |
||
3865 | helixhorne | 1375 | local ok = check.tile_idx(tilenum) |
4372 | helixhorne | 1376 | check.reserved_bits(flags, conl.user_sflags, "for sprite flags") |
3516 | helixhorne | 1377 | |
4373 | helixhorne | 1378 | local loc = g_code.aflagsloc[tilenum] |
1379 | |||
1380 | if (override and loc ~= nil) then |
||
1381 | warnprintf("'spriteflags' after %s %d", loc[4], tilenum) |
||
1382 | contprintf(false, "at %s %d:%d", loc[1], loc[2], loc[3]) |
||
1383 | end |
||
1384 | |||
4841 | helixhorne | 1385 | -- Mark the last 'spriteflags' or 'sprite*' directive for the given actor. |
5036 | helixhorne | 1386 | g_code.aflagsloc[tilenum] = getLocation(format("'%s' for actor", g_lastkw)) |
4373 | helixhorne | 1387 | |
3516 | helixhorne | 1388 | if (ffi and ok) then |
4372 | helixhorne | 1389 | local tile = ffiC.g_tile[tilenum] |
1390 | tile._flags = bit.bor(override and 0 or tile._flags, flags) |
||
3516 | helixhorne | 1391 | end |
1392 | end |
||
1393 | |||
4291 | helixhorne | 1394 | function Cmd.precache(tilenum0, tilenum1, flagnum) |
1395 | local ok = check.tile_idx(tilenum0) and check.tile_idx(tilenum1) |
||
1396 | |||
1397 | if (ffi and ok) then |
||
4372 | helixhorne | 1398 | local tile = ffiC.g_tile[tilenum0] |
1399 | tile._cacherange = tilenum1; |
||
4291 | helixhorne | 1400 | if (flagnum) then |
4372 | helixhorne | 1401 | tile._flags = bit.bor(tile._flags, conl.SFLAG.SFLAG_CACHE) |
4291 | helixhorne | 1402 | end |
1403 | end |
||
1404 | end |
||
1405 | |||
3516 | helixhorne | 1406 | function Cmd.cheatkeys(sc1, sc2) |
1407 | if (ffi) then |
||
1408 | ffiC.CheatKeys[0] = sc1 |
||
1409 | ffiC.CheatKeys[1] = sc2 |
||
1410 | end |
||
1411 | end |
||
1412 | |||
1413 | function Cmd.setdefname(filename) |
||
1414 | assert(type(filename)=="string") |
||
1415 | if (ffi) then |
||
1416 | if (ffiC.C_SetDefName(filename) ~= 0) then |
||
1417 | error("OUT OF MEMORY", 0) |
||
1418 | end |
||
1419 | end |
||
1420 | end |
||
1421 | |||
4143 | helixhorne | 1422 | function Cmd.setcfgname(filename) |
1423 | assert(type(filename)=="string") |
||
1424 | if (ffi) then |
||
1425 | ffiC.C_SetCfgName(filename) |
||
1426 | end |
||
1427 | end |
||
1428 | |||
3391 | helixhorne | 1429 | function Cmd.gamestartup(...) |
3343 | helixhorne | 1430 | local args = {...} |
2764 | helixhorne | 1431 | |
3806 | helixhorne | 1432 | -- TODO: PRE13: detection of other g_scriptVersion. |
3343 | helixhorne | 1433 | if (#args ~= 26 and #args ~= 30) then |
2764 | helixhorne | 1434 | errprintf("must pass either 26 (1.3D) or 30 (1.5) values") |
1435 | return |
||
1436 | end |
||
1437 | |||
3355 | helixhorne | 1438 | if (ffi) then |
3343 | helixhorne | 1439 | -- running from EDuke32 |
3355 | helixhorne | 1440 | if (#args == 30) then |
3343 | helixhorne | 1441 | ffiC.g_scriptVersion = 14 |
1442 | end |
||
1443 | local params = ffi.new("int32_t [30]", args) |
||
1444 | ffiC.G_DoGameStartup(params) |
||
1445 | end |
||
1446 | |||
1447 | g_data.startup = args -- TODO: sanity-check them |
||
2764 | helixhorne | 1448 | end |
1449 | |||
3847 | helixhorne | 1450 | function Cmd.definesound(sndlabel, fn, ...) |
1451 | local sndnum |
||
1452 | |||
1453 | if (type(sndlabel)=="string") then |
||
4860 | helixhorne | 1454 | -- HANDLE_RAWDEFINE |
3847 | helixhorne | 1455 | local pos, minus, label = sndlabel:match("(.-):(.-):(.+)") |
1456 | sndnum = lookup.defined_label(tonumber(pos), minus, label) |
||
1457 | |||
1458 | if (ffi and g_dynsoundi and (sndnum>=0 and sndnum<conl.MAXSOUNDS)) then |
||
1459 | dynmap.maybe_init(g_dynsoundi, ffiC.g_dynSoundList) |
||
1460 | dynmap.maybe_process(g_dynsoundi, ffiC.g_dynSoundList, label, sndnum) |
||
1461 | end |
||
1462 | else |
||
1463 | assert(type(sndlabel)=="number") |
||
1464 | sndnum = sndlabel |
||
1465 | end |
||
1466 | |||
3357 | helixhorne | 1467 | if (not (sndnum >= 0 and sndnum < conl.MAXSOUNDS)) then |
3373 | helixhorne | 1468 | errprintf("sound number is negative or exceeds sound limit of %d", conl.MAXSOUNDS-1) |
2764 | helixhorne | 1469 | return |
1470 | end |
||
1471 | |||
3882 | helixhorne | 1472 | local params = {...} -- TODO: sanity-check them some more |
4152 | helixhorne | 1473 | check.reserved_bits(params[4], 31+128, "for sound flags") |
3882 | helixhorne | 1474 | |
3355 | helixhorne | 1475 | if (ffi) then |
1476 | local cparams = ffi.new("int32_t [5]", params) |
||
1477 | assert(type(fn)=="string") |
||
1478 | ffiC.C_DefineSound(sndnum, fn, cparams) |
||
1479 | end |
||
2764 | helixhorne | 1480 | |
1481 | g_data.sound[sndnum] = { fn=fn, params=params } |
||
1482 | end |
||
1483 | |||
3391 | helixhorne | 1484 | function Cmd.music(volnum, ...) |
3817 | helixhorne | 1485 | if (not (volnum >= 0 and volnum <= conl.MAXVOLUMES+1)) then |
4588 | helixhorne | 1486 | -- The passed volume number is 1-based. |
1487 | -- Both 0 and MAXVOLUMES+1 means "special music" |
||
1488 | errprintf("volume number must be between 0 and MAXVOLUMES+1=%d", conl.MAXVOLUMES+1) |
||
3558 | helixhorne | 1489 | return |
4588 | helixhorne | 1490 | elseif (volnum == conl.MAXVOLUMES+1) then |
1491 | warnprintf("volume number MAXVOLUMES+1 is discouraged, use 0 instead") |
||
3558 | helixhorne | 1492 | end |
1493 | |||
4588 | helixhorne | 1494 | if (volnum == 0) then |
1495 | volnum = conl.MAXVOLUMES+1 -- special music |
||
1496 | end |
||
1497 | |||
2764 | helixhorne | 1498 | local filenames = {...} |
4588 | helixhorne | 1499 | local MAXFNS = conl.MAXLEVELS |
2764 | helixhorne | 1500 | |
3567 | helixhorne | 1501 | if (#filenames > MAXFNS) then |
1502 | warnprintf("ignoring extraneous %d music file names", #filenames-MAXFNS) |
||
1503 | for i=MAXFNS+1,#filenames do |
||
2764 | helixhorne | 1504 | filenames[i] = nil |
1505 | end |
||
1506 | end |
||
1507 | |||
3373 | helixhorne | 1508 | if (ffi) then |
1509 | for i=1,#filenames do |
||
1510 | assert(type(filenames[i])=="string") |
||
3558 | helixhorne | 1511 | ffiC.C_DefineMusic(volnum-1, i-1, "/"..filenames[i]) |
3373 | helixhorne | 1512 | end |
1513 | end |
||
1514 | |||
2764 | helixhorne | 1515 | g_data.music[volnum] = filenames |
1516 | end |
||
1517 | |||
1518 | |||
3390 | helixhorne | 1519 | --- GAMEVARS / GAMEARRAYS |
1520 | |||
3503 | helixhorne | 1521 | function Cmd.gamearray(identifier, initsize) |
4152 | helixhorne | 1522 | if (check.sysvar_def_attempt(identifier)) then |
3503 | helixhorne | 1523 | return |
1524 | end |
||
1525 | |||
3533 | helixhorne | 1526 | if (not (initsize >= 0 and initsize < 0x7fffffff)) then |
3503 | helixhorne | 1527 | errprintf("invalid initial size %d for gamearray `%s'", initsize, identifier) |
1528 | return |
||
1529 | end |
||
1530 | |||
1531 | local oga = g_gamearray[identifier] |
||
1532 | if (oga) then |
||
3562 | helixhorne | 1533 | if (oga.sysp) then |
1534 | errprintf("attempt to define system gamearray `%s'", identifier) |
||
1535 | return |
||
1536 | elseif (initsize ~= oga.size) then |
||
3503 | helixhorne | 1537 | errprintf("duplicate gamearray definition `%s' has different size", identifier) |
1538 | return |
||
1539 | else |
||
1540 | warnprintf("duplicate gamearray definition `%s' ignored", identifier) |
||
1541 | return |
||
1542 | end |
||
1543 | end |
||
1544 | |||
1545 | if (g_gamevar[identifier]) then |
||
1546 | warnprintf("symbol `%s' already used for game variable", identifier) |
||
4153 | helixhorne | 1547 | inform.oldgv_location(identifier, false) |
3503 | helixhorne | 1548 | end |
1549 | |||
1550 | local ga = { name=mangle_name(identifier, "A"), size=initsize } |
||
1551 | g_gamearray[identifier] = ga |
||
1552 | |||
3891 | helixhorne | 1553 | addcode("if _S then") |
3518 | helixhorne | 1554 | addcodef("%s=_con._gamearray(%d)", ga.name, initsize) |
3891 | helixhorne | 1555 | addcode("end") |
3503 | helixhorne | 1556 | end |
1557 | |||
3391 | helixhorne | 1558 | function Cmd.gamevar(identifier, initval, flags) |
4152 | helixhorne | 1559 | if (check.sysvar_def_attempt(identifier)) then |
3503 | helixhorne | 1560 | return |
1561 | end |
||
1562 | |||
3431 | helixhorne | 1563 | if (bit.band(flags, bit.bnot(GVFLAG.USER_MASK)) ~= 0) then |
3390 | helixhorne | 1564 | -- TODO: a couple of the presumably safe ones |
3431 | helixhorne | 1565 | errprintf("gamevar flags other than 1, 2, 1024 or 131072: NYI or forbidden") |
3427 | helixhorne | 1566 | return |
3390 | helixhorne | 1567 | end |
1568 | |||
4112 | helixhorne | 1569 | local perPlayer = (bit.band(flags, GVFLAG.PERPLAYER) ~= 0) |
1570 | local perActor = (bit.band(flags, GVFLAG.PERACTOR) ~= 0) |
||
1571 | |||
1572 | if (perPlayer and perActor) then |
||
3390 | helixhorne | 1573 | errprintf("invalid gamevar flags: must be either PERPLAYER or PERACTOR, not both") |
3427 | helixhorne | 1574 | return |
3390 | helixhorne | 1575 | end |
1576 | |||
1577 | local ogv = g_gamevar[identifier] |
||
4570 | helixhorne | 1578 | -- handle NORESET or NODEFAULT |
4112 | helixhorne | 1579 | local isSessionVar = (bit.band(flags, GVFLAG.NODEFAULT) ~= 0) |
4140 | helixhorne | 1580 | local storeWithSavegames = (bit.band(flags, GVFLAG.NORESET) == 0) |
3390 | helixhorne | 1581 | |
4112 | helixhorne | 1582 | if (isSessionVar and (perPlayer or perActor)) then |
1583 | if (ogv == nil) then -- warn only once per gamevar |
||
4140 | helixhorne | 1584 | warnprintf("per-%s session gamevar `%s': NYI, made %s", |
4112 | helixhorne | 1585 | perPlayer and "player" or "actor", |
4140 | helixhorne | 1586 | identifier, |
1587 | perPlayer and "global" or "non-session") |
||
4112 | helixhorne | 1588 | end |
1589 | |||
1590 | if (perActor) then |
||
1591 | flags = bit.band(flags, bit.bnot(GVFLAG.NODEFAULT)) |
||
1592 | isSessionVar = false |
||
1593 | elseif (perPlayer) then |
||
1594 | flags = bit.band(flags, bit.bnot(GVFLAG.PERPLAYER)) |
||
1595 | perPlayer = false |
||
1596 | end |
||
1597 | end |
||
1598 | |||
3390 | helixhorne | 1599 | if (ogv ~= nil) then |
4581 | helixhorne | 1600 | local oflags = bit.band(ogv.flags, bit.bnot(GVFLAG.CON_PERPLAYER)) |
1601 | |||
3419 | helixhorne | 1602 | if (oflags ~= flags) then |
4112 | helixhorne | 1603 | if (bit.band(oflags, GVFLAG.SYSTEM) ~= 0 and not isSessionVar) then |
3419 | helixhorne | 1604 | -- Attempt to override a system gamevar. See if it's read-only... |
1605 | if (bit.band(oflags, GVFLAG.READONLY) ~= 0) then |
||
1606 | errprintf("attempt to override read-only system gamevar `%s'", identifier) |
||
3427 | helixhorne | 1607 | return |
3419 | helixhorne | 1608 | end |
1609 | |||
1610 | local flagsnosys = bit.band(oflags, bit.bnot(GVFLAG.SYSTEM)) |
||
3439 | helixhorne | 1611 | if (flagsnosys ~= flags and g_warn["system-gamevar"]) then |
3419 | helixhorne | 1612 | warnprintf("overrode initial value of `%s', but kept ".. |
1613 | "flags (%d)", identifier, flagsnosys) |
||
1614 | end |
||
1615 | |||
3563 | helixhorne | 1616 | if (ogv.rbits and bit.band(ogv.rbits, initval)~=0) then |
1617 | warnprintf("set one or more reserved bits (0x%s) in overriding `%s'", |
||
1618 | bit.tohex(bit.band(ogv.rbits, initval)), identifier) |
||
1619 | end |
||
1620 | |||
3574 | helixhorne | 1621 | local linestr = "--"..getlinecol(g_lastkwpos) |
1622 | |||
3419 | helixhorne | 1623 | -- Emit code to set the variable at Lua parse time. |
3891 | helixhorne | 1624 | -- XXX: How does this interact with savegame restoration? |
3419 | helixhorne | 1625 | if (bit.band(oflags, GVFLAG.PERPLAYER) ~= 0) then |
3848 | helixhorne | 1626 | -- Replace player index by 0. PLAYER_0. |
3563 | helixhorne | 1627 | -- TODO_MP: init for all players. |
3419 | helixhorne | 1628 | local pvar, numrepls = ogv.name:gsub("_pli", "0") |
1629 | assert(numrepls>=1) |
||
3574 | helixhorne | 1630 | addcodef("%s=%d%s", pvar, initval, linestr) |
3419 | helixhorne | 1631 | else |
3574 | helixhorne | 1632 | addcodef("%s=%d%s", ogv.name, initval, linestr) |
3419 | helixhorne | 1633 | end |
3427 | helixhorne | 1634 | return |
3419 | helixhorne | 1635 | end |
1636 | |||
4581 | helixhorne | 1637 | errprintf("duplicate definition of gamevar `%s' has different flags (new: %x, old: %x)", identifier, flags, oflags) |
4153 | helixhorne | 1638 | inform.oldgv_location(identifier, true) |
3427 | helixhorne | 1639 | return |
3390 | helixhorne | 1640 | else |
4151 | helixhorne | 1641 | warnprintf("duplicate definition of gamevar `%s' ignored", identifier) |
4153 | helixhorne | 1642 | inform.oldgv_location(identifier, false) |
3427 | helixhorne | 1643 | return |
3390 | helixhorne | 1644 | end |
1645 | end |
||
1646 | |||
1647 | local ltype = g_labeltype[identifier] |
||
1648 | if (ltype ~= nil) then |
||
4151 | helixhorne | 1649 | warnprintf("Symbol `%s' already used for a defined %s.", identifier, LABEL[ltype]) |
4153 | helixhorne | 1650 | inform.olddef_location(identifier, false) |
3390 | helixhorne | 1651 | end |
1652 | |||
4112 | helixhorne | 1653 | if (isSessionVar) then |
1654 | if (g_numSessionVars == conl.MAXSESSIONVARS) then |
||
1655 | errprintf("Declared too many session gamevars (flag 1024), can have at most %d.", |
||
1656 | conl.MAXSESSIONVARS) |
||
1657 | return |
||
1658 | end |
||
1659 | |||
1660 | -- Declare new session gamevar. |
||
4570 | helixhorne | 1661 | local gv = { name=format("_gv._sessionVar[%d]", g_numSessionVars), |
1662 | flags=flags, loc=getLocation(), used=0 } |
||
4112 | helixhorne | 1663 | g_numSessionVars = g_numSessionVars+1 |
4570 | helixhorne | 1664 | |
1665 | g_gamevar[identifier] = gv; |
||
1666 | -- Initialize it (i.e. set to the declared initial value) on first run, |
||
1667 | -- but not from savegames. |
||
1668 | addcodef("if _S then %s=%d end", gv.name, initval) |
||
1669 | |||
4112 | helixhorne | 1670 | return |
1671 | end |
||
1672 | |||
4299 | helixhorne | 1673 | local gv = { name=mangle_name(identifier, "V"), flags=flags, loc=getLocation(), used=0 } |
3390 | helixhorne | 1674 | g_gamevar[identifier] = gv |
1675 | |||
4140 | helixhorne | 1676 | if (storeWithSavegames) then |
1677 | addcode("if _S then") |
||
1678 | end |
||
3891 | helixhorne | 1679 | |
4112 | helixhorne | 1680 | if (perActor) then |
3798 | helixhorne | 1681 | addcodef("%s=_con.actorvar(%d)", gv.name, initval) |
4112 | helixhorne | 1682 | elseif (perPlayer and g_cgopt["playervar"]) then |
3842 | helixhorne | 1683 | gv.flags = bit.bor(gv.flags, GVFLAG.CON_PERPLAYER) |
1684 | addcodef("%s=_con.playervar(%d)", gv.name, initval) |
||
3390 | helixhorne | 1685 | else |
3518 | helixhorne | 1686 | addcodef("%s=%d", gv.name, initval) |
3390 | helixhorne | 1687 | end |
3891 | helixhorne | 1688 | |
4140 | helixhorne | 1689 | if (storeWithSavegames) then |
1690 | addcode("end") |
||
1691 | end |
||
3390 | helixhorne | 1692 | end |
1693 | |||
3817 | helixhorne | 1694 | function Cmd.dynamicremap() |
1695 | if (g_dyntilei==nil) then |
||
1696 | print("Using dynamic tile remapping"); |
||
3847 | helixhorne | 1697 | g_dyntilei = {}; |
3817 | helixhorne | 1698 | end |
1699 | end |
||
1700 | |||
3847 | helixhorne | 1701 | function Cmd.dynamicsoundremap() |
1702 | if (g_dynsoundi==nil) then |
||
1703 | print("Using dynamic sound remapping"); |
||
1704 | g_dynsoundi = {}; |
||
1705 | end |
||
1706 | end |
||
1707 | |||
3503 | helixhorne | 1708 | function lookup.gamearray(identifier) |
1709 | local ga = g_gamearray[identifier] |
||
1710 | if (ga == nil) then |
||
1711 | errprintf("symbol `%s' is not a game array", identifier) |
||
1712 | return "_INVALIDGA" |
||
1713 | end |
||
1714 | return ga.name |
||
1715 | end |
||
1716 | |||
3842 | helixhorne | 1717 | local function thisactor_to_pli(var) |
1718 | return (var=="_aci") and "_pli" or var |
||
1719 | end |
||
1720 | |||
4111 | helixhorne | 1721 | function lookup.error_not_gamevar(identifier) |
1722 | errprintf("symbol `%s' is not a game variable", identifier) |
||
1723 | return "_INVALIDGV" |
||
1724 | end |
||
1725 | |||
3469 | helixhorne | 1726 | -- <aorpvar>: code for actor or player index |
1727 | function lookup.gamevar(identifier, aorpvar, writable) |
||
3390 | helixhorne | 1728 | local gv = g_gamevar[identifier] |
1729 | |||
1730 | if (gv == nil) then |
||
4111 | helixhorne | 1731 | return lookup.error_not_gamevar(identifier) |
3390 | helixhorne | 1732 | end |
1733 | |||
3392 | helixhorne | 1734 | if (writable and bit.band(gv.flags, GVFLAG.READONLY) ~= 0) then |
3570 | helixhorne | 1735 | errprintf("gamevar `%s' is read-only", identifier) |
3392 | helixhorne | 1736 | return "_READONLYGV" |
1737 | end |
||
1738 | |||
4299 | helixhorne | 1739 | gv.used = bit.bor(gv.used, writable and 2 or 1) |
1740 | |||
3469 | helixhorne | 1741 | if (bit.band(gv.flags, GVFLAG.PERACTOR)~=0) then |
1742 | return format("%s[%s]", gv.name, aorpvar) |
||
3842 | helixhorne | 1743 | elseif (bit.band(gv.flags, GVFLAG.CON_PERPLAYER)~=0 and g_cgopt["playervar"]) then |
1744 | return format("%s[%s]", gv.name, thisactor_to_pli(aorpvar)) |
||
3390 | helixhorne | 1745 | else |
1746 | return gv.name |
||
1747 | end |
||
1748 | end |
||
1749 | |||
1750 | local function maybe_gamevar_Cmt(subj, pos, identifier) |
||
1751 | if (g_gamevar[identifier]) then |
||
3469 | helixhorne | 1752 | return true, lookup.gamevar(identifier, "_aci", false) |
3390 | helixhorne | 1753 | end |
1754 | end |
||
1755 | |||
1756 | |||
2594 | helixhorne | 1757 | ----==== patterns ====---- |
1758 | |||
1759 | ---- basic ones |
||
2616 | helixhorne | 1760 | -- Windows, *nix and Mac newlines all exist in the wild! |
1761 | local newline = "\r"*Pat("\n")^-1 + "\n" |
||
1762 | local EOF = Pat(-1) |
||
2594 | helixhorne | 1763 | local anychar = Pat(1) |
1764 | -- comments |
||
1765 | local comment = "/*" * match_until(anychar, "*/") * "*/" |
||
1766 | local linecomment = "//" * match_until(anychar, newline) |
||
1767 | local whitespace = Var("whitespace") |
||
1768 | local sp0 = whitespace^0 |
||
2616 | helixhorne | 1769 | -- This "WS+" pattern matches EOF too, so that a forgotten newline at EOF is |
1770 | -- properly handled |
||
1771 | local sp1 = whitespace^1 + EOF |
||
2594 | helixhorne | 1772 | local alpha = Range("AZ", "az") -- locale? |
1773 | local alphanum = alpha + Range("09") |
||
2616 | helixhorne | 1774 | --local alnumtok = alphanum + Set("{}/\\*-_.") -- see isaltok() in gamedef.c |
2594 | helixhorne | 1775 | |
3390 | helixhorne | 1776 | --- Basic lexical elements ("tokens"). See the final grammar ("Grammar") for |
1777 | --- their definitions. |
||
3391 | helixhorne | 1778 | local tok = |
1779 | { |
||
1780 | maybe_minus = (Pat("-") * sp0)^-1, |
||
1781 | number = Var("t_number"), |
||
2594 | helixhorne | 1782 | |
4800 | helixhorne | 1783 | -- Valid identifier names are disjoint from keywords! |
3432 | helixhorne | 1784 | -- XXX: CON is more permissive with identifier name characters: |
3391 | helixhorne | 1785 | identifier = Var("t_identifier"), |
3432 | helixhorne | 1786 | -- This one matches keywords, too: |
3391 | helixhorne | 1787 | identifier_all = Var("t_identifier_all"), |
4860 | helixhorne | 1788 | |
3391 | helixhorne | 1789 | define = Var("t_define"), |
3847 | helixhorne | 1790 | rawdefine = Var("t_rawdefine"), |
4860 | helixhorne | 1791 | actordefine = g_cgopt["names"] and Var("t_rawdefine") or Var("t_define"), |
1792 | |||
3391 | helixhorne | 1793 | move = Var("t_move"), |
1794 | ai = Var("t_ai"), |
||
1795 | action = Var("t_action"), |
||
2594 | helixhorne | 1796 | |
3432 | helixhorne | 1797 | -- NOTE: no chance to whitespace and double quotes in filenames: |
3391 | helixhorne | 1798 | filename = lpeg.C((anychar-Set(" \t\r\n\""))^1), |
1799 | newline_term_str = match_until(anychar, newline), |
||
2594 | helixhorne | 1800 | |
3391 | helixhorne | 1801 | rvar = Var("t_rvar"), |
1802 | wvar = Var("t_wvar"), |
||
3503 | helixhorne | 1803 | gamearray = Var("t_gamearray"), |
3391 | helixhorne | 1804 | |
3432 | helixhorne | 1805 | -- for definelevelname |
3391 | helixhorne | 1806 | time = lpeg.C(alphanum*alphanum^-1*":"*alphanum*alphanum^-1), |
3409 | helixhorne | 1807 | |
1808 | state_ends = Pat("ends") |
||
1809 | + POS() * "else" * sp1 * "ends" |
||
1810 | / function(pos) pwarnprintf(pos, "stray `else' at end of state") end, |
||
3391 | helixhorne | 1811 | } |
1812 | |||
1813 | |||
2594 | helixhorne | 1814 | ---- helper patterns / pattern constructing functions |
3391 | helixhorne | 1815 | local maybe_quoted_filename = ('"' * tok.filename * '"' + tok.filename) |
2616 | helixhorne | 1816 | -- empty string is handled too; we must not eat the newline then! |
2749 | helixhorne | 1817 | local newline_term_string = (#newline + EOF)*lpeg.Cc("") |
3391 | helixhorne | 1818 | + (whitespace-newline)^1 * lpeg.C(tok.newline_term_str) |
2594 | helixhorne | 1819 | |
1820 | |||
3391 | helixhorne | 1821 | -- (sp1 * tok.define) repeated exactly n times |
2594 | helixhorne | 1822 | local function n_defines(n) -- works well only for small n |
1823 | local pat = Pat(true) |
||
1824 | for i=1,n do |
||
3391 | helixhorne | 1825 | pat = sp1 * tok.define * pat |
2594 | helixhorne | 1826 | end |
1827 | return pat |
||
1828 | end |
||
1829 | |||
1830 | |||
3503 | helixhorne | 1831 | local D, R, W, I, GARI, AC, MV, AI = -1, -2, -3, -4, -5, -6, -7, -8 |
1832 | local TOKEN_PATTERN = { [D]=tok.define, [R]=tok.rvar, [W]=tok.wvar, |
||
1833 | [I]=tok.identifier, [GARI]=tok.gamearray, |
||
3391 | helixhorne | 1834 | [AC]=tok.action, [MV]=tok.move, [AI]=tok.ai } |
2594 | helixhorne | 1835 | |
1836 | -- Generic command pattern, types given by varargs. |
||
1837 | -- The command name to be matched is attached later. |
||
1838 | -- Example: |
||
1839 | -- "command" writtenvar readvar def def: gencmd(W,R,D,D) |
||
3391 | helixhorne | 1840 | -- --> sp1 * tok.wvar * sp1 * tok.rvar * sp1 * tok.define * sp1 * tok.define |
2594 | helixhorne | 1841 | -- "command_with_no_args": gencmd() |
1842 | -- --> Pat(true) |
||
1843 | local function cmd(...) |
||
1844 | local pat = Pat(true) |
||
1845 | local vartypes = {...} |
||
1846 | |||
1847 | for i=1,#vartypes do |
||
2765 | helixhorne | 1848 | pat = pat * sp1 * assert(TOKEN_PATTERN[vartypes[i]]) |
2594 | helixhorne | 1849 | end |
1850 | |||
1851 | return pat |
||
1852 | end |
||
1853 | |||
1854 | |||
1855 | -- The command names will be attached to the front of the patterns later! |
||
1856 | |||
1857 | --== Top level CON commands ==-- |
||
2616 | helixhorne | 1858 | -- XXX: many of these are also allowed inside actors/states/events in CON. |
3391 | helixhorne | 1859 | local Couter = { |
2594 | helixhorne | 1860 | --- 1. Preprocessor |
3516 | helixhorne | 1861 | include = sp1 * maybe_quoted_filename |
1862 | / Cmd.include, |
||
1863 | includedefault = cmd() |
||
1864 | / Cmd.NYI("`includedefault'"), |
||
1865 | define = cmd(I,D) |
||
3916 | helixhorne | 1866 | / Define.label, |
2594 | helixhorne | 1867 | |
1868 | --- 2. Defines and Meta-Settings |
||
3516 | helixhorne | 1869 | dynamicremap = cmd() |
3817 | helixhorne | 1870 | / Cmd.dynamicremap, |
3845 | helixhorne | 1871 | dynamicsoundremap = cmd() |
3847 | helixhorne | 1872 | / Cmd.dynamicsoundremap, |
3516 | helixhorne | 1873 | setcfgname = sp1 * tok.filename |
4143 | helixhorne | 1874 | / Cmd.setcfgname, |
3516 | helixhorne | 1875 | setdefname = sp1 * tok.filename |
1876 | / Cmd.setdefname, |
||
1877 | setgamename = newline_term_string |
||
1878 | / Cmd.nyi("`setgamename'"), |
||
2594 | helixhorne | 1879 | |
3516 | helixhorne | 1880 | precache = cmd(D,D,D) |
4291 | helixhorne | 1881 | / Cmd.precache, |
3259 | helixhorne | 1882 | scriptsize = cmd(D) |
1883 | / "", -- no-op |
||
3516 | helixhorne | 1884 | cheatkeys = cmd(D,D) |
1885 | / Cmd.cheatkeys, |
||
2594 | helixhorne | 1886 | |
3516 | helixhorne | 1887 | definecheat = newline_term_string -- XXX: actually tricker syntax (TS) |
1888 | , -- / Cmd.nyi("`definecheat'"), |
||
1889 | definegamefuncname = sp1 * tok.define * newline_term_string -- XXX: TS? |
||
1890 | / Cmd.definegamefuncname, |
||
1891 | definegametype = n_defines(2) * newline_term_string |
||
3826 | helixhorne | 1892 | / Cmd.definegametype, |
3391 | helixhorne | 1893 | definelevelname = n_defines(2) * sp1 * tok.filename * sp1 * tok.time * sp1 * tok.time * |
3516 | helixhorne | 1894 | newline_term_string |
1895 | / Cmd.definelevelname, |
||
1896 | defineskillname = sp1 * tok.define * newline_term_string |
||
1897 | / Cmd.defineskillname, |
||
1898 | definevolumename = sp1 * tok.define * newline_term_string |
||
1899 | / Cmd.definevolumename, |
||
2594 | helixhorne | 1900 | |
3516 | helixhorne | 1901 | definequote = sp1 * tok.define * newline_term_string |
1902 | / Cmd.definequote, |
||
1903 | defineprojectile = cmd(D,D,D) |
||
1904 | / Cmd.defineprojectile, |
||
3847 | helixhorne | 1905 | definesound = sp1 * tok.rawdefine * sp1 * maybe_quoted_filename * n_defines(5) |
3516 | helixhorne | 1906 | / Cmd.definesound, |
2594 | helixhorne | 1907 | |
2763 | helixhorne | 1908 | -- NOTE: gamevar.ogg and the like is OK, too |
3516 | helixhorne | 1909 | music = sp1 * tok.define * match_until(sp1 * tok.filename, sp1 * conl.keyword * sp1) |
1910 | / Cmd.music, |
||
2594 | helixhorne | 1911 | |
5033 | hendricks2 | 1912 | definevolumeflags = cmd(D,D) |
1913 | / Cmd.definevolumeflags, |
||
1914 | |||
4977 | hendricks2 | 1915 | undefinelevel = cmd(D,D) |
1916 | / Cmd.undefinelevel, |
||
1917 | undefineskill = cmd(D) |
||
1918 | / Cmd.undefineskill, |
||
1919 | undefinevolume = cmd(D) |
||
1920 | / Cmd.undefinevolume, |
||
1921 | |||
2594 | helixhorne | 1922 | --- 3. Game Settings |
3254 | helixhorne | 1923 | -- gamestartup has 26/30 fixed defines, depending on 1.3D/1.5 version: |
3516 | helixhorne | 1924 | gamestartup = (sp1 * tok.define)^26 |
1925 | / Cmd.gamestartup, |
||
1926 | spritenopal = cmd(D) |
||
4372 | helixhorne | 1927 | / function(tilenum, flags) Cmd.xspriteflags(tilenum, conl.SFLAG.SFLAG_NOPAL) end, |
3516 | helixhorne | 1928 | spritenoshade = cmd(D) |
4372 | helixhorne | 1929 | / function(tilenum, flags) Cmd.xspriteflags(tilenum, conl.SFLAG.SFLAG_NOSHADE) end, |
3516 | helixhorne | 1930 | spritenvg = cmd(D) |
4372 | helixhorne | 1931 | / function(tilenum, flags) Cmd.xspriteflags(tilenum, conl.SFLAG.SFLAG_NVG) end, |
3516 | helixhorne | 1932 | spriteshadow = cmd(D) |
4372 | helixhorne | 1933 | / function(tilenum, flags) Cmd.xspriteflags(tilenum, conl.SFLAG.SFLAG_SHADOW) end, |
2594 | helixhorne | 1934 | |
4841 | helixhorne | 1935 | spriteflags = (sp1 * tok.define)^2 -- also see inner |
1936 | / function(tilenum, ...) Cmd.xspriteflags(tilenum, bit.bor(...), true) end, |
||
2594 | helixhorne | 1937 | |
1938 | --- 4. Game Variables / Arrays |
||
3516 | helixhorne | 1939 | gamevar = cmd(I,D,D) |
1940 | / Cmd.gamevar, |
||
1941 | gamearray = cmd(I,D) |
||
1942 | / Cmd.gamearray, |
||
2594 | helixhorne | 1943 | |
1944 | --- 5. Top level commands that are also run-time commands |
||
3516 | helixhorne | 1945 | move = sp1 * tok.identifier * (sp1 * tok.define)^-2 -- hvel, vvel |
3916 | helixhorne | 1946 | / function(...) Define.composite(LABEL.MOVE, ...) end, |
3226 | helixhorne | 1947 | |
2765 | helixhorne | 1948 | -- startframe, numframes, viewtype, incval, delay: |
3516 | helixhorne | 1949 | action = sp1 * tok.identifier * (sp1 * tok.define)^-5 |
3916 | helixhorne | 1950 | / function(...) Define.composite(LABEL.ACTION, ...) end, |
2594 | helixhorne | 1951 | |
2765 | helixhorne | 1952 | -- action, move, flags...: |
3391 | helixhorne | 1953 | ai = sp1 * tok.identifier * (sp1 * tok.action * |
3516 | helixhorne | 1954 | (sp1 * tok.move * (sp1 * tok.define)^0)^-1 |
1955 | )^-1 |
||
3916 | helixhorne | 1956 | / function(...) Define.composite(LABEL.AI, ...) end, |
2765 | helixhorne | 1957 | |
2594 | helixhorne | 1958 | --- 6. Deprecated TLCs |
1959 | betaname = newline_term_string, |
||
1960 | enhanced = cmd(D), |
||
1961 | } |
||
1962 | |||
1963 | |||
1964 | --== Run time CON commands ==-- |
||
1965 | --- 1. Gamevar Operators |
||
3854 | helixhorne | 1966 | local Op = {} |
1967 | Op.var = cmd(W,D) |
||
1968 | Op.varvar = cmd(W,R) |
||
2594 | helixhorne | 1969 | |
3949 | helixhorne | 1970 | function Op.var_common(thecmd, defaultop, trapop, wrapop) |
1971 | local theop = |
||
1972 | g_cgopt["trapv"] and trapop or |
||
1973 | g_cgopt["wrapv"] and wrapop or |
||
1974 | assert(defaultop) |
||
1975 | |||
1976 | if (#theop <= 2) then |
||
1977 | return thecmd / ("%1=%1"..theop.."%2") |
||
3392 | helixhorne | 1978 | else |
3949 | helixhorne | 1979 | return thecmd / ("%1="..theop.."(%1,%2)") |
3392 | helixhorne | 1980 | end |
1981 | end |
||
1982 | |||
3949 | helixhorne | 1983 | function Op.varf(...) |
1984 | return Op.var_common(Op.var, ...) |
||
3392 | helixhorne | 1985 | end |
1986 | |||
3949 | helixhorne | 1987 | function Op.varvarf(...) |
1988 | return Op.var_common(Op.varvar, ...) |
||
1989 | end |
||
1990 | |||
2616 | helixhorne | 1991 | -- Allow nesting... stuff like |
1992 | -- ifvarl actorvar[sprite[THISACTOR].owner].burning 0 |
||
1993 | -- is kinda breaking the classic "no array nesting" rules |
||
3324 | helixhorne | 1994 | -- (if there ever were any) but making our life harder else. |
3391 | helixhorne | 1995 | local arraypat = sp0 * "[" * sp0 * tok.rvar * sp0 * "]" |
4287 | helixhorne | 1996 | -- For {get,set}userdef: |
1997 | local arraypat_maybe_empty = sp0 * "[" * sp0 * (tok.rvar * sp0)^-1 * "]" |
||
2616 | helixhorne | 1998 | |
3854 | helixhorne | 1999 | -- Table of various patterns that are (parts of) more complex inner commands. |
2000 | local patt = {} |
||
2001 | |||
3432 | helixhorne | 2002 | -- Have to bite the bullet here and list actor/player members with second |
2003 | -- parameters, even though it's ugly to make it part of the syntax. Also, |
||
2004 | -- stuff like |
||
2616 | helixhorne | 2005 | -- actor[xxx].loogiex parm2 x |
3432 | helixhorne | 2006 | -- will be wrongly accepted at the parsing stage (loogiex is player's member) |
2007 | -- because we don't discriminate between actor and player here. |
||
3854 | helixhorne | 2008 | patt.parm2member = lpeg.C(Pat("htg_t") + "loogiex" + "loogiey" + "ammo_amount" + |
4581 | helixhorne | 2009 | "weaprecs" + "gotweapon" + "pals" + "Pals" + "max_ammo_amount") * sp1 * tok.rvar |
2010 | -- XXX: "pals" + "Pals": this sucks! It means that we for this list of members |
||
2011 | -- requiring second parameters, we will have to enumerate all lower/uppercase |
||
2012 | -- instances encountered in the wild. |
||
2594 | helixhorne | 2013 | |
3854 | helixhorne | 2014 | -- The member name must match keywords, too (_all), because e.g. cstat is a |
2015 | -- member of sprite[]. |
||
2016 | patt.bothmember = sp0 * "." * sp0 * lpeg.Ct(patt.parm2member + tok.identifier_all) |
||
2017 | patt.singlemember = sp0 * "." * sp0 * tok.identifier_all |
||
2594 | helixhorne | 2018 | |
3854 | helixhorne | 2019 | patt.cmdgetstruct = -- get<structname>[<idx>].<member> (<parm2>)? <<var>> |
2020 | arraypat * patt.bothmember * sp1 * tok.wvar |
||
2594 | helixhorne | 2021 | |
3854 | helixhorne | 2022 | patt.cmdsetstruct = -- set<structname>[<idx>].<<member>> (<parm2>)? <var> |
2023 | arraypat * patt.bothmember * sp1 * tok.rvar |
||
2594 | helixhorne | 2024 | |
3854 | helixhorne | 2025 | patt.cmdgetperxvar = -- get<actor/player>var[<idx>].<varname> <<var>> |
2026 | arraypat * patt.singlemember * sp1 * tok.wvar |
||
2594 | helixhorne | 2027 | |
3854 | helixhorne | 2028 | patt.cmdsetperxvar = -- set<actor/player>var[<idx>].<<varname>> <var> |
2029 | arraypat * patt.singlemember * sp1 * tok.rvar |
||
2030 | |||
3433 | helixhorne | 2031 | -- Function generating code for a struct read/write access. |
2032 | local function StructAccess(Structname, writep, index, membertab) |
||
2033 | assert(type(membertab)=="table") |
||
3473 | helixhorne | 2034 | -- Lowercase the member name for CON compatibility |
2035 | local member, parm2 = membertab[1]:lower(), membertab[2] |
||
2594 | helixhorne | 2036 | |
3454 | helixhorne | 2037 | local MemberCode = conl.StructAccessCode[Structname] or conl.StructAccessCode2[Structname] |
3433 | helixhorne | 2038 | -- Look up array+member name first, e.g. "spriteext[%s].angoff". |
3454 | helixhorne | 2039 | local armembcode = MemberCode[member] |
3433 | helixhorne | 2040 | if (armembcode == nil) then |
3473 | helixhorne | 2041 | errprintf("%s: invalid %s member `.%s'", g_lastkw, Structname, member) |
3433 | helixhorne | 2042 | return "_MEMBINVALID" |
2043 | end |
||
3432 | helixhorne | 2044 | |
4195 | helixhorne | 2045 | -- Function checking a literal number for being OK for assignment to this |
2046 | -- member. Can also be a table {min, max}. See con_lang.lua, LITERAL_CHECKING. |
||
2047 | local lit_ok_func_or_table |
||
2048 | |||
3433 | helixhorne | 2049 | if (type(armembcode)=="table") then |
2050 | -- Read and write accesses differ. |
||
4195 | helixhorne | 2051 | if (writep) then |
2052 | lit_ok_func_or_table = armembcode[3] |
||
2053 | end |
||
3433 | helixhorne | 2054 | armembcode = armembcode[writep and 2 or 1] |
2055 | if (armembcode==nil) then |
||
2056 | errprintf("%s access to %s[].%s is not available", |
||
2057 | writep and "write" or "read", Structname, member) |
||
2058 | return "_MEMBNOACCESS" |
||
3432 | helixhorne | 2059 | end |
3433 | helixhorne | 2060 | end |
3432 | helixhorne | 2061 | |
3473 | helixhorne | 2062 | if (Structname~="userdef") then |
2063 | -- Count number of parameters ("%s"), don't count "%%s". |
||
2064 | local _, numparms = armembcode:gsub("[^%%]%%s", "", 2) |
||
2065 | if (#membertab ~= numparms) then |
||
2066 | local nums = { "one", "two" } |
||
2067 | errprintf("%s[].%s has %s parameter%s, but %s given", Structname, |
||
2068 | member, nums[numparms], numparms==1 and "" or "s", |
||
2069 | nums[#membertab]) |
||
2070 | return "_MEMBINVPARM" |
||
2071 | end |
||
3433 | helixhorne | 2072 | end |
3432 | helixhorne | 2073 | |
3516 | helixhorne | 2074 | -- THISACTOR special meanings |
2075 | if (Structname=="player" or Structname=="input") then |
||
2076 | index = thisactor_to_pli(index) |
||
2077 | elseif (Structname=="sector") then |
||
2078 | if (index=="_aci") then |
||
2079 | index = SPS".sectnum" |
||
2080 | end |
||
2081 | end |
||
2082 | |||
3444 | helixhorne | 2083 | -- METHOD_MEMBER |
2084 | local ismethod = (armembcode:find("%%s",1,true)~=nil) |
||
2085 | -- If ismethod is true, then the formatted string will now have an "%s" |
||
3561 | helixhorne | 2086 | local code |
3473 | helixhorne | 2087 | |
2088 | if (Structname=="userdef") then |
||
2089 | -- assert(index==nil) |
||
2090 | assert(parm2==nil) |
||
3561 | helixhorne | 2091 | code = format(armembcode, parm2) |
3473 | helixhorne | 2092 | else |
3561 | helixhorne | 2093 | code = format(armembcode, index, parm2) |
3473 | helixhorne | 2094 | end |
3561 | helixhorne | 2095 | |
2096 | if (csapp()) then |
||
2097 | if (Structname=="player") then |
||
2098 | code = code:gsub("^player%[_pli%]", "_ps") |
||
2099 | elseif (Structname=="sprite") then |
||
2100 | code = code:gsub("^actor%[_aci%]", "_a") |
||
2101 | code = code:gsub("^sprite%[_aci%]", "_spr") |
||
2102 | end |
||
2103 | end |
||
2104 | |||
4195 | helixhorne | 2105 | return code, ismethod, lit_ok_func_or_table |
3433 | helixhorne | 2106 | end |
2107 | |||
2108 | function lookup.array_expr(writep, structname, index, membertab) |
||
2109 | if (conl.StructAccessCode[structname] == nil) then |
||
3503 | helixhorne | 2110 | -- Try a gamearray |
3516 | helixhorne | 2111 | local ganame = g_gamearray[structname] and lookup.gamearray(structname) |
2112 | if (ganame == nil) then |
||
3503 | helixhorne | 2113 | if (structname=="actorvar") then |
2114 | -- actorvar[] inline array expr |
||
2115 | -- XXX: kind of CODEDUP with GetOrSetPerxvarCmd() factory |
||
2116 | local gv = g_gamevar[structname] |
||
2117 | if (gv and bit.band(gv.flags, GVFLAG.PERX_MASK)~=GVFLAG.PERACTOR) then |
||
3570 | helixhorne | 2118 | errprintf("gamevar `%s' is not per-actor", structname, "actor") |
3503 | helixhorne | 2119 | end |
2120 | |||
2121 | if (membertab == nil) then |
||
2122 | errprintf("actorvar[] requires a pseudo member (gamevar) name") |
||
2123 | return "_INVALIDAV" |
||
2124 | end |
||
2125 | |||
2126 | if (#membertab > 1) then |
||
2127 | errprintf("actorvar[] cannot be used with a second parameter") |
||
2128 | return "_INVALIDAV" |
||
2129 | end |
||
2130 | |||
4299 | helixhorne | 2131 | if (gv) then |
2132 | gv.used = bit.bor(gv.used, writep and 2 or 1) |
||
2133 | end |
||
2134 | |||
3503 | helixhorne | 2135 | assert(#membertab == 1) |
2136 | return lookup.gamevar(membertab[1], index, writep) |
||
2137 | end |
||
2138 | |||
2139 | errprintf("symbol `%s' is neither a struct nor a gamearray", structname) |
||
2140 | return "_INVALIDAR" |
||
2141 | end |
||
2142 | |||
2143 | if (membertab ~= nil) then |
||
2144 | errprintf("gamearrays cannot be indexed with member names") |
||
2145 | return "_INVALIDAR" |
||
2146 | end |
||
2147 | |||
3516 | helixhorne | 2148 | assert(type(ganame)=="string") |
2149 | return format("%s[%s]", ganame, index) |
||
3433 | helixhorne | 2150 | end |
2151 | |||
3444 | helixhorne | 2152 | local membercode, ismethod = StructAccess(structname, writep, index, membertab) |
2153 | -- Written METHOD_MEMBER syntax not supported as "qwe:method(asd) = val" |
||
2154 | -- isn't valid Lua syntax. |
||
2155 | assert(not (writep and ismethod)) |
||
2156 | return membercode |
||
3433 | helixhorne | 2157 | end |
2158 | |||
2159 | local Access = |
||
2160 | { |
||
2161 | sector = function(...) return StructAccess("sector", ...) end, |
||
2162 | wall = function(...) return StructAccess("wall", ...) end, |
||
2163 | xsprite = function(...) return StructAccess("sprite", ...) end, |
||
2164 | player = function(...) return StructAccess("player", ...) end, |
||
3454 | helixhorne | 2165 | |
2166 | tspr = function(...) return StructAccess("tspr", ...) end, |
||
3463 | helixhorne | 2167 | projectile = function(...) return StructAccess("projectile", ...) end, |
3466 | helixhorne | 2168 | thisprojectile = function(...) return StructAccess("thisprojectile", ...) end, |
3473 | helixhorne | 2169 | userdef = function(...) return StructAccess("userdef", ...) end, |
3477 | helixhorne | 2170 | input = function(...) return StructAccess("input", ...) end, |
3432 | helixhorne | 2171 | } |
2172 | |||
3473 | helixhorne | 2173 | local function GetStructCmd(accessfunc, pattern) |
3854 | helixhorne | 2174 | return (pattern or patt.cmdgetstruct) / |
3469 | helixhorne | 2175 | function(idx, memb, var) |
3432 | helixhorne | 2176 | return format("%s=%s", var, accessfunc(false, idx, memb)) |
3469 | helixhorne | 2177 | end |
3432 | helixhorne | 2178 | end |
2179 | |||
3473 | helixhorne | 2180 | local function SetStructCmd(accessfunc, pattern) |
3469 | helixhorne | 2181 | local function capfunc(idx, memb, var) |
4195 | helixhorne | 2182 | -- litok: function or table |
2183 | local membercode, ismethod, litok = accessfunc(true, idx, memb) |
||
2184 | |||
2185 | -- Light static checking for literal values being OK for member |
||
2186 | -- assignment. LITERAL_CHECKING. |
||
2187 | if (type(var)=="number" and litok) then |
||
2188 | if (type(litok)=="table" and not (var>=litok[1] and var<=litok[2]) or |
||
2189 | type(litok)=="function" and not litok(var)) then |
||
2190 | local member = memb[1]:lower() |
||
2191 | warnprintf("setting member '.%s' to %d will fail at game time", |
||
2192 | member, var) |
||
2193 | end |
||
3626 | helixhorne | 2194 | end |
4195 | helixhorne | 2195 | |
3444 | helixhorne | 2196 | if (ismethod) then |
2197 | -- METHOD_MEMBER syntax |
||
2198 | |||
2199 | -- BE EXTRA CAREFUL! We must be sure that percent characters have |
||
2200 | -- not been smuggled into the member code string via variable names |
||
2201 | -- etc. |
||
2202 | local _, numpercents = membercode:gsub("%%", "", 2) |
||
2203 | assert(numpercents==1) |
||
2204 | |||
2205 | return format(membercode, var) |
||
2206 | else |
||
2207 | return format("%s=%s", membercode, var) |
||
2208 | end |
||
3439 | helixhorne | 2209 | end |
3469 | helixhorne | 2210 | |
3854 | helixhorne | 2211 | return (pattern or patt.cmdsetstruct) / capfunc |
3439 | helixhorne | 2212 | end |
3432 | helixhorne | 2213 | |
3469 | helixhorne | 2214 | -- <Setp>: whether the perxvar is set |
2215 | local function GetOrSetPerxvarCmd(Setp, Actorp) |
||
2216 | local EXPECTED_PERX_BIT = Actorp and GVFLAG.PERACTOR or GVFLAG.PERPLAYER |
||
3854 | helixhorne | 2217 | local pattern = (Setp and patt.cmdsetperxvar or patt.cmdgetperxvar) |
3439 | helixhorne | 2218 | |
3469 | helixhorne | 2219 | local function capfunc(idx, perxvarname, var) |
2220 | local gv = g_gamevar[perxvarname] |
||
2221 | if (gv and bit.band(gv.flags, GVFLAG.PERX_MASK)~=EXPECTED_PERX_BIT) then |
||
3570 | helixhorne | 2222 | -- [gs]set*var for wrong gamevar type. See if it's a getactorvar, |
3909 | helixhorne | 2223 | -- in which case we may only warn and access that instead. Note |
2224 | -- that accesses of player gamevars with actor indices are usually |
||
2225 | -- meaningless. |
||
3570 | helixhorne | 2226 | local warnp = not Setp and Actorp and not g_warn["error-bad-getactorvar"] |
2227 | local xprintf = warnp and warnprintf or errprintf |
||
2228 | |||
2229 | xprintf("gamevar `%s' is not per-%s", perxvarname, Actorp and "actor" or "player") |
||
4266 | helixhorne | 2230 | |
2231 | if (warnp and bit.band(gv.flags, GVFLAG.PERX_MASK)==GVFLAG.PERPLAYER |
||
2232 | and g_cgopt["bad-getactorvar-use-pli"]) then |
||
2233 | -- For getactorvar[] accesses to per-player gamevars, if |
||
2234 | -- -fbad-getactorvar-use-pli is provided, use current player |
||
2235 | -- index, for compatibility with CON. |
||
2236 | idx = "_pli" |
||
2237 | end |
||
3469 | helixhorne | 2238 | end |
2239 | |||
3516 | helixhorne | 2240 | if (not Actorp) then |
2241 | -- THISACTOR -> player index for {g,s}etplayervar |
||
2242 | idx = thisactor_to_pli(idx) |
||
2243 | end |
||
2244 | |||
4299 | helixhorne | 2245 | if (gv) then |
2246 | gv.used = bit.bor(gv.used, Setp and 2 or 1) |
||
2247 | end |
||
2248 | |||
3469 | helixhorne | 2249 | if (Setp) then |
2250 | return format("%s=%s", lookup.gamevar(perxvarname, idx, true), var) |
||
2251 | else |
||
2252 | return format("%s=%s", var, lookup.gamevar(perxvarname, idx, false)) |
||
2253 | end |
||
2254 | end |
||
2255 | |||
2256 | return pattern / capfunc |
||
2257 | end |
||
2258 | |||
2259 | |||
3507 | helixhorne | 2260 | local function n_s_fmt(n) |
2261 | return string.rep("%s,", n-1).."%s" |
||
2262 | end |
||
2263 | |||
3431 | helixhorne | 2264 | -- Various inner command handling functions / string capture strings. |
3391 | helixhorne | 2265 | local handle = |
2266 | { |
||
3431 | helixhorne | 2267 | NYI = function() |
2268 | errprintf("command `%s' not yet implemented", g_lastkw) |
||
3523 | helixhorne | 2269 | return "" |
3431 | helixhorne | 2270 | end, |
2271 | |||
3516 | helixhorne | 2272 | dynNYI = function() |
3882 | helixhorne | 2273 | return format([[print(%q..":%d: `%s' not yet implemented")]], |
3516 | helixhorne | 2274 | g_filename, getlinecol(g_lastkwpos), g_lastkw) |
2275 | end, |
||
2276 | |||
3431 | helixhorne | 2277 | addlog = function() |
3882 | helixhorne | 2278 | return format("print(%q..':%d: addlog')", g_filename, getlinecol(g_lastkwpos)) |
3431 | helixhorne | 2279 | end, |
2280 | |||
2281 | addlogvar = function(val) |
||
3882 | helixhorne | 2282 | return format("printf(%q..':%d: addlogvar %%s', %s)", g_filename, getlinecol(g_lastkwpos), val) |
3431 | helixhorne | 2283 | end, |
2284 | |||
2285 | debug = function(val) |
||
3882 | helixhorne | 2286 | return format("print(%q..':%d: debug %d')", g_filename, getlinecol(g_lastkwpos), val) |
3431 | helixhorne | 2287 | end, |
2288 | |||
3524 | helixhorne | 2289 | getzrange = function(...) |
2290 | local v = {...} |
||
2291 | assert(#v == 10) -- 4R 4W 2R |
||
2292 | return format("%s,%s,%s,%s=_con._getzrange(%s,%s,%s,%s,%s,%s)", |
||
2293 | v[5], v[6], v[7], v[8], -- outargs |
||
2294 | v[1], v[2], v[3], v[4], v[9], v[10]) -- inargs |
||
2295 | end, |
||
2296 | |||
3491 | helixhorne | 2297 | hitscan = function(...) |
2298 | local v = {...} |
||
2299 | assert(#v == 14) -- 7R 6W 1R |
||
2300 | local vals = { |
||
2301 | v[8], v[9], v[10], v[11], v[12], v[13], -- outargs |
||
2302 | v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[14] -- inargs |
||
2303 | } |
||
2304 | return format("%s,%s,%s,%s,%s,%s=_con._hitscan(%s,%s,%s,%s,%s,%s,%s,%s)", |
||
2305 | unpack(vals)) |
||
2306 | end, |
||
2307 | |||
2308 | neartag = function(...) |
||
2309 | local v = {...} |
||
2310 | assert(#v == 11) -- 5R 4W 2R |
||
2311 | local vals = { |
||
2312 | v[6], v[7], v[8], v[9], -- outargs |
||
2313 | v[1], v[2], v[3], v[4], v[5], v[10], v[11] -- inargs |
||
2314 | } |
||
2315 | return format("%s,%s,%s,%s=_con._neartag(%s,%s,%s,%s,%s,%s,%s)", |
||
2316 | unpack(vals)) |
||
2317 | end, |
||
2318 | |||
3812 | helixhorne | 2319 | clipmove = function(noslidep, ...) |
2320 | local v = {...} |
||
2321 | assert(#v == 11) -- 3W 1R 1W 6R |
||
2322 | local vals = { |
||
2323 | v[1], v[2], v[3], v[5], -- outargs |
||
2324 | v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], -- inargs |
||
2325 | noslidep |
||
2326 | } |
||
2327 | return format("%s,%s,%s,%s=_con._clipmovex("..n_s_fmt(11)..")", |
||
2328 | unpack(vals)) |
||
2329 | end, |
||
2330 | |||
3391 | helixhorne | 2331 | palfrom = |