Subversion Repositories eduke32

Rev

Rev 4962 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 4962 Rev 4977
1
-- LunaCON CON to Lunatic translator
1
-- LunaCON CON to Lunatic translator
2
-- requires LPeg, http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html
2
-- requires LPeg, http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html
3
3
4
local require = require
4
local require = require
5
local lpeg = require("lpeg")
5
local lpeg = require("lpeg")
6
6
7
local bit
7
local bit
8
local math = require("math")
8
local math = require("math")
9
local string = require("string")
9
local string = require("string")
10
local table = require("table")
10
local table = require("table")
11
11
12
12
13
local arg = arg
13
local arg = arg
14
14
15
local assert = assert
15
local assert = assert
16
local error = error
16
local error = error
17
local ipairs = ipairs
17
local ipairs = ipairs
18
local loadstring = loadstring
18
local loadstring = loadstring
19
local pairs = pairs
19
local pairs = pairs
20
local pcall = pcall
20
local pcall = pcall
21
local print = print
21
local print = print
22
local setmetatable = setmetatable
22
local setmetatable = setmetatable
23
local tonumber = tonumber
23
local tonumber = tonumber
24
local tostring = tostring
24
local tostring = tostring
25
local type = type
25
local type = type
26
local unpack = unpack
26
local unpack = unpack
27
27
28
-- non-nil if running from EDuke32
28
-- non-nil if running from EDuke32
29
-- (read_into_string~=nil  iff  string.dump==nil)
29
-- (read_into_string~=nil  iff  string.dump==nil)
30
local read_into_string = read_into_string
30
local read_into_string = read_into_string
31
local ffi, ffiC
31
local ffi, ffiC
32
32
33
if (string.dump) then  -- running stand-alone
33
if (string.dump) then  -- running stand-alone
34
    local ljp = pcall(function() require("ffi") end)
34
    local ljp = pcall(function() require("ffi") end)
35
    -- "lbit" is the same module as LuaJIT's "bit" (LuaBitOp:
35
    -- "lbit" is the same module as LuaJIT's "bit" (LuaBitOp:
36
    -- http://bitop.luajit.org/), but under a different name for (IMO) less
36
    -- http://bitop.luajit.org/), but under a different name for (IMO) less
37
    -- confusion. Useful for running with Rio Lua for cross-checking.
37
    -- confusion. Useful for running with Rio Lua for cross-checking.
38
    bit = ljp and require("bit") or require("lbit")
38
    bit = ljp and require("bit") or require("lbit")
39
    require("strict")
39
    require("strict")
40
else
40
else
41
    bit = require("bit")
41
    bit = require("bit")
42
    ffi = require("ffi")
42
    ffi = require("ffi")
43
    ffiC = ffi.C
43
    ffiC = ffi.C
44
end
44
end
45
45
46
46
47
47
48
module("lunacon")
48
module("lunacon")
49
49
50
50
51
-- I think that the "too many pending calls/choices" is unavoidable in general.
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
52
-- This limit is of course still arbitrary, but writing long if/else cascades
53
-- in CON isn't pretty either (though sometimes necessary because nested switches
53
-- in CON isn't pretty either (though sometimes necessary because nested switches
54
-- don't work?)
54
-- don't work?)
55
-- See also:  http://lua-users.org/lists/lua-l/2010-03/msg00086.html
55
-- See also:  http://lua-users.org/lists/lua-l/2010-03/msg00086.html
56
lpeg.setmaxstack(1024);
56
lpeg.setmaxstack(1024);
57
57
58
58
59
local Pat, Set, Range, Var = lpeg.P, lpeg.S, lpeg.R, lpeg.V
59
local Pat, Set, Range, Var = lpeg.P, lpeg.S, lpeg.R, lpeg.V
60
local POS, Cc, Ctab = lpeg.Cp, lpeg.Cc, lpeg.Ct
60
local POS, Cc, Ctab = lpeg.Cp, lpeg.Cc, lpeg.Ct
61
61
62
-- CON language definitions (among other things, all keywords pattern).
62
-- CON language definitions (among other things, all keywords pattern).
63
local conl = require("con_lang")
63
local conl = require("con_lang")
64
64
65
65
66
local function match_until(matchsp, untilsp)  -- (!untilsp matchsp)* in PEG
66
local function match_until(matchsp, untilsp)  -- (!untilsp matchsp)* in PEG
67
    -- sp: string or pattern
67
    -- sp: string or pattern
68
    return (matchsp - Pat(untilsp))^0
68
    return (matchsp - Pat(untilsp))^0
69
end
69
end
70
70
71
local format = string.format
71
local format = string.format
72
--[[
72
--[[
73
format = function(fmt, ...)
73
format = function(fmt, ...)
74
    local ok, res = pcall(string.format, fmt, ...)
74
    local ok, res = pcall(string.format, fmt, ...)
75
    if (not ok) then
75
    if (not ok) then
76
        error(string.format("FAILED format(%q, ...) | message: %s", fmt, res))
76
        error(string.format("FAILED format(%q, ...) | message: %s", fmt, res))
77
    end
77
    end
78
    return res
78
    return res
79
end
79
end
80
--]]
80
--]]
81
81
82
local function printf(fmt, ...)
82
local function printf(fmt, ...)
83
    print(format(fmt, ...))
83
    print(format(fmt, ...))
84
end
84
end
85
85
86
--- some constants
86
--- some constants
87
87
88
local C = {
88
local C = {
89
    -- These two are not used except for predefined labels.
89
    -- These two are not used except for predefined labels.
90
    -- NOTE: in-game, MAXSPRITES may be 4096 for a V7 build!
90
    -- NOTE: in-game, MAXSPRITES may be 4096 for a V7 build!
91
    MAXSTATUS = ffiC and ffiC.MAXSTATUS or 1024,
91
    MAXSTATUS = ffiC and ffiC.MAXSTATUS or 1024,
92
    MAXSPRITES = ffiC and ffiC.MAXSPRITES or 16384,
92
    MAXSPRITES = ffiC and ffiC.MAXSPRITES or 16384,
93
93
94
    MAXTILES = ffiC and ffiC.MAXTILES or 30720,
94
    MAXTILES = ffiC and ffiC.MAXTILES or 30720,
95
    MAX_WEAPONS = ffiC and ffiC.MAX_WEAPONS or 12,
95
    MAX_WEAPONS = ffiC and ffiC.MAX_WEAPONS or 12,
96
}
96
}
97
97
98
---=== semantic action functions ===---
98
---=== semantic action functions ===---
99
99
100
local inf = 1/0
100
local inf = 1/0
101
local NaN = 0/0
101
local NaN = 0/0
102
102
103
-- Last keyword position, for error diagnosis.
103
-- Last keyword position, for error diagnosis.
104
local g_lastkwpos = nil
104
local g_lastkwpos = nil
105
local g_lastkw = nil
105
local g_lastkw = nil
106
local g_badids = {}  -- maps bad id strings to 'true'
106
local g_badids = {}  -- maps bad id strings to 'true'
107
107
108
local g_recurslevel = -1  -- 0: base CON file, >0 included
108
local g_recurslevel = -1  -- 0: base CON file, >0 included
109
local g_filename = "???"
109
local g_filename = "???"
110
local g_directory = ""  -- with trailing slash if not empty
110
local g_directory = ""  -- with trailing slash if not empty
111
local g_maxerrors = 20
111
local g_maxerrors = 20
112
local g_numerrors = 0
112
local g_numerrors = 0
113
113
114
-- Default directory to search for GAME.CON etc.
114
-- Default directory to search for GAME.CON etc.
115
-- Stand-alone LunaCON only.
115
-- Stand-alone LunaCON only.
116
local g_defaultDir = nil
116
local g_defaultDir = nil
117
117
118
-- Warning options. Key names are the same as cmdline options, e.g.
118
-- Warning options. Key names are the same as cmdline options, e.g.
119
-- -Wno-bad-identifier for disabling the "bad identifier" warning.
119
-- -Wno-bad-identifier for disabling the "bad identifier" warning.
120
local g_warn = { ["not-redefined"]=true, ["bad-identifier"]=false,
120
local g_warn = { ["not-redefined"]=true, ["bad-identifier"]=false,
121
                 ["number-conversion"]=true, ["system-gamevar"]=true,
121
                 ["number-conversion"]=true, ["system-gamevar"]=true,
122
                 ["error-bad-getactorvar"]=false, ["chained-loadactor"]=true,
122
                 ["error-bad-getactorvar"]=false, ["chained-loadactor"]=true,
123
                 ["never-used-gamevar"]=false, ["never-read-gamevar"]=false, }
123
                 ["never-used-gamevar"]=false, ["never-read-gamevar"]=false, }
124
124
125
-- Code generation and output options.
125
-- Code generation and output options.
126
local g_cgopt = { ["no"]=false, ["debug-lineinfo"]=false, ["gendir"]=nil,
126
local g_cgopt = { ["no"]=false, ["debug-lineinfo"]=false, ["gendir"]=nil,
127
                  ["cache-sap"]=false, ["error-nostate"]=true,
127
                  ["cache-sap"]=false, ["error-nostate"]=true,
128
                  ["playervar"]=true, ["trapv"]=false, ["wrapv"]=false,
128
                  ["playervar"]=true, ["trapv"]=false, ["wrapv"]=false,
129
                  ["bad-getactorvar-use-pli"]=true,
129
                  ["bad-getactorvar-use-pli"]=true,
130
                  ["error-nonlocal-userdef"]=true,
130
                  ["error-nonlocal-userdef"]=true,
131
                  ["error-negative-tag-write"]=false, }
131
                  ["error-negative-tag-write"]=false, }
132
132
133
if (string.dump) then
133
if (string.dump) then
134
    g_cgopt["names"] = false
134
    g_cgopt["names"] = false
135
end
135
end
136
136
137
-- For -fnames mode.
137
-- For -fnames mode.
138
local g_actorTileToName = {}
138
local g_actorTileToName = {}
139
139
140
local function csapp() return g_cgopt["cache-sap"] end
140
local function csapp() return g_cgopt["cache-sap"] end
141
141
142
local function handle_cmdline_arg(str)
142
local function handle_cmdline_arg(str)
143
    if (str:sub(1,1)=="-") then
143
    if (str:sub(1,1)=="-") then
144
        if (#str == 1) then
144
        if (#str == 1) then
145
            printf("Warning: input from stdin not supported")
145
            printf("Warning: input from stdin not supported")
146
        else
146
        else
147
            local ok = false
147
            local ok = false
148
            local kind = str:sub(2,2)
148
            local kind = str:sub(2,2)
149
149
150
            -- -W(no-)*: warnings
150
            -- -W(no-)*: warnings
151
            if (kind=="W" and #str >= 3) then
151
            if (kind=="W" and #str >= 3) then
152
                local val = true
152
                local val = true
153
                local warnstr = str:sub(3)
153
                local warnstr = str:sub(3)
154
154
155
                if (warnstr == "all") then
155
                if (warnstr == "all") then
156
                    -- Enable all warnings.
156
                    -- Enable all warnings.
157
                    for wopt in pairs(g_warn) do
157
                    for wopt in pairs(g_warn) do
158
                        g_warn[wopt] = true
158
                        g_warn[wopt] = true
159
                    end
159
                    end
160
                    ok = true
160
                    ok = true
161
                else
161
                else
162
                    -- Enable or disable a particular warning.
162
                    -- Enable or disable a particular warning.
163
                    if (warnstr:sub(1,3)=="no-") then
163
                    if (warnstr:sub(1,3)=="no-") then
164
                        val = false
164
                        val = false
165
                        warnstr = warnstr:sub(4)
165
                        warnstr = warnstr:sub(4)
166
                    end
166
                    end
167
167
168
                    if (type(g_warn[warnstr])=="boolean") then
168
                    if (type(g_warn[warnstr])=="boolean") then
169
                        g_warn[warnstr] = val
169
                        g_warn[warnstr] = val
170
                        ok = true
170
                        ok = true
171
                    end
171
                    end
172
                end
172
                end
173
173
174
            -- -fno* special handling
174
            -- -fno* special handling
175
            elseif (str:sub(2)=="fno") then
175
            elseif (str:sub(2)=="fno") then
176
                -- Disable printing code entirely.
176
                -- Disable printing code entirely.
177
                g_cgopt["no"] = true
177
                g_cgopt["no"] = true
178
                ok = true
178
                ok = true
179
            elseif (str:sub(2)=="fno=onlycheck") then
179
            elseif (str:sub(2)=="fno=onlycheck") then
180
                -- Disable printing code, only do syntax check of gen'd code.
180
                -- Disable printing code, only do syntax check of gen'd code.
181
                g_cgopt["no"] = "onlycheck"
181
                g_cgopt["no"] = "onlycheck"
182
                ok = true
182
                ok = true
183
183
184
            -- -fgendir=<directory>: specify directory for generated code
184
            -- -fgendir=<directory>: specify directory for generated code
185
            elseif (str:sub(2,9)=="fgendir=" and #str >= 10) then
185
            elseif (str:sub(2,9)=="fgendir=" and #str >= 10) then
186
                g_cgopt["gendir"] = str:sub(10)
186
                g_cgopt["gendir"] = str:sub(10)
187
                ok = true
187
                ok = true
188
188
189
            -- -f(no-)*: code generation options
189
            -- -f(no-)*: code generation options
190
            elseif (kind=="f" and #str >= 3) then
190
            elseif (kind=="f" and #str >= 3) then
191
                local val = true
191
                local val = true
192
                local cgstr = str:sub(3)
192
                local cgstr = str:sub(3)
193
193
194
                if (cgstr:sub(1,3)=="no-") then
194
                if (cgstr:sub(1,3)=="no-") then
195
                    val = false
195
                    val = false
196
                    cgstr = cgstr:sub(4)
196
                    cgstr = cgstr:sub(4)
197
                end
197
                end
198
198
199
                if (type(g_cgopt[cgstr])=="boolean") then
199
                if (type(g_cgopt[cgstr])=="boolean") then
200
                    g_cgopt[cgstr] = val
200
                    g_cgopt[cgstr] = val
201
                    ok = true
201
                    ok = true
202
                end
202
                end
203
203
204
            -- -I<directory>: default search directory (only ONCE, not search path)
204
            -- -I<directory>: default search directory (only ONCE, not search path)
205
            elseif (kind=="I" and #str >= 3) then
205
            elseif (kind=="I" and #str >= 3) then
206
                g_defaultDir = str:sub(3)
206
                g_defaultDir = str:sub(3)
207
                ok = true
207
                ok = true
208
            end
208
            end
209
209
210
            if (not ffi and not ok) then
210
            if (not ffi and not ok) then
211
                printf("Warning: Unrecognized option %s", str)
211
                printf("Warning: Unrecognized option %s", str)
212
            end
212
            end
213
        end
213
        end
214
214
215
        return true
215
        return true
216
    end
216
    end
217
end
217
end
218
218
219
-- Handle command line arguments. Has to happen before pattern construction,
219
-- Handle command line arguments. Has to happen before pattern construction,
220
-- because some of them depend on codegen options (specifically, -ftrapv,
220
-- because some of them depend on codegen options (specifically, -ftrapv,
221
-- -fwrapv).
221
-- -fwrapv).
222
if (string.dump) then
222
if (string.dump) then
223
    -- running stand-alone
223
    -- running stand-alone
224
    local i = 1
224
    local i = 1
225
    while (arg[i]) do
225
    while (arg[i]) do
226
        if (handle_cmdline_arg(arg[i])) then
226
        if (handle_cmdline_arg(arg[i])) then
227
            table.remove(arg, i)  -- remove processed cmdline arg
227
            table.remove(arg, i)  -- remove processed cmdline arg
228
        else
228
        else
229
            i = i+1
229
            i = i+1
230
        end
230
        end
231
    end
231
    end
232
else
232
else
233
    -- running from EDuke32
233
    -- running from EDuke32
234
    local i=0
234
    local i=0
235
    while (ffiC.g_argv[i] ~= nil) do
235
    while (ffiC.g_argv[i] ~= nil) do
236
        handle_cmdline_arg(ffi.string(ffiC.g_argv[i]))
236
        handle_cmdline_arg(ffi.string(ffiC.g_argv[i]))
237
        i = i+1
237
        i = i+1
238
    end
238
    end
239
end
239
end
240
240
241
if (g_cgopt["error-negative-tag-write"]) then
241
if (g_cgopt["error-negative-tag-write"]) then
242
    conl.setup_negative_tag_check("_st")
242
    conl.setup_negative_tag_check("_st")
243
end
243
end
244
244
245
-- Stack with *true* on top if the innermost block is a "whilevar*n".
245
-- Stack with *true* on top if the innermost block is a "whilevar*n".
246
local g_isWhile = {}
246
local g_isWhile = {}
247
-- Sequence number of 'while' statements, used to implement CON "break" inside
247
-- Sequence number of 'while' statements, used to implement CON "break" inside
248
-- whilevar*n, which really behaves like what sane languages call "continue"...
248
-- whilevar*n, which really behaves like what sane languages call "continue"...
249
local g_whilenum = 0
249
local g_whilenum = 0
250
250
251
---=== Code generation ===---
251
---=== Code generation ===---
252
local GVFLAG = {
252
local GVFLAG = {
253
    PERPLAYER=1, PERACTOR=2, PERX_MASK=3,
253
    PERPLAYER=1, PERACTOR=2, PERX_MASK=3,
254
    SYSTEM   = 0x00000800,
254
    SYSTEM   = 0x00000800,
255
    READONLY = 0x00001000,
255
    READONLY = 0x00001000,
256
256
257
    NODEFAULT = 0x00000400,  -- don't reset on actor spawn
257
    NODEFAULT = 0x00000400,  -- don't reset on actor spawn
258
    NORESET   = 0x00020000,  -- don't reset when restoring map state
258
    NORESET   = 0x00020000,  -- don't reset when restoring map state
259
259
260
    CON_PERPLAYER = 0x40000000,  -- LunaCON internal
260
    CON_PERPLAYER = 0x40000000,  -- LunaCON internal
261
}
261
}
262
262
263
-- NOTE: This differs from enum GamevarFlags_t's GAMEVAR_USER_MASK
263
-- NOTE: This differs from enum GamevarFlags_t's GAMEVAR_USER_MASK
264
GVFLAG.USER_MASK = GVFLAG.PERX_MASK + GVFLAG.NODEFAULT + GVFLAG.NORESET
264
GVFLAG.USER_MASK = GVFLAG.PERX_MASK + GVFLAG.NODEFAULT + GVFLAG.NORESET
265
265
266
-- CON --> mangled Lua function name, also existence check:
266
-- CON --> mangled Lua function name, also existence check:
267
local g_funcname = {}
267
local g_funcname = {}
268
-- while parsing a block, it is a table of "gencode" tables:
268
-- while parsing a block, it is a table of "gencode" tables:
269
local g_switchCode = nil
269
local g_switchCode = nil
270
-- Global number of switch statements:
270
-- Global number of switch statements:
271
local g_switchCount = 0
271
local g_switchCount = 0
272
-- Number of session gamevars:
272
-- Number of session gamevars:
273
local g_numSessionVars = 0
273
local g_numSessionVars = 0
274
-- [identifier] = { name=<mangled name / code>, flags=<gamevar flags> }
274
-- [identifier] = { name=<mangled name / code>, flags=<gamevar flags> }
275
local g_gamevar = {}
275
local g_gamevar = {}
276
-- [identifier] = { name=<mangled name / code>, size=<initial size> }
276
-- [identifier] = { name=<mangled name / code>, size=<initial size> }
277
local g_gamearray = {}
277
local g_gamearray = {}
278
278
279
-- * nil if dynamic tile remapping disabled
279
-- * nil if dynamic tile remapping disabled
280
-- * {} if enabled but no remappings made
280
-- * {} if enabled but no remappings made
281
-- * else, a nonempty table { [name]=<g_dynTileList index> }
281
-- * else, a nonempty table { [name]=<g_dynTileList index> }
282
local g_dyntilei = nil
282
local g_dyntilei = nil
283
-- Analogously for sounds.
283
-- Analogously for sounds.
284
local g_dynsoundi = nil
284
local g_dynsoundi = nil
285
285
286
local g_have_file = {}  -- [filename]=true
286
local g_have_file = {}  -- [filename]=true
287
local g_curcode = nil  -- a table of string pieces or other "gencode" tables
287
local g_curcode = nil  -- a table of string pieces or other "gencode" tables
288
288
289
-- will be a table, see reset.codegen()
289
-- will be a table, see reset.codegen()
290
local g_code = nil
290
local g_code = nil
291
291
292
292
293
local function ACS(s) return (csapp() and "_a" or "actor[_aci]")..s end
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
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
295
local function PLS(s) return (csapp() and "_ps" or "player[_pli]")..s end
296
local function PLSX(s) return "player[_pli]"..s end
296
local function PLSX(s) return "player[_pli]"..s end
297
297
298
298
299
local function getlinecol(pos) end -- fwd-decl
299
local function getlinecol(pos) end -- fwd-decl
300
300
301
local function new_initial_codetab()
301
local function new_initial_codetab()
302
    -- NOTE: Keep this one line per line to not confuse the Lua->CON line
302
    -- NOTE: Keep this one line per line to not confuse the Lua->CON line
303
    -- mapping system.
303
    -- mapping system.
304
    return {
304
    return {
305
        -- Requires.
305
        -- Requires.
306
        "local require=require",
306
        "local require=require",
307
        "local _con, _bit, _math = require'con', require'bit', require'math'",
307
        "local _con, _bit, _math = require'con', require'bit', require'math'",
308
        "local _xmath = require'xmath'",
308
        "local _xmath = require'xmath'",
309
309
310
        -- Cache globals into locals.
310
        -- Cache globals into locals.
311
        "local sector, sprite, wall, spriteext, _atsprite = sector, sprite, wall, spriteext, _atsprite",
311
        "local sector, sprite, wall, spriteext, _atsprite = sector, sprite, wall, spriteext, _atsprite",
312
        "local actor, player, projectile, g_tile = actor, player, projectile, g_tile",
312
        "local actor, player, projectile, g_tile = actor, player, projectile, g_tile",
313
        "local gameactor, gameevent, _gv = gameactor, gameevent, gv",
313
        "local gameactor, gameevent, _gv = gameactor, gameevent, gv",
314
        "local updatesector, updatesectorz, cansee = updatesector, updatesectorz, cansee",
314
        "local updatesector, updatesectorz, cansee = updatesector, updatesectorz, cansee",
315
        "local print, printf = print, printf",
315
        "local print, printf = print, printf",
316
316
317
        -- Cache a couple of often-used functions.
317
        -- Cache a couple of often-used functions.
318
        "local _div, _mod, _mulTR, _mulWR = _con._div, _con._mod, _con._mulTR, _con._mulWR",
318
        "local _div, _mod, _mulTR, _mulWR = _con._div, _con._mod, _con._mulTR, _con._mulWR",
319
        "local _band, _bor, _bxor = _bit.band, _bit.bor, _bit.bxor",
319
        "local _band, _bor, _bxor = _bit.band, _bit.bor, _bit.bxor",
320
        "local _lsh, _rsh, _arsh = _bit.lshift, _bit.rshift, _bit.arshift",
320
        "local _lsh, _rsh, _arsh = _bit.lshift, _bit.rshift, _bit.arshift",
321
        "local _setsprite,_ssp = _con._setsprite,_con._ssp",
321
        "local _setsprite,_ssp = _con._setsprite,_con._ssp",
322
        g_cgopt["error-nonlocal-userdef"]
322
        g_cgopt["error-nonlocal-userdef"]
323
            and "local _gud=_con._get_userdef_check" or "local _gud=_con._get_userdef",
323
            and "local _gud=_con._get_userdef_check" or "local _gud=_con._get_userdef",
324
        "local _st=_con._err_if_negative",
324
        "local _st=_con._err_if_negative",
325
325
326
        -- * CON "states" (subroutines) and
326
        -- * CON "states" (subroutines) and
327
        -- * Switch function table, indexed by global switch sequence number:
327
        -- * Switch function table, indexed by global switch sequence number:
328
        "local _F,_SW = {},{}",
328
        "local _F,_SW = {},{}",
329
329
330
        -- CON gamevars and gamearrays (see mangle_name()), set up for
330
        -- CON gamevars and gamearrays (see mangle_name()), set up for
331
        -- restoration from savegames.
331
        -- restoration from savegames.
332
        "module(...)",
332
        "module(...)",
333
        "_V,_A={},{}",
333
        "_V,_A={},{}",
334
        "-- NOTE to the reader: This require's result is Lunatic-private API! DO NOT USE!",
334
        "-- NOTE to the reader: This require's result is Lunatic-private API! DO NOT USE!",
335
        "local _dummy,_S=require'end_gamevars'",
335
        "local _dummy,_S=require'end_gamevars'",
336
-- XXX: Currently commented out because of gamevar restoration from loadmapstate.
336
-- XXX: Currently commented out because of gamevar restoration from loadmapstate.
337
--        "local _V,_A=_V,_A",
337
--        "local _V,_A=_V,_A",
338
        "local _C,_M,_I={},{},{}",  -- actions, moves, ais
338
        "local _C,_M,_I={},{},{}",  -- actions, moves, ais
339
339
340
        -- Static ivec3s so that no allocations need to be made.
340
        -- Static ivec3s so that no allocations need to be made.
341
        "local _IVEC = { _xmath.ivec3(), _xmath.ivec3() }",
341
        "local _IVEC = { _xmath.ivec3(), _xmath.ivec3() }",
342
        "local function _IV(num, x, y, z)",
342
        "local function _IV(num, x, y, z)",
343
        "  local v=_IVEC[num]; v.x=x; v.y=y; v.z=z; return v;",
343
        "  local v=_IVEC[num]; v.x=x; v.y=y; v.z=z; return v;",
344
        "end",
344
        "end",
345
           }
345
           }
346
end
346
end
347
347
348
-- CON global system gamevar
348
-- CON global system gamevar
349
local function CSV(var) return "_gv._csv"..var end
349
local function CSV(var) return "_gv._csv"..var end
350
350
351
-- Creates the table of predefined game variables.
351
-- Creates the table of predefined game variables.
352
-- KEEPINSYNC gamevars.c: Gv_AddSystemVars()
352
-- KEEPINSYNC gamevars.c: Gv_AddSystemVars()
353
local function new_initial_gvartab()
353
local function new_initial_gvartab()
354
    local wmembers = conl.wdata_members
354
    local wmembers = conl.wdata_members
355
355
356
    local function GamevarCreationFunc(addflags)
356
    local function GamevarCreationFunc(addflags)
357
        return function(varname)
357
        return function(varname)
358
            -- 'used' is a bitmask: 1 is 'was read', 2 is 'was written to'
358
            -- 'used' is a bitmask: 1 is 'was read', 2 is 'was written to'
359
            return { name=varname, flags=GVFLAG.SYSTEM+addflags, used=3 }
359
            return { name=varname, flags=GVFLAG.SYSTEM+addflags, used=3 }
360
        end
360
        end
361
    end
361
    end
362
362
363
    local RW = GamevarCreationFunc(0)
363
    local RW = GamevarCreationFunc(0)
364
    local RO = GamevarCreationFunc(GVFLAG.READONLY)
364
    local RO = GamevarCreationFunc(GVFLAG.READONLY)
365
    local PRW = GamevarCreationFunc(GVFLAG.PERPLAYER)
365
    local PRW = GamevarCreationFunc(GVFLAG.PERPLAYER)
366
    local PRO = GamevarCreationFunc(GVFLAG.READONLY+GVFLAG.PERPLAYER)
366
    local PRO = GamevarCreationFunc(GVFLAG.READONLY+GVFLAG.PERPLAYER)
367
367
368
    local gamevar = {
368
    local gamevar = {
369
        -- NOTE: THISACTOR can mean different things in some contexts.
369
        -- NOTE: THISACTOR can mean different things in some contexts.
370
        THISACTOR = RO "_aci",
370
        THISACTOR = RO "_aci",
371
371
372
        RETURN = RW "_gv.RETURN",
372
        RETURN = RW "_gv.RETURN",
373
        HITAG = RW(CSV".HITAG"),
373
        HITAG = RW(CSV".HITAG"),
374
        LOTAG = RW(CSV".LOTAG"),
374
        LOTAG = RW(CSV".LOTAG"),
375
        TEXTURE = RW(CSV".TEXTURE"),
375
        TEXTURE = RW(CSV".TEXTURE"),
376
376
377
        -- This will warn when defining from CON, but it's the most
377
        -- This will warn when defining from CON, but it's the most
378
        -- straightforward implementation.
378
        -- straightforward implementation.
379
        LOGO_FLAGS = RW "_gv.g_logoFlags",
379
        LOGO_FLAGS = RW "_gv.g_logoFlags",
380
380
381
        xdim = RO "_gv.xdim",
381
        xdim = RO "_gv.xdim",
382
        ydim = RO "_gv.ydim",
382
        ydim = RO "_gv.ydim",
383
        windowx1 = RO "_gv.windowx1",
383
        windowx1 = RO "_gv.windowx1",
384
        windowy1 = RO "_gv.windowy1",
384
        windowy1 = RO "_gv.windowy1",
385
        windowx2 = RO "_gv.windowx2",
385
        windowx2 = RO "_gv.windowx2",
386
        windowy2 = RO "_gv.windowy2",
386
        windowy2 = RO "_gv.windowy2",
387
387
388
        yxaspect = RO "_gv._get_yxaspect()",
388
        yxaspect = RO "_gv._get_yxaspect()",
389
        viewingrange = RO "_gv._get_viewingrange()",
389
        viewingrange = RO "_gv._get_viewingrange()",
390
        -- TODO: gravitationalconstant, gametype_flags
390
        -- TODO: gravitationalconstant, gametype_flags
391
391
392
        numsectors = RO "_gv.numsectors",
392
        numsectors = RO "_gv.numsectors",
393
        NUMSECTORS = RO "_gv.numsectors",
393
        NUMSECTORS = RO "_gv.numsectors",
394
        NUMWALLS = RO "_gv.numwalls",
394
        NUMWALLS = RO "_gv.numwalls",
395
        Numsprites = RO "_gv.Numsprites",
395
        Numsprites = RO "_gv.Numsprites",
396
396
397
        randomseed = RW "_gv.randomseed",
397
        randomseed = RW "_gv.randomseed",
398
        totalclock = RO "_gv.totalclock",
398
        totalclock = RO "_gv.totalclock",
399
        framerate = RO "_gv._currentFramerate()",
399
        framerate = RO "_gv._currentFramerate()",
400
        current_menu = RO "_gv._currentMenu()",
400
        current_menu = RO "_gv._currentMenu()",
401
        rendmode = RO "_gv.rendmode",
401
        rendmode = RO "_gv.rendmode",
402
402
403
        screenpeek = RO "_gv.screenpeek",
403
        screenpeek = RO "_gv.screenpeek",
404
404
405
        camerax = RW "_gv.cam.pos.x",
405
        camerax = RW "_gv.cam.pos.x",
406
        cameray = RW "_gv.cam.pos.y",
406
        cameray = RW "_gv.cam.pos.y",
407
        cameraz = RW "_gv.cam.pos.z",
407
        cameraz = RW "_gv.cam.pos.z",
408
        cameraang = RW "_gv.cam.ang",
408
        cameraang = RW "_gv.cam.ang",
409
        camerahoriz = RW "_gv.cam.horiz",
409
        camerahoriz = RW "_gv.cam.horiz",
410
        camerasect = RW "_gv.cam.sect",
410
        camerasect = RW "_gv.cam.sect",
411
        cameradist = RW "_gv.cam.dist",
411
        cameradist = RW "_gv.cam.dist",
412
        cameraclock = RW "_gv.cam.clock",
412
        cameraclock = RW "_gv.cam.clock",
413
413
414
        -- HUD weapon gamevars
414
        -- HUD weapon gamevars
415
        currentweapon = RW "_gv.hudweap.cur",
415
        currentweapon = RW "_gv.hudweap.cur",
416
        weaponcount = RW "_gv.hudweap.count",
416
        weaponcount = RW "_gv.hudweap.count",
417
        weapon_xoffset = RW "_gv.hudweap.gunposx",
417
        weapon_xoffset = RW "_gv.hudweap.gunposx",
418
        looking_angSR1 = RW "_gv.hudweap.lookhalfang",
418
        looking_angSR1 = RW "_gv.hudweap.lookhalfang",
419
        gun_pos = RW "_gv.hudweap.gunposy",
419
        gun_pos = RW "_gv.hudweap.gunposy",
420
        looking_arc = RW "_gv.hudweap.lookhoriz",
420
        looking_arc = RW "_gv.hudweap.lookhoriz",
421
        gs = RW "_gv.hudweap.shade",
421
        gs = RW "_gv.hudweap.shade",
422
422
423
        -- Some per-player gamevars
423
        -- Some per-player gamevars
424
        ZRANGE = PRW(PLSX".zrange"),
424
        ZRANGE = PRW(PLSX".zrange"),
425
        ANGRANGE = PRW(PLSX".angrange"),
425
        ANGRANGE = PRW(PLSX".angrange"),
426
        AUTOAIMANGLE = PRW(PLSX".autoaimang"),
426
        AUTOAIMANGLE = PRW(PLSX".autoaimang"),
427
427
428
        PIPEBOMB_CONTROL = PRW(PLSX".pipebombControl"),
428
        PIPEBOMB_CONTROL = PRW(PLSX".pipebombControl"),
429
        GRENADE_LIFETIME = PRW(PLSX".pipebombLifetime"),
429
        GRENADE_LIFETIME = PRW(PLSX".pipebombLifetime"),
430
        GRENADE_LIFETIME_VAR = PRW(PLSX".pipebombLifetimeVar"),
430
        GRENADE_LIFETIME_VAR = PRW(PLSX".pipebombLifetimeVar"),
431
        TRIPBOMB_CONTROL = PRW(PLSX".tripbombControl"),
431
        TRIPBOMB_CONTROL = PRW(PLSX".tripbombControl"),
432
        STICKYBOMB_LIFETIME = PRW(PLSX".tripbombLifetime"),
432
        STICKYBOMB_LIFETIME = PRW(PLSX".tripbombLifetime"),
433
        STICKYBOMB_LIFETIME_VAR = PRW(PLSX".tripbombLifetimeVar"),
433
        STICKYBOMB_LIFETIME_VAR = PRW(PLSX".tripbombLifetimeVar"),
434
434
435
        -- Some *writable* system gamevars relating to multiplayer.
435
        -- Some *writable* system gamevars relating to multiplayer.
436
        -- TODO_MP.
436
        -- TODO_MP.
437
        RESPAWN_MONSTERS = RO "0",
437
        RESPAWN_MONSTERS = RO "0",
438
        RESPAWN_ITEMS = RO "0",
438
        RESPAWN_ITEMS = RO "0",
439
        RESPAWN_INVENTORY = RO "0",
439
        RESPAWN_INVENTORY = RO "0",
440
        MONSTERS_OFF = RO "0",
440
        MONSTERS_OFF = RO "0",
441
        MARKER = RO "0",
441
        MARKER = RO "0",
442
442
443
        -- These are not 100% authentic (they're only updated in certain
443
        -- These are not 100% authentic (they're only updated in certain
444
        -- circumstances, see player.c: P_SetWeaponGamevars()). But IMO it's
444
        -- circumstances, see player.c: P_SetWeaponGamevars()). But IMO it's
445
        -- more useful like this.
445
        -- more useful like this.
446
        WEAPON = PRO(PLSX".curr_weapon"),
446
        WEAPON = PRO(PLSX".curr_weapon"),
447
        WORKSLIKE = PRO(format(PLSX".weapon[%s].workslike", PLSX".curr_weapon")),
447
        WORKSLIKE = PRO(format(PLSX".weapon[%s].workslike", PLSX".curr_weapon")),
448
448
449
        VOLUME = RO "_gv._ud.volume_number",
449
        VOLUME = RO "_gv._ud.volume_number",
450
        LEVEL = RO "_gv._ud.level_number",
450
        LEVEL = RO "_gv._ud.level_number",
451
    }
451
    }
452
452
453
    -- Reserved bits
453
    -- Reserved bits
454
    gamevar.LOGO_FLAGS.rbits = bit.bnot(0x001fffff)
454
    gamevar.LOGO_FLAGS.rbits = bit.bnot(0x001fffff)
455
455
456
    for w=0,C.MAX_WEAPONS-1 do
456
    for w=0,C.MAX_WEAPONS-1 do
457
        for i=1,#wmembers do
457
        for i=1,#wmembers do
458
            local member = wmembers[i]:gsub(".*_t ","")  -- strip e.g. "const int32_t "
458
            local member = wmembers[i]:gsub(".*_t ","")  -- strip e.g. "const int32_t "
459
                                      :gsub("^_","")  -- strip potentially leading underscore
459
                                      :gsub("^_","")  -- strip potentially leading underscore
460
            local name = format("WEAPON%d_%s", w, member:upper())
460
            local name = format("WEAPON%d_%s", w, member:upper())
461
            gamevar[name] = PRW(format(PLSX".weapon[%d].%s", w, member))
461
            gamevar[name] = PRW(format(PLSX".weapon[%d].%s", w, member))
462
462
463
            if (member=="flags") then
463
            if (member=="flags") then
464
                gamevar[name].rbits = bit.bnot(0x1ffff)
464
                gamevar[name].rbits = bit.bnot(0x1ffff)
465
            end
465
            end
466
        end
466
        end
467
    end
467
    end
468
468
469
    return gamevar
469
    return gamevar
470
end
470
end
471
471
472
local reset = {}
472
local reset = {}
473
473
474
function reset.codegen()
474
function reset.codegen()
475
    g_funcname = {}
475
    g_funcname = {}
476
    g_switchCode = nil
476
    g_switchCode = nil
477
    g_switchCount = 0
477
    g_switchCount = 0
478
    g_numSessionVars = 0
478
    g_numSessionVars = 0
479
    g_gamevar = new_initial_gvartab()
479
    g_gamevar = new_initial_gvartab()
480
    g_gamearray = {
480
    g_gamearray = {
481
        -- SYSTEM_GAMEARRAY
481
        -- SYSTEM_GAMEARRAY
482
        tilesizx = { name="g_tile.sizx", size=C.MAXTILES, sysp=true },
482
        tilesizx = { name="g_tile.sizx", size=C.MAXTILES, sysp=true },
483
        tilesizy = { name="g_tile.sizy", size=C.MAXTILES, sysp=true },
483
        tilesizy = { name="g_tile.sizy", size=C.MAXTILES, sysp=true },
484
    }
484
    }
485
485
486
    g_dyntilei = nil
486
    g_dyntilei = nil
487
    g_dynsoundi = nil
487
    g_dynsoundi = nil
488
488
489
    g_have_file = {}
489
    g_have_file = {}
490
    g_curcode = new_initial_codetab()
490
    g_curcode = new_initial_codetab()
491
    -- actor, event, loadactor: [{actor, event, actor}num] = gencode_table
491
    -- actor, event, loadactor: [{actor, event, actor}num] = gencode_table
492
    --
492
    --
493
    -- aflagsloc[actornum]: location of '(user)actor' token, 'spriteflags' or
493
    -- aflagsloc[actornum]: location of '(user)actor' token, 'spriteflags' or
494
    -- 'sprite*' command; result of getLocation(<kind>, <pos>)
494
    -- 'sprite*' command; result of getLocation(<kind>, <pos>)
495
    g_code = { actor={}, event={}, loadactor={}, aflagsloc={} }
495
    g_code = { actor={}, event={}, loadactor={}, aflagsloc={} }
496
496
497
    g_recurslevel = -1
497
    g_recurslevel = -1
498
    g_numerrors = 0
498
    g_numerrors = 0
499
end
499
end
500
500
501
-- Is SYSTEM_GAMEARRAY?
501
-- Is SYSTEM_GAMEARRAY?
502
local function issysgar(str)
502
local function issysgar(str)
503
    return str:match("^g_tile.siz[xy]")
503
    return str:match("^g_tile.siz[xy]")
504
end
504
end
505
505
506
local function addcode(x)
506
local function addcode(x)
507
    assert(type(x)=="string" or type(x)=="table")
507
    assert(type(x)=="string" or type(x)=="table")
508
    g_curcode[#g_curcode+1] = x
508
    g_curcode[#g_curcode+1] = x
509
end
509
end
510
510
511
local function addcodef(fmt, ...)
511
local function addcodef(fmt, ...)
512
    addcode(format(fmt, ...))
512
    addcode(format(fmt, ...))
513
end
513
end
514
514
515
local function paddcodef(pos, fmt, ...)
515
local function paddcodef(pos, fmt, ...)
516
    addcodef(fmt.."--"..getlinecol(pos), ...)
516
    addcodef(fmt.."--"..getlinecol(pos), ...)
517
end
517
end
518
518
519
local function add_code_and_end(codetab, endstr)
519
local function add_code_and_end(codetab, endstr)
520
    assert(type(codetab)=="table")
520
    assert(type(codetab)=="table")
521
    addcode(codetab)
521
    addcode(codetab)
522
    addcode(endstr)
522
    addcode(endstr)
523
end
523
end
524
524
525
local function get_cache_sap_code()
525
local function get_cache_sap_code()
526
    return csapp() and "local _spr,_a,_ps=_con._getsap(_aci,_pli)" or ""
526
    return csapp() and "local _spr,_a,_ps=_con._getsap(_aci,_pli)" or ""
527
end
527
end
528
528
529
-- fwd-decls
529
-- fwd-decls
530
local warnprintf, errprintf, pwarnprintf, perrprintf, contprintf
530
local warnprintf, errprintf, pwarnprintf, perrprintf, contprintf
531
local getLocation
531
local getLocation
532
532
533
local on = {}
533
local on = {}
534
534
535
-- Map from CON actor usertype to SFLAGs.
535
-- Map from CON actor usertype to SFLAGs.
536
local MAP_ACTOR_FLAGS = {
536
local MAP_ACTOR_FLAGS = {
537
    [0] = 0,
537
    [0] = 0,
538
    [1] = conl.SFLAG.SFLAG_BADGUY,
538
    [1] = conl.SFLAG.SFLAG_BADGUY,
539
    [2] = conl.SFLAG.SFLAG_BADGUY + conl.SFLAG.SFLAG_BADGUYSTAYPUT,
539
    [2] = conl.SFLAG.SFLAG_BADGUY + conl.SFLAG.SFLAG_BADGUYSTAYPUT,
540
    [3] = conl.SFLAG.SFLAG_BADGUY + conl.SFLAG.SFLAG_BADGUYSTAYPUT,
540
    [3] = conl.SFLAG.SFLAG_BADGUY + conl.SFLAG.SFLAG_BADGUYSTAYPUT,
541
}
541
}
542
for i=4,7 do
542
for i=4,7 do
543
    MAP_ACTOR_FLAGS[i] = MAP_ACTOR_FLAGS[i-4] + conl.SFLAG.SFLAG_ROTFIXED
543
    MAP_ACTOR_FLAGS[i] = MAP_ACTOR_FLAGS[i-4] + conl.SFLAG.SFLAG_ROTFIXED
544
end
544
end
545
545
546
546
547
-- Table of functions doing various lookups (label, gamevar, ...)
547
-- Table of functions doing various lookups (label, gamevar, ...)
548
local lookup = {}
548
local lookup = {}
549
549
550
-- For -fnames mode.
550
-- For -fnames mode.
551
function on.fnames_tilenum_label(tilenum)
551
function on.fnames_tilenum_label(tilenum)
552
    if (g_cgopt["names"] and type(tilenum)=="string") then
552
    if (g_cgopt["names"] and type(tilenum)=="string") then
553
        -- <tilenum> may be a string (define label)
553
        -- <tilenum> may be a string (define label)
554
        -- HANDLE_RAWDEFINE
554
        -- HANDLE_RAWDEFINE
555
        local pos, minus, label = tilenum:match("(.-):(.-):(.+)")
555
        local pos, minus, label = tilenum:match("(.-):(.-):(.+)")
556
        local realtilenum = lookup.defined_label(tonumber(pos), minus, label)
556
        local realtilenum = lookup.defined_label(tonumber(pos), minus, label)
557
557
558
        g_actorTileToName[realtilenum] = label
558
        g_actorTileToName[realtilenum] = label
559
        return true
559
        return true
560
    end
560
    end
561
end
561
end
562
562
563
function on.actor_end(pos, usertype, tsamm, codetab)
563
function on.actor_end(pos, usertype, tsamm, codetab)
564
    local tilenum = tsamm[1]
564
    local tilenum = tsamm[1]
565
    local flags = 0
565
    local flags = 0
566
566
567
    if (on.fnames_tilenum_label(tilenum)) then
567
    if (on.fnames_tilenum_label(tilenum)) then
568
        return
568
        return
569
    end
569
    end
570
570
571
    if (usertype ~= nil) then  -- useractor
571
    if (usertype ~= nil) then  -- useractor
572
        if (not (bit.band(usertype, bit.bnot(7)) == 0)) then
572
        if (not (bit.band(usertype, bit.bnot(7)) == 0)) then
573
            perrprintf(pos, "invalid usertype: must be bitwise OR of 1, 2 and/or 4")
573
            perrprintf(pos, "invalid usertype: must be bitwise OR of 1, 2 and/or 4")
574
        else
574
        else
575
            flags = MAP_ACTOR_FLAGS[usertype]
575
            flags = MAP_ACTOR_FLAGS[usertype]
576
        end
576
        end
577
    end
577
    end
578
578
579
    -- 0x08000000: actor.FLAGS.replace
579
    -- 0x08000000: actor.FLAGS.replace
580
    flags = bit.bor(flags, 0x08000000)
580
    flags = bit.bor(flags, 0x08000000)
581
581
582
    local str = flags..","
582
    local str = flags..","
583
    for i=2,math.min(#tsamm,4) do
583
    for i=2,math.min(#tsamm,4) do
584
        str = str .. tostring(tsamm[i])..","
584
        str = str .. tostring(tsamm[i])..","
585
    end
585
    end
586
    if (#tsamm >= 5) then
586
    if (#tsamm >= 5) then
587
        local movflags = bit.bor(unpack(tsamm, 5))
587
        local movflags = bit.bor(unpack(tsamm, 5))
588
        str = str .. movflags..","
588
        str = str .. movflags..","
589
    end
589
    end
590
590
591
    paddcodef(pos, "gameactor{%d,%sfunction(_aci,_pli,_dist)", tilenum, str)
591
    paddcodef(pos, "gameactor{%d,%sfunction(_aci,_pli,_dist)", tilenum, str)
592
    addcode(get_cache_sap_code())
592
    addcode(get_cache_sap_code())
593
    add_code_and_end(codetab, "end}")
593
    add_code_and_end(codetab, "end}")
594
594
595
    if (g_code.actor[tilenum] ~= nil) then
595
    if (g_code.actor[tilenum] ~= nil) then
596
        pwarnprintf(pos, "redefined actor %d", tilenum)
596
        pwarnprintf(pos, "redefined actor %d", tilenum)
597
    end
597
    end
598
    g_code.actor[tilenum] = codetab
598
    g_code.actor[tilenum] = codetab
599
    g_code.aflagsloc[tilenum] = getLocation("definition of actor", pos)
599
    g_code.aflagsloc[tilenum] = getLocation("definition of actor", pos)
600
end
600
end
601
601
602
-- NOTE: in C-CON, the slash and backslash can also be part of an identifier,
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.
603
-- but this is likely to support file names in other places.
604
local BAD_ID_CHARS0 = "_*?"  -- allowed 1st identifier chars
604
local BAD_ID_CHARS0 = "_*?"  -- allowed 1st identifier chars
605
local BAD_ID_CHARS1 = "_*-+?."  -- allowed following identifier chars
605
local BAD_ID_CHARS1 = "_*-+?."  -- allowed following identifier chars
606
606
607
local function truetab(tab)
607
local function truetab(tab)
608
    local ttab = {}
608
    local ttab = {}
609
    for i=1,#tab do
609
    for i=1,#tab do
610
        ttab[tab[i]] = true
610
        ttab[tab[i]] = true
611
    end
611
    end
612
    return ttab
612
    return ttab
613
end
613
end
614
614
615
-- Lua 5.2 keywords. Not 5.1 because we use "goto" for codegen.
615
-- Lua 5.2 keywords. Not 5.1 because we use "goto" for codegen.
616
local LUA_KEYW = truetab {
616
local LUA_KEYW = truetab {
617
    "and", "break", "do", "else", "elseif", "end",
617
    "and", "break", "do", "else", "elseif", "end",
618
    "false", "for", "function", "goto", "if", "in",
618
    "false", "for", "function", "goto", "if", "in",
619
    "local", "nil", "not", "or", "repeat", "return",
619
    "local", "nil", "not", "or", "repeat", "return",
620
    "then", "true", "until", "while"
620
    "then", "true", "until", "while"
621
}
621
}
622
622
623
-- Return the Lua code by which the CON object <name> is referenced in the
623
-- Return the Lua code by which the CON object <name> is referenced in the
624
-- translated code.
624
-- translated code.
625
local function mangle_name(name, prefix)
625
local function mangle_name(name, prefix)
626
    if (name:match("^[A-Za-z_][A-Za-z_0-9]*$") and not LUA_KEYW[name]) then
626
    if (name:match("^[A-Za-z_][A-Za-z_0-9]*$") and not LUA_KEYW[name]) then
627
        return format("_%s.%s", prefix, name)
627
        return format("_%s.%s", prefix, name)
628
    else
628
    else
629
        return format("_%s[%q]", prefix, name)
629
        return format("_%s[%q]", prefix, name)
630
    end
630
    end
631
end
631
end
632
632
633
function on.state_begin_Cmt(_subj, _pos, statename)
633
function on.state_begin_Cmt(_subj, _pos, statename)
634
    -- We must register the state name early (Cmt) because otherwise, it won't
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
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
636
    -- of "Cmt"s in other places, which messes up the sequence of running the
637
    -- semantic actions.
637
    -- semantic actions.
638
    local ourname = mangle_name(statename, "F")
638
    local ourname = mangle_name(statename, "F")
639
    g_funcname[statename] = ourname
639
    g_funcname[statename] = ourname
640
    return true, ourname
640
    return true, ourname
641
end
641
end
642
642
643
function on.state_end(pos, funcname, codetab)
643
function on.state_end(pos, funcname, codetab)
644
    paddcodef(pos, "%s=function(_aci,_pli,_dist)", funcname)
644
    paddcodef(pos, "%s=function(_aci,_pli,_dist)", funcname)
645
    addcode(get_cache_sap_code())
645
    addcode(get_cache_sap_code())
646
    add_code_and_end(codetab, "end")
646
    add_code_and_end(codetab, "end")
647
end
647
end
648
648
649
function on.event_end(pos, eventidx, codetab)
649
function on.event_end(pos, eventidx, codetab)
650
    assert(type(codetab)=="table")
650
    assert(type(codetab)=="table")
651
    -- 0x20000000: actor.FLAGS.chain_beg
651
    -- 0x20000000: actor.FLAGS.chain_beg
652
    paddcodef(pos, "gameevent{%d,0x20000000,function (_aci,_pli,_dist)", eventidx)
652
    paddcodef(pos, "gameevent{%d,0x20000000,function (_aci,_pli,_dist)", eventidx)
653
    addcode(get_cache_sap_code())
653
    addcode(get_cache_sap_code())
654
    addcode(codetab)
654
    addcode(codetab)
655
    addcode("end}")
655
    addcode("end}")
656
656
657
    g_code.event[eventidx] = codetab
657
    g_code.event[eventidx] = codetab
658
end
658
end
659
659
660
function on.eventloadactor_end(pos, tilenum, codetab)
660
function on.eventloadactor_end(pos, tilenum, codetab)
661
    if (on.fnames_tilenum_label(tilenum)) then
661
    if (on.fnames_tilenum_label(tilenum)) then
662
        return
662
        return
663
    end
663
    end
664
664
665
    -- Translate eventloadactor into a chained EVENT_LOADACTOR block
665
    -- Translate eventloadactor into a chained EVENT_LOADACTOR block
666
    paddcodef(pos, "gameevent{'LOADACTOR', function (_aci,_pli,_dist)")
666
    paddcodef(pos, "gameevent{'LOADACTOR', function (_aci,_pli,_dist)")
667
    addcode(get_cache_sap_code())
667
    addcode(get_cache_sap_code())
668
    addcodef("if (%s==%d) then", SPS".picnum", tilenum)
668
    addcodef("if (%s==%d) then", SPS".picnum", tilenum)
669
    addcode(codetab)
669
    addcode(codetab)
670
    addcode("end")
670
    addcode("end")
671
    addcode("end}")
671
    addcode("end}")
672
672
673
    if (g_code.loadactor[tilenum] ~= nil and g_warn["chained-loadactor"]) then
673
    if (g_code.loadactor[tilenum] ~= nil and g_warn["chained-loadactor"]) then
674
        -- NOTE: C-CON redefines loadactor code if encountered multiple times.
674
        -- NOTE: C-CON redefines loadactor code if encountered multiple times.
675
        pwarnprintf(pos, "chained additional loadactor %d code", tilenum)
675
        pwarnprintf(pos, "chained additional loadactor %d code", tilenum)
676
    end
676
    end
677
    g_code.loadactor[tilenum] = codetab
677
    g_code.loadactor[tilenum] = codetab
678
end
678
end
679
679
680
----------
680
----------
681
681
682
local function linecolstr(pos)
682
local function linecolstr(pos)
683
    local line, col = getlinecol(pos)
683
    local line, col = getlinecol(pos)
684
    return format("%d:%d", line, col)
684
    return format("%d:%d", line, col)
685
end
685
end
686
686
687
local function increment_numerrors()
687
local function increment_numerrors()
688
    g_numerrors = g_numerrors+1
688
    g_numerrors = g_numerrors+1
689
    if (g_numerrors == g_maxerrors) then
689
    if (g_numerrors == g_maxerrors) then
690
        g_numerrors = inf
690
        g_numerrors = inf
691
        printf("Too many errors (%d), aborting...", g_maxerrors)
691
        printf("Too many errors (%d), aborting...", g_maxerrors)
692
    end
692
    end
693
end
693
end
694
694
695
function perrprintf(pos, fmt, ...)
695
function perrprintf(pos, fmt, ...)
696
    printf("%s %s: error: "..fmt, g_filename,
696
    printf("%s %s: error: "..fmt, g_filename,
697
           pos and linecolstr(pos) or "???", ...)
697
           pos and linecolstr(pos) or "???", ...)
698
    increment_numerrors()
698
    increment_numerrors()
699
end
699
end
700
700
701
function errprintf(fmt, ...)
701
function errprintf(fmt, ...)
702
    perrprintf(g_lastkwpos, fmt, ...)
702
    perrprintf(g_lastkwpos, fmt, ...)
703
end
703
end
704
704
705
function pwarnprintf(pos, fmt, ...)
705
function pwarnprintf(pos, fmt, ...)
706
    printf("%s %s: warning: "..fmt, g_filename,
706
    printf("%s %s: warning: "..fmt, g_filename,
707
           pos and linecolstr(pos) or "???", ...)
707
           pos and linecolstr(pos) or "???", ...)
708
end
708
end
709
709
710
function warnprintf(fmt, ...)
710
function warnprintf(fmt, ...)
711
    pwarnprintf(g_lastkwpos, fmt, ...)
711
    pwarnprintf(g_lastkwpos, fmt, ...)
712
end
712
end
713
713
714
-- Print a continuation line to an error or warning.
714
-- Print a continuation line to an error or warning.
715
function contprintf(iserr, fmt, ...)
715
function contprintf(iserr, fmt, ...)
716
    printf("%s %s: %s  "..fmt, g_filename,
716
    printf("%s %s: %s  "..fmt, g_filename,
717
           g_lastkwpos and linecolstr(g_lastkwpos) or "???",
717
           g_lastkwpos and linecolstr(g_lastkwpos) or "???",
718
           iserr and "     " or "       ", ...)
718
           iserr and "     " or "       ", ...)
719
end
719
end
720
720
721
local function parse_number(pos, numstr)
721
local function parse_number(pos, numstr)
722
    -- <numstr> is a full number string, potentially prefixed with a minus sign.
722
    -- <numstr> is a full number string, potentially prefixed with a minus sign.
723
    local num = tonumber((numstr:gsub("h$", "")))
723
    local num = tonumber((numstr:gsub("h$", "")))
724
--    local onum = num
724
--    local onum = num
725
    local hex = numstr:match("0[xX]([^h]*)h?")  -- get hex digits, if any
725
    local hex = numstr:match("0[xX]([^h]*)h?")  -- get hex digits, if any
726
726
727
    -- num==nil for Rio Lua, which doesn't handle large hex literals.
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
728
    if (num==nil or not (num >= -0x80000000 and num <= 0xffffffff)) then
729
        -- number is <INT32_MIN or >UINT32_MAX or NaN
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
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.
731
            -- Too many hex digits, but they're all Fs.
732
            pwarnprintf(pos, "number %s truncated to 32 bits", numstr)
732
            pwarnprintf(pos, "number %s truncated to 32 bits", numstr)
733
            num = bit.tobit(num)
733
            num = bit.tobit(num)
734
        else
734
        else
735
            perrprintf(pos, "number %s out of the range of a 32-bit integer", numstr)
735
            perrprintf(pos, "number %s out of the range of a 32-bit integer", numstr)
736
            -- Be careful not to write bound checks like
736
            -- Be careful not to write bound checks like
737
            -- "if (i<LOWBOUND or i>HIGHBOUND) then error('...') end":
737
            -- "if (i<LOWBOUND or i>HIGHBOUND) then error('...') end":
738
            num = NaN
738
            num = NaN
739
        end
739
        end
740
    elseif (num >= 0x80000000) then
740
    elseif (num >= 0x80000000) then
741
        num = bit.tobit(num)
741
        num = bit.tobit(num)
742
        if (not hex and g_warn["number-conversion"]) then
742
        if (not hex and g_warn["number-conversion"]) then
743
            pwarnprintf(pos, "number %s converted to %d", numstr, num)
743
            pwarnprintf(pos, "number %s converted to %d", numstr, num)
744
        end
744
        end
745
    end
745
    end
746
746
747
--    printf("numstr:%s, num=%d (0x%s) '%s', resnum=%d (0x%s)",
747
--    printf("numstr:%s, num=%d (0x%s) '%s', resnum=%d (0x%s)",
748
--           numstr, onum, bit.tohex(onum), hex, num, bit.tohex(num))
748
--           numstr, onum, bit.tohex(onum), hex, num, bit.tohex(num))
749
    return num
749
    return num
750
end
750
end
751
751
752
-- Bound checking functions that generate a compilation error on failure.
752
-- Bound checking functions that generate a compilation error on failure.
753
local check = {}
753
local check = {}
754
754
755
function check.tile_idx(tilenum)
755
function check.tile_idx(tilenum)
756
    if (not (tilenum >= 0 and tilenum < C.MAXTILES)) then
756
    if (not (tilenum >= 0 and tilenum < C.MAXTILES)) then
757
        errprintf("invalid tile number %d", tilenum)
757
        errprintf("invalid tile number %d", tilenum)
758
        return false
758
        return false
759
    end
759
    end
760
    return true
760
    return true
761
end
761
end
762
762
763
function check.sound_idx(sidx)
763
function check.sound_idx(sidx)
764
    if (not (sidx >= 0 and sidx < conl.MAXSOUNDS)) then
764
    if (not (sidx >= 0 and sidx < conl.MAXSOUNDS)) then
765
        errprintf("invalid sound number %d", sidx)
765
        errprintf("invalid sound number %d", sidx)
766
        return false
766
        return false
767
    end
767
    end
768
    return true
768
    return true
769
end
769
end
770
770
771
771
772
-- Mapping of various "define" types to the respective number of members and
772
-- Mapping of various "define" types to the respective number of members and
773
-- vice versa
773
-- vice versa
774
local LABEL = { MOVE=2, AI=3, ACTION=5, [2]="move", [3]="ai", [5]="action",
774
local LABEL = { MOVE=2, AI=3, ACTION=5, [2]="move", [3]="ai", [5]="action",
775
                NUMBER=1, [1]="number" }
775
                NUMBER=1, [1]="number" }
776
776
777
-- Function names in the 'con' module:
777
-- Function names in the 'con' module:
778
local LABEL_FUNCNAME = { [2]="move", [3]="ai", [5]="action" }
778
local LABEL_FUNCNAME = { [2]="move", [3]="ai", [5]="action" }
779
local LABEL_PREFIX = { [2]="M", [3]="I", [5]="C" }  -- _C, _M, _I in the gen'd code
779
local LABEL_PREFIX = { [2]="M", [3]="I", [5]="C" }  -- _C, _M, _I in the gen'd code
780
780
781
local g_labeldef = {}  -- Lua numbers for numbers, strings for composites
781
local g_labeldef = {}  -- Lua numbers for numbers, strings for composites
782
local g_labeltype = {}
782
local g_labeltype = {}
783
local g_labelspecial = {}  -- [<label>] = true
783
local g_labelspecial = {}  -- [<label>] = true
784
local g_labelloc = {}  -- [<label>] = { filename, linenum, colnum }
784
local g_labelloc = {}  -- [<label>] = { filename, linenum, colnum }
785
785
786
-- Get location table for use in continued warning/error reporting.
786
-- Get location table for use in continued warning/error reporting.
787
--[[ local --]]
787
--[[ local --]]
788
function getLocation(kind, pos)
788
function getLocation(kind, pos)
789
    local loc = { g_filename, getlinecol(pos or g_lastkwpos) }
789
    local loc = { g_filename, getlinecol(pos or g_lastkwpos) }
790
    loc[4] = kind
790
    loc[4] = kind
791
    return loc
791
    return loc
792
end
792
end
793
793
794
function reset.labels()
794
function reset.labels()
795
    g_badids = {}
795
    g_badids = {}
796
796
797
    -- NO is also a valid `move', `ai' or `action', but they are handled
797
    -- NO is also a valid `move', `ai' or `action', but they are handled
798
    -- separately in lookup.composite().
798
    -- separately in lookup.composite().
799
    g_labeldef = {
799
    g_labeldef = {
800
        NO = 0,
800
        NO = 0,
801
        -- NOTE: these are read-only gamevars in C-CON
801
        -- NOTE: these are read-only gamevars in C-CON
802
        CLIPMASK0 = 65536+1,  -- blocking
802
        CLIPMASK0 = 65536+1,  -- blocking
803
        CLIPMASK1 = (256*65536)+64,  -- hittable
803
        CLIPMASK1 = (256*65536)+64,  -- hittable
804
        -- TODO_MP
804
        -- TODO_MP
805
        COOP = 0,
805
        COOP = 0,
806
        MULTIMODE = 1,
806
        MULTIMODE = 1,
807
        numplayers = 1,
807
        numplayers = 1,
808
        myconnectindex = 0,
808
        myconnectindex = 0,
809
        -- Predefined constants
809
        -- Predefined constants
810
        MAXSTATUS = C.MAXSTATUS,
810
        MAXSTATUS = C.MAXSTATUS,
811
        MAXSPRITES = C.MAXSPRITES,
811
        MAXSPRITES = C.MAXSPRITES,
812
        MAX_WEAPONS = C.MAX_WEAPONS,
812
        MAX_WEAPONS = C.MAX_WEAPONS,
813
    }
813
    }
814
814
815
    g_labeltype = {}
815
    g_labeltype = {}
816
    g_labelspecial = {}
816
    g_labelspecial = {}
817
    g_labelloc = {}
817
    g_labelloc = {}
818
818
819
    for varname,_ in pairs(g_labeldef) do
819
    for varname,_ in pairs(g_labeldef) do
820
        g_labeltype[varname] = LABEL.NUMBER
820
        g_labeltype[varname] = LABEL.NUMBER
821
        g_labelspecial[varname] = true
821
        g_labelspecial[varname] = true
822
    end
822
    end
823
823
824
    -- Initialize default defines.
824
    -- Initialize default defines.
825
    for i=1,#conl.labels do
825
    for i=1,#conl.labels do
826
        for label, val in pairs(conl.labels[i]) do
826
        for label, val in pairs(conl.labels[i]) do
827
            g_labeldef[label] = val
827
            g_labeldef[label] = val
828
            g_labeltype[label] = LABEL.NUMBER
828
            g_labeltype[label] = LABEL.NUMBER
829
        end
829
        end
830
    end
830
    end
831
end
831
end
832
832
833
function lookup.defined_label(pos, maybe_minus_str, identifier)
833
function lookup.defined_label(pos, maybe_minus_str, identifier)
834
    local num = g_labeldef[identifier]
834
    local num = g_labeldef[identifier]
835
835
836
    if (num == nil) then
836
    if (num == nil) then
837
        perrprintf(pos, "label \"%s\" is not defined", identifier)
837
        perrprintf(pos, "label \"%s\" is not defined", identifier)
838
        return -inf  -- return a number for type cleanness
838
        return -inf  -- return a number for type cleanness
839
    end
839
    end
840
840
841
    if (g_labeltype[identifier] ~= LABEL.NUMBER) then
841
    if (g_labeltype[identifier] ~= LABEL.NUMBER) then
842
        perrprintf(pos, "label \"%s\" is not a `define'd number", identifier)
842
        perrprintf(pos, "label \"%s\" is not a `define'd number", identifier)
843
        return -inf
843
        return -inf
844
    end
844
    end
845
845
846
    assert(type(num)=="number")
846
    assert(type(num)=="number")
847
847
848
    return (maybe_minus_str=="" and 1 or -1) * num
848
    return (maybe_minus_str=="" and 1 or -1) * num
849
end
849
end
850
850
851
assert(not BAD_ID_CHARS1:find(":"))
851
assert(not BAD_ID_CHARS1:find(":"))
852
function lookup.raw_defined_label(pos, maybe_minus_str, identifier)
852
function lookup.raw_defined_label(pos, maybe_minus_str, identifier)
853
    return pos..":"..maybe_minus_str..":"..identifier
853
    return pos..":"..maybe_minus_str..":"..identifier
854
end
854
end
855
855
856
local dynmap = {}
856
local dynmap = {}
857
-- When necessary, initialize dynamic {tile,sound} mapping list.
857
-- When necessary, initialize dynamic {tile,sound} mapping list.
858
function dynmap.maybe_init(dyni, dynList)
858
function dynmap.maybe_init(dyni, dynList)
859
    if (dyni[1]==nil) then
859
    if (dyni[1]==nil) then
860
        dyni[1] = true
860
        dyni[1] = true
861
        -- Init name -> g_dyn*List index mapping
861
        -- Init name -> g_dyn*List index mapping
862
        for i=0,math.huge do
862
        for i=0,math.huge do
863
            local str = dynList[i].str
863
            local str = dynList[i].str
864
            if (str==nil) then
864
            if (str==nil) then
865
                break
865
                break
866
            end
866
            end
867
867
868
            dyni[ffi.string(str)] = i
868
            dyni[ffi.string(str)] = i
869
        end
869
        end
870
    end
870
    end
871
end
871
end
872
872
873
-- Potentially process one dynamic {tile,sound} remapping.
873
-- Potentially process one dynamic {tile,sound} remapping.
874
function dynmap.maybe_process(dyni, dynList, identifier, num)
874
function dynmap.maybe_process(dyni, dynList, identifier, num)
875
    if (dyni[identifier]) then
875
    if (dyni[identifier]) then
876
        local di = dynList[dyni[identifier]]
876
        local di = dynList[dyni[identifier]]
877
877
878
        if (ffiC._DEBUG_LUNATIC~=0 and di.staticval~=num) then
878
        if (ffiC._DEBUG_LUNATIC~=0 and di.staticval~=num) then
879
            printf("REMAP %s (%d) --> %d", ffi.string(di.str), di.staticval, num)
879
            printf("REMAP %s (%d) --> %d", ffi.string(di.str), di.staticval, num)
880
        end
880
        end
881
        di.dynvalptr[0] = num
881
        di.dynvalptr[0] = num
882
    end
882
    end
883
end
883
end
884
884
885
-- The 'check' table is also used to hold a couple of misc checkers.
885
-- The 'check' table is also used to hold a couple of misc checkers.
886
886
887
function check.sysvar_def_attempt(identifier)
887
function check.sysvar_def_attempt(identifier)
888
    if (identifier=="actorvar") then
888
    if (identifier=="actorvar") then
889
        errprintf("cannot define reserved symbol `actorvar'")
889
        errprintf("cannot define reserved symbol `actorvar'")
890
        return true
890
        return true
891
    end
891
    end
892
    if (identifier=="_IS_NORESET_GAMEVAR") then
892
    if (identifier=="_IS_NORESET_GAMEVAR") then
893
        errprintf("cannot define reserved symbol `_IS_NORESET_GAMEVAR'")
893
        errprintf("cannot define reserved symbol `_IS_NORESET_GAMEVAR'")
894
        return true
894
        return true
895
    end
895
    end
896
end
896
end
897
897
898
898
899
local inform = {}
899
local inform = {}
900
900
901
function inform.common(loc, iserr)
901
function inform.common(loc, iserr)
902
    if (loc) then
902
    if (loc) then
903
        contprintf(iserr, "Old definition is at %s %d:%d", loc[1], loc[2], loc[3])
903
        contprintf(iserr, "Old definition is at %s %d:%d", loc[1], loc[2], loc[3])
904
    else
904
    else
905
        contprintf(iserr, "Old definition is built-in")
905
        contprintf(iserr, "Old definition is built-in")
906
    end
906
    end
907
end
907
end
908
908
909
function inform.olddef_location(identifier, iserr)
909
function inform.olddef_location(identifier, iserr)
910
    inform.common(g_labelloc[identifier], iserr)
910
    inform.common(g_labelloc[identifier], iserr)
911
end
911
end
912
912
913
function inform.oldgv_location(identifier, iserr)
913
function inform.oldgv_location(identifier, iserr)
914
    inform.common(g_gamevar[identifier].loc, iserr)
914
    inform.common(g_gamevar[identifier].loc, iserr)
915
end
915
end
916
916
917
917
918
local Define = {}
918
local Define = {}
919
919
920
function Define.label(identifier, num)
920
function Define.label(identifier, num)
921
    if (check.sysvar_def_attempt(identifier)) then
921
    if (check.sysvar_def_attempt(identifier)) then
922
        return
922
        return
923
    end
923
    end
924
924
925
    local oldtype = g_labeltype[identifier]
925
    local oldtype = g_labeltype[identifier]
926
    local oldval = g_labeldef[identifier]
926
    local oldval = g_labeldef[identifier]
927
927
928
    if (oldval) then
928
    if (oldval) then
929
        if (oldtype ~= LABEL.NUMBER) then
929
        if (oldtype ~= LABEL.NUMBER) then
930
            errprintf("Refusing to overwrite `%s' label \"%s\" with a `define'd number.",
930
            errprintf("Refusing to overwrite `%s' label \"%s\" with a `define'd number.",
931
                      LABEL[oldtype], identifier)
931
                      LABEL[oldtype], identifier)
932
            inform.olddef_location(identifier, true)
932
            inform.olddef_location(identifier, true)
933
        else
933
        else
934
            -- conl.labels[...]: don't warn for wrong PROJ_ redefinitions
934
            -- conl.labels[...]: don't warn for wrong PROJ_ redefinitions
935
            if (g_warn["not-redefined"]) then
935
            if (g_warn["not-redefined"]) then
936
                if (oldval ~= num and conl.PROJ[identifier]==nil) then
936
                if (oldval ~= num and conl.PROJ[identifier]==nil) then
937
                    warnprintf("Label \"%s\" not redefined with new value %d (old: %d).",
937
                    warnprintf("Label \"%s\" not redefined with new value %d (old: %d).",
938
                               identifier, num, oldval)
938
                               identifier, num, oldval)
939
                    inform.olddef_location(identifier, false)
939
                    inform.olddef_location(identifier, false)
940
                end
940
                end
941
            end
941
            end
942
        end
942
        end
943
    else
943
    else
944
        if (g_gamevar[identifier]) then
944
        if (g_gamevar[identifier]) then
945
            warnprintf("symbol `%s' already used for game variable", identifier)
945
            warnprintf("symbol `%s' already used for game variable", identifier)
946
            inform.oldgv_location(identifier, false)
946
            inform.oldgv_location(identifier, false)
947
        end
947
        end
948
948
949
        if (ffi and g_dyntilei and (num>=0 and num<C.MAXTILES)) then
949
        if (ffi and g_dyntilei and (num>=0 and num<C.MAXTILES)) then
950
            dynmap.maybe_init(g_dyntilei, ffiC.g_dynTileList)
950
            dynmap.maybe_init(g_dyntilei, ffiC.g_dynTileList)
951
            dynmap.maybe_process(g_dyntilei, ffiC.g_dynTileList, identifier, num)
951
            dynmap.maybe_process(g_dyntilei, ffiC.g_dynTileList, identifier, num)
952
        end
952
        end
953
953
954
        -- New definition of a label
954
        -- New definition of a label
955
        g_labeldef[identifier] = num
955
        g_labeldef[identifier] = num
956
        g_labeltype[identifier] = LABEL.NUMBER
956
        g_labeltype[identifier] = LABEL.NUMBER
957
        g_labelloc[identifier] = getLocation()
957
        g_labelloc[identifier] = getLocation()
958
    end
958
    end
959
end
959
end
960
960
961
function check.composite_literal(labeltype, pos, num)
961
function check.composite_literal(labeltype, pos, num)
962
    if (num==0 or num==1) then
962
    if (num==0 or num==1) then
963
        return (num==0) and "0" or "1"
963
        return (num==0) and "0" or "1"
964
    else
964
    else
965
        perrprintf(pos, "literal `%s' number must be either 0 or 1", LABEL[labeltype])
965
        perrprintf(pos, "literal `%s' number must be either 0 or 1", LABEL[labeltype])
966
        return "_INVALIT"
966
        return "_INVALIT"
967
    end
967
    end
968
end
968
end
969
969
970
function lookup.composite(labeltype, pos, identifier)
970
function lookup.composite(labeltype, pos, identifier)
971
    if (identifier=="NO") then
971
    if (identifier=="NO") then
972
        -- NO is a special case and is valid for move, action and ai,
972
        -- NO is a special case and is valid for move, action and ai,
973
        -- being the same as passing a literal 0.
973
        -- being the same as passing a literal 0.
974
        return "0"
974
        return "0"
975
    end
975
    end
976
976
977
    local val = g_labeldef[identifier]
977
    local val = g_labeldef[identifier]
978
    local typ = g_labeltype[identifier]
978
    local typ = g_labeltype[identifier]
979
979
980
    if (val == nil) then
980
    if (val == nil) then
981
        perrprintf(pos, "label \"%s\" is not defined", identifier)
981
        perrprintf(pos, "label \"%s\" is not defined", identifier)
982
        return "_NOTDEF"
982
        return "_NOTDEF"
983
    elseif (typ ~= labeltype) then
983
    elseif (typ ~= labeltype) then
984
        if (identifier=="randomangle" and labeltype==LABEL.MOVE and typ==LABEL.NUMBER) 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.
985
            -- Be forgiving with a 1.3/1.5 GAME.CON type error.
986
            pwarnprintf(pos, "label \"randomangle\" is not a `move' value, assuming 0")
986
            pwarnprintf(pos, "label \"randomangle\" is not a `move' value, assuming 0")
987
            return "0"
987
            return "0"
988
        elseif (identifier=="BLIMPRESPAWNTIME" and labeltype==LABEL.ACTION and typ==LABEL.NUMBER) then
988
        elseif (identifier=="BLIMPRESPAWNTIME" and labeltype==LABEL.ACTION and typ==LABEL.NUMBER) then
989
            -- Be forgiving with a 1.3 GAME.CON type error.
989
            -- Be forgiving with a 1.3 GAME.CON type error.
990
            pwarnprintf(pos, "label \"BLIMPRESPAWNTIME\" is not an `action' value, assuming 0")
990
            pwarnprintf(pos, "label \"BLIMPRESPAWNTIME\" is not an `action' value, assuming 0")
991
            return "0"
991
            return "0"
992
        else
992
        else
993
            perrprintf(pos, "label \"%s\" is not a%s `%s' value", identifier,
993
            perrprintf(pos, "label \"%s\" is not a%s `%s' value", identifier,
994
                       labeltype==LABEL.MOVE and "" or "n", LABEL[labeltype])
994
                       labeltype==LABEL.MOVE and "" or "n", LABEL[labeltype])
995
            return "_WRONGTYPE"
995
            return "_WRONGTYPE"
996
        end
996
        end
997
    end
997
    end
998
998
999
    return val
999
    return val
1000
end
1000
end
1001
1001
1002
function check.reserved_bits(flags, allowedbits, suffix)
1002
function check.reserved_bits(flags, allowedbits, suffix)
1003
    local rbits = bit.bnot(allowedbits)
1003
    local rbits = bit.bnot(allowedbits)
1004
    if (bit.band(flags, rbits) ~= 0) then
1004
    if (bit.band(flags, rbits) ~= 0) then
1005
        warnprintf("set one or more reserved bits (0x%s) "..suffix,
1005
        warnprintf("set one or more reserved bits (0x%s) "..suffix,
1006
                   bit.tohex(bit.band(flags, rbits)))
1006
                   bit.tohex(bit.band(flags, rbits)))
1007
    end
1007
    end
1008
end
1008
end
1009
1009
1010
-- KEEPINSYNC control.lua
1010
-- KEEPINSYNC control.lua
1011
Define.ALLOWED_VIEWTYPE = truetab { 0, 1, 2, 3,4, 5, 7, 8, -5, -7 }
1011
Define.ALLOWED_VIEWTYPE = truetab { 0, 1, 2, 3,4, 5, 7, 8, -5, -7 }
1012
1012
1013
function Define.composite(labeltype, identifier, ...)
1013
function Define.composite(labeltype, identifier, ...)
1014
    local oldtype = g_labeltype[identifier]
1014
    local oldtype = g_labeltype[identifier]
1015
    local oldval = g_labeldef[identifier]
1015
    local oldval = g_labeldef[identifier]
1016
1016
1017
    if (oldval) then
1017
    if (oldval) then
1018
        if (oldtype ~= labeltype) then
1018
        if (oldtype ~= labeltype) then
1019
            errprintf("Refusing to overwrite `%s' label \"%s\" with a `%s' value.",
1019
            errprintf("Refusing to overwrite `%s' label \"%s\" with a `%s' value.",
1020
                      LABEL[oldtype], identifier, LABEL[labeltype])
1020
                      LABEL[oldtype], identifier, LABEL[labeltype])
1021
            inform.olddef_location(identifier, true)
1021
            inform.olddef_location(identifier, true)
1022
        else
1022
        else
1023
            warnprintf("Duplicate `%s' definition of \"%s\" ignored.",
1023
            warnprintf("Duplicate `%s' definition of \"%s\" ignored.",
1024
                       LABEL[labeltype], identifier)
1024
                       LABEL[labeltype], identifier)
1025
            inform.olddef_location(identifier, false)
1025
            inform.olddef_location(identifier, false)
1026
        end
1026
        end
1027
        return
1027
        return
1028
    end
1028
    end
1029
1029
1030
    -- Fill up omitted arguments denoting composites with zeros.
1030
    -- Fill up omitted arguments denoting composites with zeros.
1031
    local isai = (labeltype == LABEL.AI)
1031
    local isai = (labeltype == LABEL.AI)
1032
    local args = {...}
1032
    local args = {...}
1033
    for i=#args+1,labeltype do
1033
    for i=#args+1,labeltype do
1034
        -- Passing nil/nothing as remaining args to con.ai will make the
1034
        -- Passing nil/nothing as remaining args to con.ai will make the
1035
        -- action/move the null one.
1035
        -- action/move the null one.
1036
        args[i] = (isai and i<=2) and "nil" or 0
1036
        args[i] = (isai and i<=2) and "nil" or 0
1037
    end
1037
    end
1038
1038
1039
    if (isai) then
1039
    if (isai) then
1040
        assert(type(args[1])=="string")
1040
        assert(type(args[1])=="string")
1041
        assert(type(args[2])=="string")
1041
        assert(type(args[2])=="string")
1042
1042
1043
        -- OR together the flags
1043
        -- OR together the flags
1044
        for i=#args,LABEL.AI+1, -1 do
1044
        for i=#args,LABEL.AI+1, -1 do
1045
            args[LABEL.AI] = bit.bor(args[LABEL.AI], args[i])
1045
            args[LABEL.AI] = bit.bor(args[LABEL.AI], args[i])
1046
            args[i] = nil
1046
            args[i] = nil
1047
        end
1047
        end
1048
1048
1049
        -- Check whether movflags use reserved bits.
1049
        -- Check whether movflags use reserved bits.
1050
        check.reserved_bits(args[LABEL.AI], 4096+2047, "for ai's movflags")
1050
        check.reserved_bits(args[LABEL.AI], 4096+2047, "for ai's movflags")
1051
    end
1051
    end
1052
1052
1053
    if (labeltype == LABEL.ACTION) then
1053
    if (labeltype == LABEL.ACTION) then
1054
        -- Sanity-check action members.
1054
        -- Sanity-check action members.
1055
        -- KEEPINSYNC with ACTOR_CHECK in control.lua for consistency.
1055
        -- KEEPINSYNC with ACTOR_CHECK in control.lua for consistency.
1056
        if (not (args[2] >= 0)) then
1056
        if (not (args[2] >= 0)) then
1057
            errprintf("action \"%s\" has negative number of frames", identifier)
1057
            errprintf("action \"%s\" has negative number of frames", identifier)
1058
        end
1058
        end
1059
        if (Define.ALLOWED_VIEWTYPE[args[3]] == nil) then
1059
        if (Define.ALLOWED_VIEWTYPE[args[3]] == nil) then
1060
            errprintf("action \"%s\" has disallowed viewtype %d", identifier, args[3])
1060
            errprintf("action \"%s\" has disallowed viewtype %d", identifier, args[3])
1061
        end
1061
        end
1062
        if (not (args[4] >= -1 and args[4] <= 1)) then
1062
        if (not (args[4] >= -1 and args[4] <= 1)) then
1063
            warnprintf("action \"%s\" has incval different from -1, 0 or 1", identifier)
1063
            warnprintf("action \"%s\" has incval different from -1, 0 or 1", identifier)
1064
        end
1064
        end
1065
    end
1065
    end
1066
1066
1067
    -- Make a string out of that.
1067
    -- Make a string out of that.
1068
    for i=1+(isai and 2 or 0),#args do
1068
    for i=1+(isai and 2 or 0),#args do
1069
        args[i] = format("%d", args[i])
1069
        args[i] = format("%d", args[i])
1070
    end
1070
    end
1071
1071
1072
    local refcode = mangle_name(identifier, LABEL_PREFIX[labeltype])
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
1073
    addcodef(isai and "%s=_con.%s(%s)" or "%s=_con.%s{%s}",  -- ai has parens
1074
             refcode, LABEL_FUNCNAME[labeltype], table.concat(args, ","))
1074
             refcode, LABEL_FUNCNAME[labeltype], table.concat(args, ","))
1075
1075
1076
    g_labeldef[identifier] = refcode
1076
    g_labeldef[identifier] = refcode
1077
    g_labeltype[identifier] = labeltype
1077
    g_labeltype[identifier] = labeltype
1078
    g_labelloc[identifier] = getLocation()
1078
    g_labelloc[identifier] = getLocation()
1079
end
1079
end
1080
1080
1081
1081
1082
local function parse(contents) end -- fwd-decl
1082
local function parse(contents) end -- fwd-decl
1083
1083
1084
local function do_include_file(dirname, filename, isroot)
1084
local function do_include_file(dirname, filename, isroot)
1085
    assert(type(filename)=="string")
1085
    assert(type(filename)=="string")
1086
1086
1087
    if (g_have_file[filename] ~= nil) then
1087
    if (g_have_file[filename] ~= nil) then
1088
        printf("[%d] Fatal error: infinite loop including \"%s\"", g_recurslevel, filename)
1088
        printf("[%d] Fatal error: infinite loop including \"%s\"", g_recurslevel, filename)
1089
        g_numerrors = inf
1089
        g_numerrors = inf
1090
        return
1090
        return
1091
    end
1091
    end
1092
1092
1093
    local contents
1093
    local contents
1094
1094
1095
    if (read_into_string) then
1095
    if (read_into_string) then
1096
        -- running from EDuke32
1096
        -- running from EDuke32
1097
        contents = read_into_string(filename)
1097
        contents = read_into_string(filename)
1098
    else
1098
    else
1099
        -- running stand-alone
1099
        -- running stand-alone
1100
        local io = require("io")
1100
        local io = require("io")
1101
1101
1102
        local fd, msg = io.open(dirname..filename)
1102
        local fd, msg = io.open(dirname..filename)
1103
        while (fd == nil and not isroot and filename:find("/")) do
1103
        while (fd == nil and not isroot and filename:find("/")) do
1104
            -- strip up to and including first slash:
1104
            -- strip up to and including first slash:
1105
            filename = filename:gsub("^.-/", "")
1105
            filename = filename:gsub("^.-/", "")
1106
            fd, msg = io.open(dirname..filename)
1106
            fd, msg = io.open(dirname..filename)
1107
        end
1107
        end
1108
1108
1109
        -- As a last resort, try the "default directory"
1109
        -- As a last resort, try the "default directory"
1110
        if (fd==nil and not isroot and g_defaultDir) then
1110
        if (fd==nil and not isroot and g_defaultDir) then
1111
            -- strip up to and including last slash (if any):
1111
            -- strip up to and including last slash (if any):
1112
            filename = filename:gsub("^.*/", "")
1112
            filename = filename:gsub("^.*/", "")
1113
            dirname = g_defaultDir.."/"
1113
            dirname = g_defaultDir.."/"
1114
            fd, msg = io.open(dirname..filename)
1114
            fd, msg = io.open(dirname..filename)
1115
        end
1115
        end
1116
1116
1117
        if (fd == nil) then
1117
        if (fd == nil) then
1118
            printf("[%d] Fatal error: couldn't open %s", g_recurslevel, msg)
1118
            printf("[%d] Fatal error: couldn't open %s", g_recurslevel, msg)
1119
            g_numerrors = inf
1119
            g_numerrors = inf
1120
            return
1120
            return
1121
        end
1121
        end
1122
1122
1123
        contents = fd:read("*all")
1123
        contents = fd:read("*all")
1124
        fd:close()
1124
        fd:close()
1125
    end
1125
    end
1126
1126
1127
    if (contents == nil) then
1127
    if (contents == nil) then
1128
        -- maybe that file name turned out to be a directory or other
1128
        -- maybe that file name turned out to be a directory or other
1129
        -- special file accidentally
1129
        -- special file accidentally
1130
        printf("[%d] Fatal error: couldn't read from \"%s\"",
1130
        printf("[%d] Fatal error: couldn't read from \"%s\"",
1131
               g_recurslevel, dirname..filename)
1131
               g_recurslevel, dirname..filename)
1132
        g_numerrors = inf
1132
        g_numerrors = inf
1133
        return
1133
        return
1134
    end
1134
    end
1135
1135
1136
    printf("%s[%d] Translating file \"%s\"", (g_recurslevel==-1 and "\n---- ") or "",
1136
    printf("%s[%d] Translating file \"%s\"", (g_recurslevel==-1 and "\n---- ") or "",
1137
           g_recurslevel+1, dirname..filename);
1137
           g_recurslevel+1, dirname..filename);
1138
1138
1139
    local oldfilename = g_filename
1139
    local oldfilename = g_filename
1140
    g_filename = filename
1140
    g_filename = filename
1141
    parse(contents)
1141
    parse(contents)
1142
    g_filename = oldfilename
1142
    g_filename = oldfilename
1143
end
1143
end
1144
1144
1145
-- Table of various outer command handling functions.
1145
-- Table of various outer command handling functions.
1146
local Cmd = {}
1146
local Cmd = {}
1147
1147
1148
function Cmd.NYI(msg)
1148
function Cmd.NYI(msg)
1149
    return function()
1149
    return function()
1150
        errprintf(msg.." not yet implemented")
1150
        errprintf(msg.." not yet implemented")
1151
    end
1151
    end
1152
end
1152
end
1153
1153
1154
function Cmd.nyi(msg)
1154
function Cmd.nyi(msg)
1155
    return function()
1155
    return function()
1156
        warnprintf(msg.." not yet implemented")
1156
        warnprintf(msg.." not yet implemented")
1157
    end
1157
    end
1158
end
1158
end
1159
1159
1160
function Cmd.include(filename)
1160
function Cmd.include(filename)
1161
    do_include_file(g_directory, filename, false)
1161
    do_include_file(g_directory, filename, false)
1162
end
1162
end
1163
1163
1164
--- Per-module game data
1164
--- Per-module game data
1165
local g_data = {}
1165
local g_data = {}
1166
local EPMUL = conl.MAXLEVELS
1166
local EPMUL = conl.MAXLEVELS
1167
1167
1168
function reset.gamedata()
1168
function reset.gamedata()
1169
    g_data = {}
1169
    g_data = {}
1170
1170
1171
    -- [EPMUL*ep + lev] = { ptime=<num>, dtime=<num>, fn=<str>, name=<str> }
1171
    -- [EPMUL*ep + lev] = { ptime=<num>, dtime=<num>, fn=<str>, name=<str> }
1172
    g_data.level = {}
1172
    g_data.level = {}
1173
    -- [ep] = <str>
1173
    -- [ep] = <str>
1174
    g_data.volname = {}
1174
    g_data.volname = {}
1175
    -- [skillnum] = <str>
1175
    -- [skillnum] = <str>
1176
    g_data.skillname = {}
1176
    g_data.skillname = {}
1177
    -- [quotenum] = <str>
1177
    -- [quotenum] = <str>
1178
    g_data.quote = {}
1178
    g_data.quote = {}
1179
    -- table of length 26 or 30 containg numbers
1179
    -- table of length 26 or 30 containg numbers
1180
    g_data.startup = {}
1180
    g_data.startup = {}
1181
    -- [soundnum] = { fn=<str>, params=<table of length 5> }
1181
    -- [soundnum] = { fn=<str>, params=<table of length 5> }
1182
    g_data.sound = {}
1182
    g_data.sound = {}
1183
    -- [volnum] = <table of length numlevels (<= MAXLEVELS) of <str>>
1183
    -- [volnum] = <table of length numlevels (<= MAXLEVELS) of <str>>
1184
    g_data.music = {}
1184
    g_data.music = {}
1185
end
1185
end
1186
1186
1187
-- TODO: PRE13 has no <dtstr> (3D Realms time).
1187
-- TODO: PRE13 has no <dtstr> (3D Realms time).
1188
function Cmd.definelevelname(vol, lev, fn, ptstr, dtstr, levname)
1188
function Cmd.definelevelname(vol, lev, fn, ptstr, dtstr, levname)
1189
    if (not (vol >= 0 and vol < conl.MAXVOLUMES)) then
1189
    if (not (vol >= 0 and vol < conl.MAXVOLUMES)) then
1190
        errprintf("volume number exceeds maximum volume count.")
1190
        errprintf("volume number exceeds maximum volume count.")
1191
        return
1191
        return
1192
    end
1192
    end
1193
1193
1194
    if (not (lev >= 0 and lev < conl.MAXLEVELS)) then
1194
    if (not (lev >= 0 and lev < conl.MAXLEVELS)) then
1195
        errprintf("level number exceeds maximum number of levels per episode.")
1195
        errprintf("level number exceeds maximum number of levels per episode.")
1196
        return
1196
        return
1197
    end
1197
    end
1198
1198
1199
    -- TODO: Bcorrectfilename(fn)
1199
    -- TODO: Bcorrectfilename(fn)
1200
1200
1201
    local function secs(tstr)
1201
    local function secs(tstr)
1202
        local m, s = string.match(tstr, ".+:.+")
1202
        local m, s = string.match(tstr, ".+:.+")
1203
        m, s = tonumber(m), tonumber(s)
1203
        m, s = tonumber(m), tonumber(s)
1204
        return (m and s) and m*60+s or 0
1204
        return (m and s) and m*60+s or 0
1205
    end
1205
    end
1206
1206
1207
    local map = {
1207
    local map = {
1208
        ptime=secs(ptstr), dtime=secs(dtstr), fn="/"..fn, name=levname
1208
        ptime=secs(ptstr), dtime=secs(dtstr), fn="/"..fn, name=levname
1209
    }
1209
    }
1210
1210
1211
    if (ffi) then
1211
    if (ffi) then
1212
        ffiC.C_DefineLevelName(vol, lev, map.fn, map.ptime, map.dtime, map.name)
1212
        ffiC.C_DefineLevelName(vol, lev, map.fn, map.ptime, map.dtime, map.name)
1213
    end
1213
    end
1214
1214
1215
    g_data.level[EPMUL*vol+lev] = map
1215
    g_data.level[EPMUL*vol+lev] = map
1216
end
1216
end
1217
1217
-
 
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
1218
local function defineXname(what, ffiCfuncname, X, name)
1234
local function defineXname(what, ffiCfuncname, X, name)
1219
    name = name:upper()
-
 
1220
    if (ffi) then
1235
    if (ffi) then
1221
        ffiC[ffiCfuncname](X, name)
1236
        ffiC[ffiCfuncname](X, name)
1222
        if (#name > 32) then
1237
        if (#name > 32) then
1223
            warnprintf("%s %d name truncated to 32 characters.", what, X)
1238
            warnprintf("%s %d name truncated to 32 characters.", what, X)
1224
        end
1239
        end
1225
    end
1240
    end
1226
    return name
1241
    return name
1227
end
1242
end
1228
1243
1229
function Cmd.defineskillname(skillnum, name)
1244
function Cmd.defineskillname(skillnum, name)
1230
    if (not (skillnum >= 0 and skillnum < conl.MAXSKILLS)) then
1245
    if (not (skillnum >= 0 and skillnum < conl.MAXSKILLS)) then
1231
        errprintf("skill number is negative or exceeds maximum skill count.")
1246
        errprintf("skill number is negative or exceeds maximum skill count.")
1232
        return
1247
        return
1233
    end
1248
    end
1234
1249
1235
    name = defineXname("skill", "C_DefineSkillName", skillnum, name)
1250
    name = defineXname("skill", "C_DefineSkillName", skillnum, name)
1236
    g_data.skillname[skillnum] = name
1251
    g_data.skillname[skillnum] = name
1237
end
1252
end
1238
1253
-
 
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
1239
function Cmd.definevolumename(vol, name)
1265
function Cmd.definevolumename(vol, name)
1240
    if (not (vol >= 0 and vol < conl.MAXVOLUMES)) then
1266
    if (not (vol >= 0 and vol < conl.MAXVOLUMES)) then
1241
        errprintf("volume number is negative or exceeds maximum volume count.")
1267
        errprintf("volume number is negative or exceeds maximum volume count.")
1242
        return
1268
        return
1243
    end
1269
    end
1244
1270
1245
    name = defineXname("volume", "C_DefineVolumeName", vol, name)
1271
    name = defineXname("volume", "C_DefineVolumeName", vol, name)
1246
    g_data.volname[vol] = name
1272
    g_data.volname[vol] = name
1247
end
1273
end
1248
1274
-
 
1275
function Cmd.undefinevolume(vol)
-
 
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_UndefineVolume(vol)
-
 
1283
    end
-
 
1284
end
-
 
1285
1249
function Cmd.definegamefuncname(idx, name)
1286
function Cmd.definegamefuncname(idx, name)
1250
    local NUMGAMEFUNCTIONS = (ffi and ffiC.NUMGAMEFUNCTIONS or 56)
1287
    local NUMGAMEFUNCTIONS = (ffi and ffiC.NUMGAMEFUNCTIONS or 56)
1251
    if (not (idx >= 0 and idx < NUMGAMEFUNCTIONS)) then
1288
    if (not (idx >= 0 and idx < NUMGAMEFUNCTIONS)) then
1252
        errprintf("function number exceeds number of game functions.")
1289
        errprintf("function number exceeds number of game functions.")
1253
        return
1290
        return
1254
    end
1291
    end
1255
1292
1256
    assert(type(name)=="string")
1293
    assert(type(name)=="string")
1257
    -- XXX: in place of C-CON's "invalid character in function name" report:
1294
    -- XXX: in place of C-CON's "invalid character in function name" report:
1258
    name = name:gsub("[^A-Za-z0-9]", "_")
1295
    name = name:gsub("[^A-Za-z0-9]", "_")
1259
1296
1260
    if (ffi) then
1297
    if (ffi) then
1261
        ffiC.C_DefineGameFuncName(idx, name)
1298
        ffiC.C_DefineGameFuncName(idx, name)
1262
    end
1299
    end
1263
end
1300
end
1264
1301
1265
function Cmd.definegametype(idx, flags, name)
1302
function Cmd.definegametype(idx, flags, name)
1266
    if (not (idx >= 0 and idx < conl.MAXGAMETYPES)) then
1303
    if (not (idx >= 0 and idx < conl.MAXGAMETYPES)) then
1267
        errprintf("gametype number exceeds maximum gametype count.")
1304
        errprintf("gametype number exceeds maximum gametype count.")
1268
        return
1305
        return
1269
    end
1306
    end
1270
1307
1271
    if (ffi) then
1308
    if (ffi) then
1272
        ffiC.C_DefineGameType(idx, flags, name)
1309
        ffiC.C_DefineGameType(idx, flags, name)
1273
    end
1310
    end
1274
end
1311
end
1275
1312
1276
-- strip whitespace from front and back
1313
-- strip whitespace from front and back
1277
local function stripws(str)
1314
local function stripws(str)
1278
    return str:match("^%s*(.*)%s*$")
1315
    return str:match("^%s*(.*)%s*$")
1279
end
1316
end
1280
1317
1281
function Cmd.definequote(qnum, quotestr)
1318
function Cmd.definequote(qnum, quotestr)
1282
    if (not (qnum >= 0 and qnum < conl.MAXQUOTES)) then
1319
    if (not (qnum >= 0 and qnum < conl.MAXQUOTES)) then
1283
        errprintf("quote number is negative or exceeds limit of %d.", conl.MAXQUOTES-1)
1320
        errprintf("quote number is negative or exceeds limit of %d.", conl.MAXQUOTES-1)
1284
        return ""
1321
        return ""
1285
    end
1322
    end
1286
1323
1287
    quotestr = stripws(quotestr)
1324
    quotestr = stripws(quotestr)
1288
1325
1289
    if (#quotestr >= conl.MAXQUOTELEN) then
1326
    if (#quotestr >= conl.MAXQUOTELEN) then
1290
        -- NOTE: Actually, C_DefineQuote takes care of this! That is,
1327
        -- NOTE: Actually, C_DefineQuote takes care of this! That is,
1291
        -- standalone, the string isn't truncated.
1328
        -- standalone, the string isn't truncated.
1292
        warnprintf("quote %d truncated to %d characters.", qnum, conl.MAXQUOTELEN-1)
1329
        warnprintf("quote %d truncated to %d characters.", qnum, conl.MAXQUOTELEN-1)
1293
    end
1330
    end
1294
1331
1295
    if (ffi) then
1332
    if (ffi) then
1296
        ffiC.C_DefineQuote(qnum, quotestr)
1333
        ffiC.C_DefineQuote(qnum, quotestr)
1297
    end
1334
    end
1298
1335
1299
    g_data.quote[qnum] = quotestr
1336
    g_data.quote[qnum] = quotestr
1300
    return ""
1337
    return ""
1301
end
1338
end
1302
1339
1303
local PROJ = {}
1340
local PROJ = {}
1304
for key, val in pairs(conl.PROJ) do
1341
for key, val in pairs(conl.PROJ) do
1305
    -- Strip "PROJ_"
1342
    -- Strip "PROJ_"
1306
    PROJ[key:sub(6)] = val
1343
    PROJ[key:sub(6)] = val
1307
end
1344
end
1308
1345
1309
function Cmd.defineprojectile(tilenum, what, val)
1346
function Cmd.defineprojectile(tilenum, what, val)
1310
    local ok = check.tile_idx(tilenum)
1347
    local ok = check.tile_idx(tilenum)
1311
1348
1312
    if (what==PROJ.WORKSLIKE) then
1349
    if (what==PROJ.WORKSLIKE) then
1313
        check.reserved_bits(val, 2^21-1, "for PROJ_WORKSLIKE")
1350
        check.reserved_bits(val, 2^21-1, "for PROJ_WORKSLIKE")
1314
    elseif (what==PROJ.SOUND or what==PROJ.ISOUND or what==PROJ.BSOUND) then
1351
    elseif (what==PROJ.SOUND or what==PROJ.ISOUND or what==PROJ.BSOUND) then
1315
        ok = ok and (val==-1 or check.sound_idx(val))
1352
        ok = ok and (val==-1 or check.sound_idx(val))
1316
    elseif (what==PROJ.SPAWNS or what==PROJ.DECAL or what==PROJ.TRAIL) then
1353
    elseif (what==PROJ.SPAWNS or what==PROJ.DECAL or what==PROJ.TRAIL) then
1317
        ok = ok and (val==-1 or check.tile_idx(val))
1354
        ok = ok and (val==-1 or check.tile_idx(val))
1318
    end
1355
    end
1319
1356
1320
    if (ffi and ok) then
1357
    if (ffi and ok) then
1321
        ffiC.C_DefineProjectile(tilenum, what, val)
1358
        ffiC.C_DefineProjectile(tilenum, what, val)
1322
    end
1359
    end
1323
end
1360
end
1324
1361
1325
-- <override>: override-set flags? The default is to bitwise OR with existing.
1362
-- <override>: override-set flags? The default is to bitwise OR with existing.
1326
function Cmd.xspriteflags(tilenum, flags, override)
1363
function Cmd.xspriteflags(tilenum, flags, override)
1327
    local ok = check.tile_idx(tilenum)
1364
    local ok = check.tile_idx(tilenum)
1328
    check.reserved_bits(flags, conl.user_sflags, "for sprite flags")
1365
    check.reserved_bits(flags, conl.user_sflags, "for sprite flags")
1329
1366
1330
    local loc = g_code.aflagsloc[tilenum]
1367
    local loc = g_code.aflagsloc[tilenum]
1331
1368
1332
    if (override and loc ~= nil) then
1369
    if (override and loc ~= nil) then
1333
        warnprintf("'spriteflags' after %s %d", loc[4], tilenum)
1370
        warnprintf("'spriteflags' after %s %d", loc[4], tilenum)
1334
        contprintf(false, "at %s %d:%d", loc[1], loc[2], loc[3])
1371
        contprintf(false, "at %s %d:%d", loc[1], loc[2], loc[3])
1335
    end
1372
    end
1336
1373
1337
    -- Mark the last 'spriteflags' or 'sprite*' directive for the given actor.
1374
    -- Mark the last 'spriteflags' or 'sprite*' directive for the given actor.
1338
    g_code.aflagsloc[tilenum] = getLocation(format("'%s' for actor", g_lastkw), pos)
1375
    g_code.aflagsloc[tilenum] = getLocation(format("'%s' for actor", g_lastkw), pos)
1339
1376
1340
    if (ffi and ok) then
1377
    if (ffi and ok) then
1341
        local tile = ffiC.g_tile[tilenum]
1378
        local tile = ffiC.g_tile[tilenum]
1342
        tile._flags = bit.bor(override and 0 or tile._flags, flags)
1379
        tile._flags = bit.bor(override and 0 or tile._flags, flags)
1343
    end
1380
    end
1344
end
1381
end
1345
1382
1346
function Cmd.precache(tilenum0, tilenum1, flagnum)
1383
function Cmd.precache(tilenum0, tilenum1, flagnum)
1347
    local ok = check.tile_idx(tilenum0) and check.tile_idx(tilenum1)
1384
    local ok = check.tile_idx(tilenum0) and check.tile_idx(tilenum1)
1348
1385
1349
    if (ffi and ok) then
1386
    if (ffi and ok) then
1350
        local tile = ffiC.g_tile[tilenum0]
1387
        local tile = ffiC.g_tile[tilenum0]
1351
        tile._cacherange = tilenum1;
1388
        tile._cacherange = tilenum1;
1352
        if (flagnum) then
1389
        if (flagnum) then
1353
            tile._flags = bit.bor(tile._flags, conl.SFLAG.SFLAG_CACHE)
1390
            tile._flags = bit.bor(tile._flags, conl.SFLAG.SFLAG_CACHE)
1354
        end
1391
        end
1355
    end
1392
    end
1356
end
1393
end
1357
1394
1358
function Cmd.cheatkeys(sc1, sc2)
1395
function Cmd.cheatkeys(sc1, sc2)
1359
    if (ffi) then
1396
    if (ffi) then
1360
        ffiC.CheatKeys[0] = sc1
1397
        ffiC.CheatKeys[0] = sc1
1361
        ffiC.CheatKeys[1] = sc2
1398
        ffiC.CheatKeys[1] = sc2
1362
    end
1399
    end
1363
end
1400
end
1364
1401
1365
function Cmd.setdefname(filename)
1402
function Cmd.setdefname(filename)
1366
    assert(type(filename)=="string")
1403
    assert(type(filename)=="string")
1367
    if (ffi) then
1404
    if (ffi) then
1368
        if (ffiC.C_SetDefName(filename) ~= 0) then
1405
        if (ffiC.C_SetDefName(filename) ~= 0) then
1369
            error("OUT OF MEMORY", 0)
1406
            error("OUT OF MEMORY", 0)
1370
        end
1407
        end
1371
    end
1408
    end
1372
end
1409
end
1373
1410
1374
function Cmd.setcfgname(filename)
1411
function Cmd.setcfgname(filename)
1375
    assert(type(filename)=="string")
1412
    assert(type(filename)=="string")
1376
    if (ffi) then
1413
    if (ffi) then
1377
        ffiC.C_SetCfgName(filename)
1414
        ffiC.C_SetCfgName(filename)
1378
    end
1415
    end
1379
end
1416
end
1380
1417
1381
function Cmd.gamestartup(...)
1418
function Cmd.gamestartup(...)
1382
    local args = {...}
1419
    local args = {...}
1383
1420
1384
    -- TODO: PRE13: detection of other g_scriptVersion.
1421
    -- TODO: PRE13: detection of other g_scriptVersion.
1385
    if (#args ~= 26 and #args ~= 30) then
1422
    if (#args ~= 26 and #args ~= 30) then
1386
        errprintf("must pass either 26 (1.3D) or 30 (1.5) values")
1423
        errprintf("must pass either 26 (1.3D) or 30 (1.5) values")
1387
        return
1424
        return
1388
    end
1425
    end
1389
1426
1390
    if (ffi) then
1427
    if (ffi) then
1391
        -- running from EDuke32
1428
        -- running from EDuke32
1392
        if (#args == 30) then
1429
        if (#args == 30) then
1393
            ffiC.g_scriptVersion = 14
1430
            ffiC.g_scriptVersion = 14
1394
        end
1431
        end
1395
        local params = ffi.new("int32_t [30]", args)
1432
        local params = ffi.new("int32_t [30]", args)
1396
        ffiC.G_DoGameStartup(params)
1433
        ffiC.G_DoGameStartup(params)
1397
    end
1434
    end
1398
1435
1399
    g_data.startup = args  -- TODO: sanity-check them
1436
    g_data.startup = args  -- TODO: sanity-check them
1400
end
1437
end
1401
1438
1402
function Cmd.definesound(sndlabel, fn, ...)
1439
function Cmd.definesound(sndlabel, fn, ...)
1403
    local sndnum
1440
    local sndnum
1404
1441
1405
    if (type(sndlabel)=="string") then
1442
    if (type(sndlabel)=="string") then
1406
        -- HANDLE_RAWDEFINE
1443
        -- HANDLE_RAWDEFINE
1407
        local pos, minus, label = sndlabel:match("(.-):(.-):(.+)")
1444
        local pos, minus, label = sndlabel:match("(.-):(.-):(.+)")
1408
        sndnum = lookup.defined_label(tonumber(pos), minus, label)
1445
        sndnum = lookup.defined_label(tonumber(pos), minus, label)
1409
1446
1410
        if (ffi and g_dynsoundi and (sndnum>=0 and sndnum<conl.MAXSOUNDS)) then
1447
        if (ffi and g_dynsoundi and (sndnum>=0 and sndnum<conl.MAXSOUNDS)) then
1411
            dynmap.maybe_init(g_dynsoundi, ffiC.g_dynSoundList)
1448
            dynmap.maybe_init(g_dynsoundi, ffiC.g_dynSoundList)
1412
            dynmap.maybe_process(g_dynsoundi, ffiC.g_dynSoundList, label, sndnum)
1449
            dynmap.maybe_process(g_dynsoundi, ffiC.g_dynSoundList, label, sndnum)
1413
        end
1450
        end
1414
    else
1451
    else
1415
        assert(type(sndlabel)=="number")
1452
        assert(type(sndlabel)=="number")
1416
        sndnum = sndlabel
1453
        sndnum = sndlabel
1417
    end
1454
    end
1418
1455
1419
    if (not (sndnum >= 0 and sndnum < conl.MAXSOUNDS)) then
1456
    if (not (sndnum >= 0 and sndnum < conl.MAXSOUNDS)) then
1420
        errprintf("sound number is negative or exceeds sound limit of %d", conl.MAXSOUNDS-1)
1457
        errprintf("sound number is negative or exceeds sound limit of %d", conl.MAXSOUNDS-1)
1421
        return
1458
        return
1422
    end
1459
    end
1423
1460
1424
    local params = {...}  -- TODO: sanity-check them some more
1461
    local params = {...}  -- TODO: sanity-check them some more
1425
    check.reserved_bits(params[4], 31+128, "for sound flags")
1462
    check.reserved_bits(params[4], 31+128, "for sound flags")
1426
1463
1427
    if (ffi) then
1464
    if (ffi) then
1428
        local cparams = ffi.new("int32_t [5]", params)
1465
        local cparams = ffi.new("int32_t [5]", params)
1429
        assert(type(fn)=="string")
1466
        assert(type(fn)=="string")
1430
        ffiC.C_DefineSound(sndnum, fn, cparams)
1467
        ffiC.C_DefineSound(sndnum, fn, cparams)
1431
    end
1468
    end
1432
1469
1433
    g_data.sound[sndnum] = { fn=fn, params=params }
1470
    g_data.sound[sndnum] = { fn=fn, params=params }
1434
end
1471
end
1435
1472
1436
function Cmd.music(volnum, ...)
1473
function Cmd.music(volnum, ...)
1437
    if (not (volnum >= 0 and volnum <= conl.MAXVOLUMES+1)) then
1474
    if (not (volnum >= 0 and volnum <= conl.MAXVOLUMES+1)) then
1438
        -- The passed volume number is 1-based.
1475
        -- The passed volume number is 1-based.
1439
        -- Both 0 and MAXVOLUMES+1 means "special music"
1476
        -- Both 0 and MAXVOLUMES+1 means "special music"
1440
        errprintf("volume number must be between 0 and MAXVOLUMES+1=%d", conl.MAXVOLUMES+1)
1477
        errprintf("volume number must be between 0 and MAXVOLUMES+1=%d", conl.MAXVOLUMES+1)
1441
        return
1478
        return
1442
    elseif (volnum == conl.MAXVOLUMES+1) then
1479
    elseif (volnum == conl.MAXVOLUMES+1) then
1443
        warnprintf("volume number MAXVOLUMES+1 is discouraged, use 0 instead")
1480
        warnprintf("volume number MAXVOLUMES+1 is discouraged, use 0 instead")
1444
    end
1481
    end
1445
1482
1446
    if (volnum == 0) then
1483
    if (volnum == 0) then
1447
        volnum = conl.MAXVOLUMES+1  -- special music
1484
        volnum = conl.MAXVOLUMES+1  -- special music
1448
    end
1485
    end
1449
1486
1450
    local filenames = {...}
1487
    local filenames = {...}
1451
    local MAXFNS = conl.MAXLEVELS
1488
    local MAXFNS = conl.MAXLEVELS
1452
1489
1453
    if (#filenames > MAXFNS) then
1490
    if (#filenames > MAXFNS) then
1454
        warnprintf("ignoring extraneous %d music file names", #filenames-MAXFNS)
1491
        warnprintf("ignoring extraneous %d music file names", #filenames-MAXFNS)
1455
        for i=MAXFNS+1,#filenames do
1492
        for i=MAXFNS+1,#filenames do
1456
            filenames[i] = nil
1493
            filenames[i] = nil
1457
        end
1494
        end
1458
    end
1495
    end
1459
1496
1460
    if (ffi) then
1497
    if (ffi) then
1461
        for i=1,#filenames do
1498
        for i=1,#filenames do
1462
            assert(type(filenames[i])=="string")
1499
            assert(type(filenames[i])=="string")
1463
            ffiC.C_DefineMusic(volnum-1, i-1, "/"..filenames[i])
1500
            ffiC.C_DefineMusic(volnum-1, i-1, "/"..filenames[i])
1464
        end
1501
        end
1465
    end
1502
    end
1466
1503
1467
    g_data.music[volnum] = filenames
1504
    g_data.music[volnum] = filenames
1468
end
1505
end
1469
1506
1470
1507
1471
--- GAMEVARS / GAMEARRAYS
1508
--- GAMEVARS / GAMEARRAYS
1472
1509
1473
function Cmd.gamearray(identifier, initsize)
1510
function Cmd.gamearray(identifier, initsize)
1474
    if (check.sysvar_def_attempt(identifier)) then
1511
    if (check.sysvar_def_attempt(identifier)) then
1475
        return
1512
        return
1476
    end
1513
    end
1477
1514
1478
    if (not (initsize >= 0 and initsize < 0x7fffffff)) then
1515
    if (not (initsize >= 0 and initsize < 0x7fffffff)) then
1479
        errprintf("invalid initial size %d for gamearray `%s'", initsize, identifier)
1516
        errprintf("invalid initial size %d for gamearray `%s'", initsize, identifier)
1480
        return
1517
        return
1481
    end
1518
    end
1482
1519
1483
    local oga = g_gamearray[identifier]
1520
    local oga = g_gamearray[identifier]
1484
    if (oga) then
1521
    if (oga) then
1485
        if (oga.sysp) then
1522
        if (oga.sysp) then
1486
            errprintf("attempt to define system gamearray `%s'", identifier)
1523
            errprintf("attempt to define system gamearray `%s'", identifier)
1487
            return
1524
            return
1488
        elseif (initsize ~= oga.size) then
1525
        elseif (initsize ~= oga.size) then
1489
            errprintf("duplicate gamearray definition `%s' has different size", identifier)
1526
            errprintf("duplicate gamearray definition `%s' has different size", identifier)
1490
            return
1527
            return
1491
        else
1528
        else
1492
            warnprintf("duplicate gamearray definition `%s' ignored", identifier)
1529
            warnprintf("duplicate gamearray definition `%s' ignored", identifier)
1493
            return
1530
            return
1494
        end
1531
        end
1495
    end
1532
    end
1496
1533
1497
    if (g_gamevar[identifier]) then
1534
    if (g_gamevar[identifier]) then
1498
        warnprintf("symbol `%s' already used for game variable", identifier)
1535
        warnprintf("symbol `%s' already used for game variable", identifier)
1499
        inform.oldgv_location(identifier, false)
1536
        inform.oldgv_location(identifier, false)
1500
    end
1537
    end
1501
1538
1502
    local ga = { name=mangle_name(identifier, "A"), size=initsize }
1539
    local ga = { name=mangle_name(identifier, "A"), size=initsize }
1503
    g_gamearray[identifier] = ga
1540
    g_gamearray[identifier] = ga
1504
1541
1505
    addcode("if _S then")
1542
    addcode("if _S then")
1506
    addcodef("%s=_con._gamearray(%d)", ga.name, initsize)
1543
    addcodef("%s=_con._gamearray(%d)", ga.name, initsize)
1507
    addcode("end")
1544
    addcode("end")
1508
end
1545
end
1509
1546
1510
function Cmd.gamevar(identifier, initval, flags)
1547
function Cmd.gamevar(identifier, initval, flags)
1511
    if (check.sysvar_def_attempt(identifier)) then
1548
    if (check.sysvar_def_attempt(identifier)) then
1512
        return
1549
        return
1513
    end
1550
    end
1514
1551
1515
    if (bit.band(flags, bit.bnot(GVFLAG.USER_MASK)) ~= 0) then
1552
    if (bit.band(flags, bit.bnot(GVFLAG.USER_MASK)) ~= 0) then
1516
        -- TODO: a couple of the presumably safe ones
1553
        -- TODO: a couple of the presumably safe ones
1517
        errprintf("gamevar flags other than 1, 2, 1024 or 131072: NYI or forbidden")
1554
        errprintf("gamevar flags other than 1, 2, 1024 or 131072: NYI or forbidden")
1518
        return
1555
        return
1519
    end
1556
    end
1520
1557
1521
    local perPlayer = (bit.band(flags, GVFLAG.PERPLAYER) ~= 0)
1558
    local perPlayer = (bit.band(flags, GVFLAG.PERPLAYER) ~= 0)
1522
    local perActor = (bit.band(flags, GVFLAG.PERACTOR) ~= 0)
1559
    local perActor = (bit.band(flags, GVFLAG.PERACTOR) ~= 0)
1523
1560
1524
    if (perPlayer and perActor) then
1561
    if (perPlayer and perActor) then
1525
        errprintf("invalid gamevar flags: must be either PERPLAYER or PERACTOR, not both")
1562
        errprintf("invalid gamevar flags: must be either PERPLAYER or PERACTOR, not both")
1526
        return
1563
        return
1527
    end
1564
    end
1528
1565
1529
    local ogv = g_gamevar[identifier]
1566
    local ogv = g_gamevar[identifier]
1530
    -- handle NORESET or NODEFAULT
1567
    -- handle NORESET or NODEFAULT
1531
    local isSessionVar = (bit.band(flags, GVFLAG.NODEFAULT) ~= 0)
1568
    local isSessionVar = (bit.band(flags, GVFLAG.NODEFAULT) ~= 0)
1532
    local storeWithSavegames = (bit.band(flags, GVFLAG.NORESET) == 0)
1569
    local storeWithSavegames = (bit.band(flags, GVFLAG.NORESET) == 0)
1533
1570
1534
    if (isSessionVar and (perPlayer or perActor)) then
1571
    if (isSessionVar and (perPlayer or perActor)) then
1535
        if (ogv == nil) then  -- warn only once per gamevar
1572
        if (ogv == nil) then  -- warn only once per gamevar
1536
            warnprintf("per-%s session gamevar `%s': NYI, made %s",
1573
            warnprintf("per-%s session gamevar `%s': NYI, made %s",
1537
                       perPlayer and "player" or "actor",
1574
                       perPlayer and "player" or "actor",
1538
                       identifier,
1575
                       identifier,
1539
                       perPlayer and "global" or "non-session")
1576
                       perPlayer and "global" or "non-session")
1540
        end
1577
        end
1541
1578
1542
        if (perActor) then
1579
        if (perActor) then
1543
            flags = bit.band(flags, bit.bnot(GVFLAG.NODEFAULT))
1580
            flags = bit.band(flags, bit.bnot(GVFLAG.NODEFAULT))
1544
            isSessionVar = false
1581
            isSessionVar = false
1545
        elseif (perPlayer) then
1582
        elseif (perPlayer) then
1546
            flags = bit.band(flags, bit.bnot(GVFLAG.PERPLAYER))
1583
            flags = bit.band(flags, bit.bnot(GVFLAG.PERPLAYER))
1547
            perPlayer = false
1584
            perPlayer = false
1548
        end
1585
        end
1549
    end
1586
    end
1550
1587
1551
    if (ogv ~= nil) then
1588
    if (ogv ~= nil) then
1552
        local oflags = bit.band(ogv.flags, bit.bnot(GVFLAG.CON_PERPLAYER))
1589
        local oflags = bit.band(ogv.flags, bit.bnot(GVFLAG.CON_PERPLAYER))
1553
1590
1554
        if (oflags ~= flags) then
1591
        if (oflags ~= flags) then
1555
            if (bit.band(oflags, GVFLAG.SYSTEM) ~= 0 and not isSessionVar) then
1592
            if (bit.band(oflags, GVFLAG.SYSTEM) ~= 0 and not isSessionVar) then
1556
                -- Attempt to override a system gamevar. See if it's read-only...
1593
                -- Attempt to override a system gamevar. See if it's read-only...
1557
                if (bit.band(oflags, GVFLAG.READONLY) ~= 0) then
1594
                if (bit.band(oflags, GVFLAG.READONLY) ~= 0) then
1558
                    errprintf("attempt to override read-only system gamevar `%s'", identifier)
1595
                    errprintf("attempt to override read-only system gamevar `%s'", identifier)
1559
                    return
1596
                    return
1560
                end
1597
                end
1561
1598
1562
                local flagsnosys = bit.band(oflags, bit.bnot(GVFLAG.SYSTEM))
1599
                local flagsnosys = bit.band(oflags, bit.bnot(GVFLAG.SYSTEM))
1563
                if (flagsnosys ~= flags and g_warn["system-gamevar"]) then
1600
                if (flagsnosys ~= flags and g_warn["system-gamevar"]) then
1564
                    warnprintf("overrode initial value of `%s', but kept "..
1601
                    warnprintf("overrode initial value of `%s', but kept "..
1565
                               "flags (%d)", identifier, flagsnosys)
1602
                               "flags (%d)", identifier, flagsnosys)
1566
                end
1603
                end
1567
1604
1568
                if (ogv.rbits and bit.band(ogv.rbits, initval)~=0) then
1605
                if (ogv.rbits and bit.band(ogv.rbits, initval)~=0) then
1569
                    warnprintf("set one or more reserved bits (0x%s) in overriding `%s'",
1606
                    warnprintf("set one or more reserved bits (0x%s) in overriding `%s'",
1570
                               bit.tohex(bit.band(ogv.rbits, initval)), identifier)
1607
                               bit.tohex(bit.band(ogv.rbits, initval)), identifier)
1571
                end
1608
                end
1572
1609
1573
                local linestr = "--"..getlinecol(g_lastkwpos)
1610
                local linestr = "--"..getlinecol(g_lastkwpos)
1574
1611
1575
                -- Emit code to set the variable at Lua parse time.
1612
                -- Emit code to set the variable at Lua parse time.
1576
                -- XXX: How does this interact with savegame restoration?
1613
                -- XXX: How does this interact with savegame restoration?
1577
                if (bit.band(oflags, GVFLAG.PERPLAYER) ~= 0) then
1614
                if (bit.band(oflags, GVFLAG.PERPLAYER) ~= 0) then
1578
                    -- Replace player index by 0. PLAYER_0.
1615
                    -- Replace player index by 0. PLAYER_0.
1579
                    -- TODO_MP: init for all players.
1616
                    -- TODO_MP: init for all players.
1580
                    local pvar, numrepls = ogv.name:gsub("_pli", "0")
1617
                    local pvar, numrepls = ogv.name:gsub("_pli", "0")
1581
                    assert(numrepls>=1)
1618
                    assert(numrepls>=1)
1582
                    addcodef("%s=%d%s", pvar, initval, linestr)
1619
                    addcodef("%s=%d%s", pvar, initval, linestr)
1583
                else
1620
                else
1584
                    addcodef("%s=%d%s", ogv.name, initval, linestr)
1621
                    addcodef("%s=%d%s", ogv.name, initval, linestr)
1585
                end
1622
                end
1586
                return
1623
                return
1587
            end
1624
            end
1588
1625
1589
            errprintf("duplicate definition of gamevar `%s' has different flags (new: %x, old: %x)", identifier, flags, oflags)
1626
            errprintf("duplicate definition of gamevar `%s' has different flags (new: %x, old: %x)", identifier, flags, oflags)
1590
            inform.oldgv_location(identifier, true)
1627
            inform.oldgv_location(identifier, true)
1591
            return
1628
            return
1592
        else
1629
        else
1593
            warnprintf("duplicate definition of gamevar `%s' ignored", identifier)
1630
            warnprintf("duplicate definition of gamevar `%s' ignored", identifier)
1594
            inform.oldgv_location(identifier, false)
1631
            inform.oldgv_location(identifier, false)
1595
            return
1632
            return
1596
        end
1633
        end
1597
    end
1634
    end
1598
1635
1599
    local ltype = g_labeltype[identifier]
1636
    local ltype = g_labeltype[identifier]
1600
    if (ltype ~= nil) then
1637
    if (ltype ~= nil) then
1601
        warnprintf("Symbol `%s' already used for a defined %s.", identifier, LABEL[ltype])
1638
        warnprintf("Symbol `%s' already used for a defined %s.", identifier, LABEL[ltype])
1602
        inform.olddef_location(identifier, false)
1639
        inform.olddef_location(identifier, false)
1603
    end
1640
    end
1604
1641
1605
    if (isSessionVar) then
1642
    if (isSessionVar) then
1606
        if (g_numSessionVars == conl.MAXSESSIONVARS) then
1643
        if (g_numSessionVars == conl.MAXSESSIONVARS) then
1607
            errprintf("Declared too many session gamevars (flag 1024), can have at most %d.",
1644
            errprintf("Declared too many session gamevars (flag 1024), can have at most %d.",
1608
                      conl.MAXSESSIONVARS)
1645
                      conl.MAXSESSIONVARS)
1609
            return
1646
            return
1610
        end
1647
        end
1611
1648
1612
        -- Declare new session gamevar.
1649
        -- Declare new session gamevar.
1613
        local gv = { name=format("_gv._sessionVar[%d]", g_numSessionVars),
1650
        local gv = { name=format("_gv._sessionVar[%d]", g_numSessionVars),
1614
                     flags=flags, loc=getLocation(), used=0 }
1651
                     flags=flags, loc=getLocation(), used=0 }
1615
        g_numSessionVars = g_numSessionVars+1
1652
        g_numSessionVars = g_numSessionVars+1
1616
1653
1617
        g_gamevar[identifier] = gv;
1654
        g_gamevar[identifier] = gv;
1618
        -- Initialize it (i.e. set to the declared initial value) on first run,
1655
        -- Initialize it (i.e. set to the declared initial value) on first run,
1619
        -- but not from savegames.
1656
        -- but not from savegames.
1620
        addcodef("if _S then %s=%d end", gv.name, initval)
1657
        addcodef("if _S then %s=%d end", gv.name, initval)
1621
1658
1622
        return
1659
        return
1623
    end
1660
    end
1624
1661
1625
    local gv = { name=mangle_name(identifier, "V"), flags=flags, loc=getLocation(), used=0 }
1662
    local gv = { name=mangle_name(identifier, "V"), flags=flags, loc=getLocation(), used=0 }
1626
    g_gamevar[identifier] = gv
1663
    g_gamevar[identifier] = gv
1627
1664
1628
    if (storeWithSavegames) then
1665
    if (storeWithSavegames) then
1629
        addcode("if _S then")
1666
        addcode("if _S then")
1630
    end
1667
    end
1631
1668
1632
    if (perActor) then
1669
    if (perActor) then
1633
        addcodef("%s=_con.actorvar(%d)", gv.name, initval)
1670
        addcodef("%s=_con.actorvar(%d)", gv.name, initval)
1634
    elseif (perPlayer and g_cgopt["playervar"]) then
1671
    elseif (perPlayer and g_cgopt["playervar"]) then
1635
        gv.flags = bit.bor(gv.flags, GVFLAG.CON_PERPLAYER)
1672
        gv.flags = bit.bor(gv.flags, GVFLAG.CON_PERPLAYER)
1636
        addcodef("%s=_con.playervar(%d)", gv.name, initval)
1673
        addcodef("%s=_con.playervar(%d)", gv.name, initval)
1637
    else
1674
    else
1638
        addcodef("%s=%d", gv.name, initval)
1675
        addcodef("%s=%d", gv.name, initval)
1639
    end
1676
    end
1640
1677
1641
    if (storeWithSavegames) then
1678
    if (storeWithSavegames) then
1642
        addcode("end")
1679
        addcode("end")
1643
    end
1680
    end
1644
end
1681
end
1645
1682
1646
function Cmd.dynamicremap()
1683
function Cmd.dynamicremap()
1647
    if (g_dyntilei==nil) then
1684
    if (g_dyntilei==nil) then
1648
        print("Using dynamic tile remapping");
1685
        print("Using dynamic tile remapping");
1649
        g_dyntilei = {};
1686
        g_dyntilei = {};
1650
    end
1687
    end
1651
end
1688
end
1652
1689
1653
function Cmd.dynamicsoundremap()
1690
function Cmd.dynamicsoundremap()
1654
    if (g_dynsoundi==nil) then
1691
    if (g_dynsoundi==nil) then
1655
        print("Using dynamic sound remapping");
1692
        print("Using dynamic sound remapping");
1656
        g_dynsoundi = {};
1693
        g_dynsoundi = {};
1657
    end
1694
    end
1658
end
1695
end
1659
1696
1660
function lookup.gamearray(identifier)
1697
function lookup.gamearray(identifier)
1661
    local ga = g_gamearray[identifier]
1698
    local ga = g_gamearray[identifier]
1662
    if (ga == nil) then
1699
    if (ga == nil) then
1663
        errprintf("symbol `%s' is not a game array", identifier)
1700
        errprintf("symbol `%s' is not a game array", identifier)
1664
        return "_INVALIDGA"
1701
        return "_INVALIDGA"
1665
    end
1702
    end
1666
    return ga.name
1703
    return ga.name
1667
end
1704
end
1668
1705
1669
local function thisactor_to_pli(var)
1706
local function thisactor_to_pli(var)
1670
    return (var=="_aci") and "_pli" or var
1707
    return (var=="_aci") and "_pli" or var
1671
end
1708
end
1672
1709
1673
function lookup.error_not_gamevar(identifier)
1710
function lookup.error_not_gamevar(identifier)
1674
    errprintf("symbol `%s' is not a game variable", identifier)
1711
    errprintf("symbol `%s' is not a game variable", identifier)
1675
    return "_INVALIDGV"
1712
    return "_INVALIDGV"
1676
end
1713
end
1677
1714
1678
-- <aorpvar>: code for actor or player index
1715
-- <aorpvar>: code for actor or player index
1679
function lookup.gamevar(identifier, aorpvar, writable)
1716
function lookup.gamevar(identifier, aorpvar, writable)
1680
    local gv = g_gamevar[identifier]
1717
    local gv = g_gamevar[identifier]
1681
1718
1682
    if (gv == nil) then
1719
    if (gv == nil) then
1683
        return lookup.error_not_gamevar(identifier)
1720
        return lookup.error_not_gamevar(identifier)
1684
    end
1721
    end
1685
1722
1686
    if (writable and bit.band(gv.flags, GVFLAG.READONLY) ~= 0) then
1723
    if (writable and bit.band(gv.flags, GVFLAG.READONLY) ~= 0) then
1687
        errprintf("gamevar `%s' is read-only", identifier)
1724
        errprintf("gamevar `%s' is read-only", identifier)
1688
        return "_READONLYGV"
1725
        return "_READONLYGV"
1689
    end
1726
    end
1690
1727
1691
    gv.used = bit.bor(gv.used, writable and 2 or 1)
1728
    gv.used = bit.bor(gv.used, writable and 2 or 1)
1692
1729
1693
    if (bit.band(gv.flags, GVFLAG.PERACTOR)~=0) then
1730
    if (bit.band(gv.flags, GVFLAG.PERACTOR)~=0) then
1694
        return format("%s[%s]", gv.name, aorpvar)
1731
        return format("%s[%s]", gv.name, aorpvar)
1695
    elseif (bit.band(gv.flags, GVFLAG.CON_PERPLAYER)~=0 and g_cgopt["playervar"]) then
1732
    elseif (bit.band(gv.flags, GVFLAG.CON_PERPLAYER)~=0 and g_cgopt["playervar"]) then
1696
        return format("%s[%s]", gv.name, thisactor_to_pli(aorpvar))
1733
        return format("%s[%s]", gv.name, thisactor_to_pli(aorpvar))
1697
    else
1734
    else
1698
        return gv.name
1735
        return gv.name
1699
    end
1736
    end
1700
end
1737
end
1701
1738
1702
local function maybe_gamevar_Cmt(subj, pos, identifier)
1739
local function maybe_gamevar_Cmt(subj, pos, identifier)
1703
    if (g_gamevar[identifier]) then
1740
    if (g_gamevar[identifier]) then
1704
        return true, lookup.gamevar(identifier, "_aci", false)
1741
        return true, lookup.gamevar(identifier, "_aci", false)
1705
    end
1742
    end
1706
end
1743
end
1707
1744
1708
1745
1709
----==== patterns ====----
1746
----==== patterns ====----
1710
1747
1711
---- basic ones
1748
---- basic ones
1712
-- Windows, *nix and Mac newlines all exist in the wild!
1749
-- Windows, *nix and Mac newlines all exist in the wild!
1713
local newline = "\r"*Pat("\n")^-1 + "\n"
1750
local newline = "\r"*Pat("\n")^-1 + "\n"
1714
local EOF = Pat(-1)
1751
local EOF = Pat(-1)
1715
local anychar = Pat(1)
1752
local anychar = Pat(1)
1716
-- comments
1753
-- comments
1717
local comment = "/*" * match_until(anychar, "*/") * "*/"
1754
local comment = "/*" * match_until(anychar, "*/") * "*/"
1718
local linecomment = "//" * match_until(anychar, newline)
1755
local linecomment = "//" * match_until(anychar, newline)
1719
local whitespace = Var("whitespace")
1756
local whitespace = Var("whitespace")
1720
local sp0 = whitespace^0
1757
local sp0 = whitespace^0
1721
-- This "WS+" pattern matches EOF too, so that a forgotten newline at EOF is
1758
-- This "WS+" pattern matches EOF too, so that a forgotten newline at EOF is
1722
-- properly handled
1759
-- properly handled
1723
local sp1 = whitespace^1 + EOF
1760
local sp1 = whitespace^1 + EOF
1724
local alpha = Range("AZ", "az")  -- locale?
1761
local alpha = Range("AZ", "az")  -- locale?
1725
local alphanum = alpha + Range("09")
1762
local alphanum = alpha + Range("09")
1726
--local alnumtok = alphanum + Set("{}/\\*-_.")  -- see isaltok() in gamedef.c
1763
--local alnumtok = alphanum + Set("{}/\\*-_.")  -- see isaltok() in gamedef.c
1727
1764
1728
--- Basic lexical elements ("tokens"). See the final grammar ("Grammar") for
1765
--- Basic lexical elements ("tokens"). See the final grammar ("Grammar") for
1729
--- their definitions.
1766
--- their definitions.
1730
local tok =
1767
local tok =
1731
{
1768
{
1732
    maybe_minus = (Pat("-") * sp0)^-1,
1769
    maybe_minus = (Pat("-") * sp0)^-1,
1733
    number = Var("t_number"),
1770
    number = Var("t_number"),
1734
1771
1735
    -- Valid identifier names are disjoint from keywords!
1772
    -- Valid identifier names are disjoint from keywords!
1736
    -- XXX: CON is more permissive with identifier name characters:
1773
    -- XXX: CON is more permissive with identifier name characters:
1737
    identifier = Var("t_identifier"),
1774
    identifier = Var("t_identifier"),
1738
    -- This one matches keywords, too:
1775
    -- This one matches keywords, too:
1739
    identifier_all = Var("t_identifier_all"),
1776
    identifier_all = Var("t_identifier_all"),
1740
1777
1741
    define = Var("t_define"),
1778
    define = Var("t_define"),
1742
    rawdefine = Var("t_rawdefine"),
1779
    rawdefine = Var("t_rawdefine"),
1743
    actordefine = g_cgopt["names"] and Var("t_rawdefine") or Var("t_define"),
1780
    actordefine = g_cgopt["names"] and Var("t_rawdefine") or Var("t_define"),
1744
1781
1745
    move = Var("t_move"),
1782
    move = Var("t_move"),
1746
    ai = Var("t_ai"),
1783
    ai = Var("t_ai"),
1747
    action = Var("t_action"),
1784
    action = Var("t_action"),
1748
1785
1749
    -- NOTE: no chance to whitespace and double quotes in filenames:
1786
    -- NOTE: no chance to whitespace and double quotes in filenames:
1750
    filename = lpeg.C((anychar-Set(" \t\r\n\""))^1),
1787
    filename = lpeg.C((anychar-Set(" \t\r\n\""))^1),
1751
    newline_term_str = match_until(anychar, newline),
1788
    newline_term_str = match_until(anychar, newline),
1752
1789
1753
    rvar = Var("t_rvar"),
1790
    rvar = Var("t_rvar"),
1754
    wvar = Var("t_wvar"),
1791
    wvar = Var("t_wvar"),