Subversion Repositories eduke32

Rev

Rev 6224 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 6224 Rev 6569
1
-- INTERNAL
1
-- INTERNAL
2
-- definitions of BUILD and game types for the Lunatic Interpreter
2
-- definitions of BUILD and game types for the Lunatic Interpreter
3
3
4
local require = require
4
local require = require
5
local ffi = require("ffi")
5
local ffi = require("ffi")
6
local ffiC = ffi.C
6
local ffiC = ffi.C
7
7
8
-- Lua C API functions.
8
-- Lua C API functions.
9
local CF = CF
9
local CF = CF
10
10
11
local bit = bit
11
local bit = bit
12
local coroutine = coroutine
12
local coroutine = coroutine
13
local string = string
13
local string = string
14
local table = table
14
local table = table
15
local math = math
15
local math = math
16
16
17
local assert = assert
17
local assert = assert
18
local error = error
18
local error = error
19
local getfenv = getfenv
19
local getfenv = getfenv
20
local getmetatable = getmetatable
20
local getmetatable = getmetatable
21
local ipairs = ipairs
21
local ipairs = ipairs
22
local loadstring = loadstring
22
local loadstring = loadstring
23
local pairs = pairs
23
local pairs = pairs
24
local pcall = pcall
24
local pcall = pcall
25
local rawget = rawget
25
local rawget = rawget
26
local rawset = rawset
26
local rawset = rawset
27
local select = select
27
local select = select
28
local setmetatable = setmetatable
28
local setmetatable = setmetatable
29
local setfenv = setfenv
29
local setfenv = setfenv
30
local tonumber = tonumber
30
local tonumber = tonumber
31
local tostring = tostring
31
local tostring = tostring
32
local type = type
32
local type = type
33
33
34
-- Create a new module for passing stuff to other modules.
34
-- Create a new module for passing stuff to other modules.
35
local lprivate = {}
35
local lprivate = {}
36
require("package").loaded.lprivate = lprivate
36
require("package").loaded.lprivate = lprivate
37
37
38
require("jit.opt").start("maxmcode=10240")  -- in KiB
38
require("jit.opt").start("maxmcode=10240")  -- in KiB
39
39
40
-- The "gv" global will provide access to C global *scalars* and safe functions.
40
-- The "gv" global will provide access to C global *scalars* and safe functions.
41
-- NOTE: This exposes C library functions from e.g. the global C namespace, but
41
-- NOTE: This exposes C library functions from e.g. the global C namespace, but
42
-- without their declarations, they will be sitting there like a stone.
42
-- without their declarations, they will be sitting there like a stone.
43
local gv_ = {}
43
local gv_ = {}
44
-- [key]=<boolean> forbids, [key]=<non-boolean (e.g. table, function)> overrides
44
-- [key]=<boolean> forbids, [key]=<non-boolean (e.g. table, function)> overrides
45
local gv_access = {}
45
local gv_access = {}
46
46
47
-- This is for declarations of arrays or pointers which should not be
47
-- This is for declarations of arrays or pointers which should not be
48
-- accessible through the "gv" global. The "defs_common" module will
48
-- accessible through the "gv" global. The "defs_common" module will
49
-- use this function.
49
-- use this function.
50
--
50
--
51
-- Notes: do not declare multiple scalars on one line (this is bad:
51
-- Notes: do not declare multiple scalars on one line (this is bad:
52
-- "int32_t a, b"). Do not name array arguments (or add a space
52
-- "int32_t a, b"). Do not name array arguments (or add a space
53
-- between the identifier and the '[' instead).
53
-- between the identifier and the '[' instead).
54
function decl(str, ...)
54
function decl(str, ...)
55
    -- NOTE that the regexp also catches non-array/non-function identifiers
55
    -- NOTE that the regexp also catches non-array/non-function identifiers
56
    -- like "user_defs ud;"
56
    -- like "user_defs ud;"
57
    for varname in string.gmatch(str, "([%a_][%w_]*)[[(;]") do
57
    for varname in string.gmatch(str, "([%a_][%w_]*)[[(;]") do
58
        if (ffiC._DEBUG_LUNATIC ~= 0) then
58
        if (ffiC._DEBUG_LUNATIC ~= 0) then
59
            print("FORBID "..varname)
59
            print("FORBID "..varname)
60
        end
60
        end
61
        gv_access[varname] = true
61
        gv_access[varname] = true
62
    end
62
    end
63
63
64
    ffi.cdef(str, ...)
64
    ffi.cdef(str, ...)
65
end
65
end
66
66
67
lprivate.decl = decl
67
lprivate.decl = decl
68
68
69
ffi.cdef[[
69
ffi.cdef[[
70
enum {
70
enum {
71
    LUNATIC_CLIENT_MAPSTER32 = 0,
71
    LUNATIC_CLIENT_MAPSTER32 = 0,
72
    LUNATIC_CLIENT_EDUKE32 = 1,
72
    LUNATIC_CLIENT_EDUKE32 = 1,
73
73
74
    LUNATIC_CLIENT = LUNATIC_CLIENT_EDUKE32
74
    LUNATIC_CLIENT = LUNATIC_CLIENT_EDUKE32
75
}
75
}
76
]]
76
]]
77
77
78
-- Load the definitions common to the game's and editor's Lua interface.
78
-- Load the definitions common to the game's and editor's Lua interface.
79
local defs_c = require("defs_common")
79
local defs_c = require("defs_common")
80
local cansee = defs_c.cansee
80
local cansee = defs_c.cansee
81
local strip_const = defs_c.strip_const
81
local strip_const = defs_c.strip_const
82
local setmtonce = defs_c.setmtonce
82
local setmtonce = defs_c.setmtonce
83
83
84
-- Must be after loading "defs_common" which redefines "print" to use
84
-- Must be after loading "defs_common" which redefines "print" to use
85
-- OSD_Printf()
85
-- OSD_Printf()
86
local print, printf = print, defs_c.printf
86
local print, printf = print, defs_c.printf
87
87
88
88
89
---=== EDuke32 game definitions ===---
89
---=== EDuke32 game definitions ===---
90
90
91
local INV_NAMES = {
91
local INV_NAMES = {
92
    "STEROIDS",
92
    "STEROIDS",
93
    "SHIELD",
93
    "SHIELD",
94
    "SCUBA",
94
    "SCUBA",
95
    "HOLODUKE",
95
    "HOLODUKE",
96
    "JETPACK",
96
    "JETPACK",
97
    "DUMMY1",
97
    "DUMMY1",
98
    "ACCESS",
98
    "ACCESS",
99
    "HEATS",
99
    "HEATS",
100
    "DUMMY2",
100
    "DUMMY2",
101
    "FIRSTAID",
101
    "FIRSTAID",
102
    "BOOTS",
102
    "BOOTS",
103
}
103
}
104
104
105
local WEAPON_NAMES = {
105
local WEAPON_NAMES = {
106
    "KNEE",
106
    "KNEE",
107
    "PISTOL",
107
    "PISTOL",
108
    "SHOTGUN",
108
    "SHOTGUN",
109
    "CHAINGUN",
109
    "CHAINGUN",
110
    "RPG",
110
    "RPG",
111
    "HANDBOMB",
111
    "HANDBOMB",
112
    "SHRINKER",
112
    "SHRINKER",
113
    "DEVISTATOR",
113
    "DEVISTATOR",
114
    "TRIPBOMB",
114
    "TRIPBOMB",
115
    "FREEZE",
115
    "FREEZE",
116
    "HANDREMOTE",
116
    "HANDREMOTE",
117
    "GROW",
117
    "GROW",
118
}
118
}
119
119
120
---- game structs ----
120
---- game structs ----
121
121
122
lprivate.GET = defs_c.conststruct(INV_NAMES)
122
lprivate.GET = defs_c.conststruct(INV_NAMES)
123
lprivate.WEAPON = defs_c.conststruct(WEAPON_NAMES)
123
lprivate.WEAPON = defs_c.conststruct(WEAPON_NAMES)
124
124
125
ffi.cdef([[
125
ffi.cdef([[
126
enum {
126
enum {
127
    GET_MAX = 11,
127
    GET_MAX = 11,
128
    MAX_WEAPONS = 12,
128
    MAX_WEAPONS = 12,
129
    MAXPLAYERS = 16,
129
    MAXPLAYERS = 16,
130
    GTICSPERSEC = 30,  // The real number of movement updates per second
130
    GTICSPERSEC = 30,  // The real number of movement updates per second
131
};
131
};
132
]])
132
]])
133
133
134
ffi.cdef[[
134
ffi.cdef[[
135
struct action {
135
struct action {
136
    int16_t startframe, numframes;
136
    int16_t startframe, numframes;
137
    int16_t viewtype, incval, delay;
137
    int16_t viewtype, incval, delay;
138
    uint16_t flags;
138
    uint16_t flags;
139
};
139
};
140
140
141
struct move {
141
struct move {
142
    int16_t hvel, vvel;
142
    int16_t hvel, vvel;
143
};
143
};
144
144
145
#pragma pack(push,1)
145
#pragma pack(push,1)
146
typedef struct { int32_t id; struct move mv; } con_move_t;
146
typedef struct { int32_t id; struct move mv; } con_move_t;
147
typedef struct { int32_t id; struct action ac; } con_action_t;
147
typedef struct { int32_t id; struct action ac; } con_action_t;
148
#pragma pack(pop)
148
#pragma pack(pop)
149
149
150
typedef struct {
150
typedef struct {
151
    int32_t id;
151
    int32_t id;
152
    con_action_t act;
152
    con_action_t act;
153
    con_move_t mov;
153
    con_move_t mov;
154
    int32_t movflags;
154
    int32_t movflags;
155
} con_ai_t;
155
} con_ai_t;
156
]]
156
]]
157
157
158
defs_c.bitint_new_struct_type("int16_t", "SBit16")
158
defs_c.bitint_new_struct_type("int16_t", "SBit16")
159
defs_c.bitint_new_struct_type("int32_t", "SBit32")
159
defs_c.bitint_new_struct_type("int32_t", "SBit32")
160
defs_c.bitint_new_struct_type("uint32_t", "UBit32")
160
defs_c.bitint_new_struct_type("uint32_t", "UBit32")
161
161
162
-- Struct template for actor_t. It already has 'const' fields (TODO: might need
162
-- Struct template for actor_t. It already has 'const' fields (TODO: might need
163
-- to make more 'const'), but still has array members exposed, so is unsuited
163
-- to make more 'const'), but still has array members exposed, so is unsuited
164
-- for external exposure.
164
-- for external exposure.
165
local ACTOR_STRUCT = [[
165
local ACTOR_STRUCT = [[
166
struct {
166
struct {
167
    const int32_t t_data[10];
167
    const int32_t t_data[10];
168
    const struct move mv;
168
    const struct move mv;
169
    const struct action ac;
169
    const struct action ac;
170
    const uint16_t actiontics;
170
    const uint16_t actiontics;
171
171
172
]]..defs_c.bitint_member("SBit32", "flags")..[[
172
]]..defs_c.bitint_member("SBit32", "flags")..[[
173
    vec3_t bpos; //12b
173
    vec3_t bpos; //12b
174
    int32_t floorz,ceilingz,lastvx,lastvy; //16b
174
    int32_t floorz,ceilingz,lastvx,lastvy; //16b
175
    int32_t lasttransport; //4b
175
    int32_t lasttransport; //4b
176
176
177
    const int16_t picnum;
177
    const int16_t picnum;
178
    int16_t ang, extra;
178
    int16_t ang, extra;
179
    const int16_t owner;
179
    const int16_t owner;
180
    // NOTE: not to be confused with .movflags:
180
    // NOTE: not to be confused with .movflags:
181
]]..defs_c.bitint_member("SBit16", "_movflag")..[[
181
]]..defs_c.bitint_member("SBit16", "_movflag")..[[
182
    int16_t tempang, timetosleep;
182
    int16_t tempang, timetosleep;
183
183
184
    int16_t stayputsect;
184
    int16_t stayputsect;
185
    const int16_t dispicnum;
185
    const int16_t dispicnum;
186
    // Movement flags, sprite[i].hitag in C-CON.
186
    // Movement flags, sprite[i].hitag in C-CON.
187
    // XXX: more research where it was used in EDuke32's C code? (also .lotag <-> actiontics)
187
    // XXX: more research where it was used in EDuke32's C code? (also .lotag <-> actiontics)
188
    // XXX: what if CON code knew of the above implementation detail?
188
    // XXX: what if CON code knew of the above implementation detail?
189
]]..defs_c.bitint_member("UBit16", "movflags")..[[
189
]]..defs_c.bitint_member("UBit16", "movflags")..[[
190
    int16_t cgg;
190
    int16_t cgg;
191

191

192
    const int16_t lightId, lightcount, lightmaxrange;
192
    const int16_t lightId, lightcount, lightmaxrange;
193
    // NOTE: on 32-bit, C's lightptr+filler <=> this dummy:
193
    // NOTE: on 32-bit, C's lightptr+filler <=> this dummy:
194
    const union { intptr_t ptr; uint64_t dummy; } _light;
194
    const union { intptr_t ptr; uint64_t dummy; } _light;
195
}
195
}
196
]]
196
]]
197
197
198
local bcarray = require("bcarray")
198
local bcarray = require("bcarray")
199
199
200
local bcheck = require("bcheck")
200
local bcheck = require("bcheck")
201
local check_sector_idx, check_tile_idx = bcheck.sector_idx, bcheck.tile_idx
201
local check_sector_idx, check_tile_idx = bcheck.sector_idx, bcheck.tile_idx
202
local check_sprite_idx = bcheck.sprite_idx
202
local check_sprite_idx = bcheck.sprite_idx
203
local check_weapon_idx, check_inventory_idx = bcheck.weapon_idx, bcheck.inventory_idx
203
local check_weapon_idx, check_inventory_idx = bcheck.weapon_idx, bcheck.inventory_idx
204
local check_sound_idx = bcheck.sound_idx
204
local check_sound_idx = bcheck.sound_idx
205
local check_number = bcheck.number
205
local check_number = bcheck.number
206
local check_type = bcheck.type
206
local check_type = bcheck.type
207
207
208
bcarray.new("int16_t", 64, "loogie", "int16_x_64")  -- TODO: randomize member names
208
bcarray.new("int16_t", 64, "loogie", "int16_x_64")  -- TODO: randomize member names
209
bcarray.new("int16_t", ffiC.MAX_WEAPONS, "weapon", "int16_x_MAX_WEAPONS", WEAPON_NAMES)
209
bcarray.new("int16_t", ffiC.MAX_WEAPONS, "weapon", "int16_x_MAX_WEAPONS", WEAPON_NAMES)
210
bcarray.new("int16_t", ffiC.GET_MAX, "inventory", "int16_x_GET_MAX", INV_NAMES)
210
bcarray.new("int16_t", ffiC.GET_MAX, "inventory", "int16_x_GET_MAX", INV_NAMES)
211
211
212
-- NOTE: writing e.g. "ps.jetpack_on" in Lua when "ps.jetpack_on~=0" was meant
212
-- NOTE: writing e.g. "ps.jetpack_on" in Lua when "ps.jetpack_on~=0" was meant
213
-- is probably one of the most commonly committed errors, so we make it a bool
213
-- is probably one of the most commonly committed errors, so we make it a bool
214
-- type instead of uint8_t. The only issue is that if CON coders used these
214
-- type instead of uint8_t. The only issue is that if CON coders used these
215
-- fields to store more than just one bit, we're in trouble.
215
-- fields to store more than just one bit, we're in trouble.
216
-- This will need to be documented and frozen for release.
216
-- This will need to be documented and frozen for release.
217
local DUKEPLAYER_STRUCT = [[
217
local DUKEPLAYER_STRUCT = [[
218
__attribute__((packed)) struct {
218
__attribute__((packed)) struct {
219
    vec3_t pos, opos, vel, npos;
219
    vec3_t pos, opos, vel, npos;
220
    vec2_t bobpos, fric;
220
    vec2_t bobpos, fric;
221
    int32_t truefz, truecz, player_par;
221
    int32_t truefz, truecz, player_par;
222
    int32_t randomflamex, exitx, exity;
222
    int32_t randomflamex, exitx, exity;
223
    int32_t runspeed, max_player_health, max_shield_amount;
223
    int32_t runspeed, max_player_health, max_shield_amount;
224
    int32_t autostep, autostep_sbw;
224
    int32_t autostep, autostep_sbw;
225
225
226
    uint32_t interface_toggle_flag;
226
    uint32_t interface_toggle_flag;
227
227
228
    int32_t pipebombControl, pipebombLifetime, pipebombLifetimeVar;
228
    int32_t pipebombControl, pipebombLifetime, pipebombLifetimeVar;
229
    int32_t tripbombControl, tripbombLifetime, tripbombLifetimeVar;
229
    int32_t tripbombControl, tripbombLifetime, tripbombLifetimeVar;
230
230
231
    int32_t zrange;
231
    int32_t zrange;
232
    int16_t angrange, autoaimang;
232
    int16_t angrange, autoaimang;
233
233
234
    uint16_t max_actors_killed, actors_killed;
234
    uint16_t max_actors_killed, actors_killed;
235
]]..defs_c.bitint_member("UBit16", "gotweapon")..[[
235
]]..defs_c.bitint_member("UBit16", "gotweapon")..[[
236
    uint16_t zoom;
236
    uint16_t zoom;
237
237
238
    int16_x_64 loogiex;
238
    int16_x_64 loogiex;
239
    int16_x_64 loogiey;
239
    int16_x_64 loogiey;
240
    int16_t sbs, sound_pitch;
240
    int16_t sbs, sound_pitch;
241
241
242
    int16_t ang, oang, angvel;
242
    int16_t ang, oang, angvel;
243
    const<S> int16_t cursectnum;
243
    const<S> int16_t cursectnum;
244
    int16_t look_ang, last_extra, subweapon;
244
    int16_t look_ang, last_extra, subweapon;
245
    int16_x_MAX_WEAPONS max_ammo_amount;
245
    int16_x_MAX_WEAPONS max_ammo_amount;
246
    int16_x_MAX_WEAPONS ammo_amount;
246
    int16_x_MAX_WEAPONS ammo_amount;
247
    int16_x_GET_MAX inv_amount;
247
    int16_x_GET_MAX inv_amount;
248
    const<I-> int16_t wackedbyactor;
248
    const<I-> int16_t wackedbyactor;
249
    int16_t pyoff, opyoff;
249
    int16_t pyoff, opyoff;
250
250
251
    int16_t horiz, horizoff, ohoriz, ohorizoff;
251
    int16_t horiz, horizoff, ohoriz, ohorizoff;
252
    const<I-> int16_t newowner;
252
    const<I-> int16_t newowner;
253
    int16_t jumping_counter, airleft;
253
    int16_t jumping_counter, airleft;
254
    int16_t fta;
254
    int16_t fta;
255
    const<Q> int16_t ftq;
255
    const<Q> int16_t ftq;
256
    const int16_t access_wallnum, access_spritenum;
256
    const int16_t access_wallnum, access_spritenum;
257
    int16_t got_access, weapon_ang, visibility;
257
    int16_t got_access, weapon_ang, visibility;
258
    int16_t somethingonplayer, on_crane;
258
    int16_t somethingonplayer, on_crane;
259
    const int16_t i;
259
    const int16_t i;
260
    const int16_t one_parallax_sectnum;
260
    const int16_t one_parallax_sectnum;
261
    int16_t random_club_frame, one_eighty_count;
261
    int16_t random_club_frame, one_eighty_count;
262
    const<I-> int16_t dummyplayersprite;
262
    const<I-> int16_t dummyplayersprite;
263
    int16_t extra_extra8;
263
    int16_t extra_extra8;
264
    int16_t actorsqu, timebeforeexit;
264
    int16_t actorsqu, timebeforeexit;
265
    const<X-> int16_t customexitsound;
265
    const<X-> int16_t customexitsound;
266
    int16_t last_pissed_time;
266
    int16_t last_pissed_time;
267
267
268
    int16_x_MAX_WEAPONS weaprecs;
268
    int16_x_MAX_WEAPONS weaprecs;
269
    int16_t weapon_sway, crack_time, bobcounter;
269
    int16_t weapon_sway, crack_time, bobcounter;
270
270
271
    int16_t orotscrnang, rotscrnang, dead_flag;   // JBF 20031220: added orotscrnang
271
    int16_t orotscrnang, rotscrnang, dead_flag;   // JBF 20031220: added orotscrnang
272
    int16_t holoduke_on, pycount;
272
    int16_t holoduke_on, pycount;
273
    int16_t transporter_hold;
273
    int16_t transporter_hold;
274
274
275
    uint8_t max_secret_rooms, secret_rooms;
275
    uint8_t max_secret_rooms, secret_rooms;
276
    uint8_t frag, fraggedself, quick_kick, last_quick_kick;
276
    uint8_t frag, fraggedself, quick_kick, last_quick_kick;
277
    uint8_t return_to_center;
277
    uint8_t return_to_center;
278
    bool reloading;
278
    bool reloading;
279
    const uint8_t weapreccnt;
279
    const uint8_t weapreccnt;
280
    uint8_t aim_mode, auto_aim, weaponswitch, movement_lock, team;
280
    uint8_t aim_mode, auto_aim, weaponswitch, movement_lock, team;
281
    uint8_t tipincs, hbomb_hold_delay;
281
    uint8_t tipincs, hbomb_hold_delay;
282
    const<P> uint8_t frag_ps;
282
    const<P> uint8_t frag_ps;
283
    uint8_t kickback_pic;
283
    uint8_t kickback_pic;
284
284
285
    uint8_t gm;
285
    uint8_t gm;
286
    bool on_warping_sector;
286
    bool on_warping_sector;
287
    uint8_t footprintcount, hurt_delay;
287
    uint8_t footprintcount, hurt_delay;
288
    bool hbomb_on, jumping_toggle, rapid_fire_hold, on_ground;
288
    bool hbomb_on, jumping_toggle, rapid_fire_hold, on_ground;
289
    // NOTE: there's array indexing with inven_icon, but always after a
289
    // NOTE: there's array indexing with inven_icon, but always after a
290
    // bound check:
290
    // bound check:
291
    uint8_t inven_icon, buttonpalette;
291
    uint8_t inven_icon, buttonpalette;
292
    bool over_shoulder_on;
292
    bool over_shoulder_on;
293
    uint8_t show_empty_weapon;
293
    uint8_t show_empty_weapon;
294

294

295
    bool jetpack_on, spritebridge;
295
    bool jetpack_on, spritebridge;
296
    uint8_t lastrandomspot;  // unused
296
    uint8_t lastrandomspot;  // unused
297
    bool scuba_on;
297
    bool scuba_on;
298
    uint8_t footprintpal;
298
    uint8_t footprintpal;
299
    bool heat_on;
299
    bool heat_on;
300
    uint8_t invdisptime;
300
    uint8_t invdisptime;
301

301

302
    bool holster_weapon;
302
    bool holster_weapon;
303
    uint8_t falling_counter, footprintshade;
303
    uint8_t falling_counter, footprintshade;
304
    uint8_t refresh_inventory;
304
    uint8_t refresh_inventory;
305
    const<W> uint8_t last_full_weapon;
305
    const<W> uint8_t last_full_weapon;
306

306

307
    const uint8_t toggle_key_flag;
307
    const uint8_t toggle_key_flag;
308
    uint8_t knuckle_incs, knee_incs, access_incs;
308
    uint8_t knuckle_incs, knee_incs, access_incs;
309
    uint8_t walking_snd_toggle, palookup, hard_landing, fist_incs;
309
    uint8_t walking_snd_toggle, palookup, hard_landing, fist_incs;
310

310

311
    int8_t numloogs, loogcnt;
311
    int8_t numloogs, loogcnt;
312
    const int8_t scream_voice;
312
    const int8_t scream_voice;
313
    const<W-> int8_t last_weapon;
313
    const<W-> int8_t last_weapon;
314
    const int8_t cheat_phase;
314
    const int8_t cheat_phase;
315
    int8_t weapon_pos;
315
    int8_t weapon_pos;
316
    const<W-> int8_t wantweaponfire;
316
    const<W-> int8_t wantweaponfire;
317
    const<W> int8_t curr_weapon;
317
    const<W> int8_t curr_weapon;
318

318

319
    const uint8_t palette;
319
    const uint8_t palette;
320
    palette_t _pals;
320
    palette_t _pals;
321
    int8_t _palsfadespeed, _palsfadenext, _palsfadeprio, _padding2;
321
    int8_t _palsfadespeed, _palsfadenext, _palsfadeprio, _padding2;
322

322

323
    // NOTE: In C, the struct type has no name. We only have it here to define
323
    // NOTE: In C, the struct type has no name. We only have it here to define
324
    // a metatype later.
324
    // a metatype later.
325
    const weaponaccess_t weapon;
325
    const weaponaccess_t weapon;
326
    const int8_t _padding;
326
    const int8_t _padding;
327
}
327
}
328
]]
328
]]
329

329

330
local PROJECTILE_STRUCT = [[
330
local PROJECTILE_STRUCT = [[
331
struct {
331
struct {
332
    int32_t workslike, cstat;
332
    int32_t workslike, cstat;
333
    int32_t hitradius, range, flashcolor;
333
    int32_t hitradius, range, flashcolor;
334
    const int16_t spawns;
334
    const int16_t spawns;
335
    const int16_t sound, isound;
335
    const int16_t sound, isound;
336
    int16_t vel;
336
    int16_t vel;
337
    const int16_t decal, trail;
337
    const int16_t decal, trail;
338
    int16_t tnum, drop;
338
    int16_t tnum, drop;
339
    int16_t offset, bounces;
339
    int16_t offset, bounces;
340
    const int16_t bsound;
340
    const int16_t bsound;
341
    int16_t toffset;
341
    int16_t toffset;
342
    int16_t extra, extra_rand;
342
    int16_t extra, extra_rand;
343
    int8_t sxrepeat, syrepeat, txrepeat, tyrepeat;
343
    int8_t sxrepeat, syrepeat, txrepeat, tyrepeat;
344
    int8_t shade, xrepeat, yrepeat, pal;
344
    int8_t shade, xrepeat, yrepeat, pal;
345
    int8_t movecnt;
345
    int8_t movecnt;
346
    uint8_t clipdist;
346
    uint8_t clipdist;
347
    int8_t filler[2];
347
    int8_t filler[2];
348
    int32_t userdata;
348
    int32_t userdata;
349
}
349
}
350
]]
350
]]
351

351

352
-- KEEPINSYNC weapondata_mt below.
352
-- KEEPINSYNC weapondata_mt below.
353
local WEAPONDATA_STRUCT = "struct {"..table.concat(con_lang.wdata_members, ';').."; }"
353
local WEAPONDATA_STRUCT = "struct {"..table.concat(con_lang.wdata_members, ';').."; }"
354

354

355
local randgen = require("randgen")
355
local randgen = require("randgen")
356

356

357
local ma_rand = randgen.new(true)  -- initialize to "random" (time-based) seed
357
local ma_rand = randgen.new(true)  -- initialize to "random" (time-based) seed
358
local ma_count = nil
358
local ma_count = nil
359

359

360
local function ma_replace_array(typestr, neltstr)
360
local function ma_replace_array(typestr, neltstr)
361
    local nelts = tonumber(neltstr)
361
    local nelts = tonumber(neltstr)
362
    if (nelts==nil) then
362
    if (nelts==nil) then
363
        nelts = ffiC[neltstr]
363
        nelts = ffiC[neltstr]
364
        assert(type(nelts)=="number")
364
        assert(type(nelts)=="number")
365
    end
365
    end
366

366

367
    local strtab = { "const ", typestr.." " }
367
    local strtab = { "const ", typestr.." " }
368
    for i=1,nelts do
368
    for i=1,nelts do
369
        local ch1 = 97 + (ma_rand:getu32() % 25)  -- 'a'..'z'
369
        local ch1 = 97 + (ma_rand:getu32() % 25)  -- 'a'..'z'
370
        strtab[i+2] = string.format("_%c%x%s", ch1, ma_count, (i<nelts) and "," or ";")
370
        strtab[i+2] = string.format("_%c%x%s", ch1, ma_count, (i<nelts) and "," or ";")
371
        ma_count = ma_count+1
371
        ma_count = ma_count+1
372
    end
372
    end
373

373

374
    return table.concat(strtab)
374
    return table.concat(strtab)
375
end
375
end
376

376

377
---=== Protection of scalars in (currently only) DukePlayer_t struct. ===---
377
---=== Protection of scalars in (currently only) DukePlayer_t struct. ===---
378
-- This is more convenient than writing dozens of set-member methods.
378
-- This is more convenient than writing dozens of set-member methods.
379
local prot_scalar_chkfunc = {
379
local prot_scalar_chkfunc = {
380
    S = check_sector_idx,
380
    S = check_sector_idx,
381
    I = check_sprite_idx,
381
    I = check_sprite_idx,
382

382

383
    P = bcheck.player_idx,
383
    P = bcheck.player_idx,
384
    W = check_weapon_idx,
384
    W = check_weapon_idx,
385
    X = check_sound_idx,
385
    X = check_sound_idx,
386
    Q = bcheck.quote_idx,
386
    Q = bcheck.quote_idx,
387
}
387
}
388

388

389
local DukePlayer_prot_allowneg = {}  -- [<member name>] = true if setting <0 allowed
389
local DukePlayer_prot_allowneg = {}  -- [<member name>] = true if setting <0 allowed
390
local DukePlayer_prot_chkfunc = {}  -- [<member name>] = <checking function>
390
local DukePlayer_prot_chkfunc = {}  -- [<member name>] = <checking function>
391

391

392
local function ma_replace_scalar(what, typestr, membname)
392
local function ma_replace_scalar(what, typestr, membname)
393
    DukePlayer_prot_chkfunc[membname] = assert(prot_scalar_chkfunc[what:sub(1,1)])
393
    DukePlayer_prot_chkfunc[membname] = assert(prot_scalar_chkfunc[what:sub(1,1)])
394
    DukePlayer_prot_allowneg[membname] = (what:sub(2)=="-")
394
    DukePlayer_prot_allowneg[membname] = (what:sub(2)=="-")
395
    return ma_replace_array(typestr, 1)
395
    return ma_replace_array(typestr, 1)
396
end
396
end
397

397

398
-- Converts a template struct definition to an external one, in which arrays
398
-- Converts a template struct definition to an external one, in which arrays
399
-- have been substituted by randomly named scalar fields.
399
-- have been substituted by randomly named scalar fields.
400
-- <also_scalars>: also handle protected scalars like "const<W-> ..." etc.
400
-- <also_scalars>: also handle protected scalars like "const<W-> ..." etc.
401
local function mangle_arrays(structstr, also_scalars)
401
local function mangle_arrays(structstr, also_scalars)
402
    ma_count = 0
402
    ma_count = 0
403
    -- NOTE: regexp only works for non-nested arrays and for one array per line.
403
    -- NOTE: regexp only works for non-nested arrays and for one array per line.
404
    structstr = structstr:gsub("const%s+([%w_]+)[^\n]+%[([%w_]+)%];", ma_replace_array)
404
    structstr = structstr:gsub("const%s+([%w_]+)[^\n]+%[([%w_]+)%];", ma_replace_array)
405

405

406
    if (also_scalars) then
406
    if (also_scalars) then
407
        -- One protected scalar per line, too.
407
        -- One protected scalar per line, too.
408
        structstr = structstr:gsub("const<(.-)>%s+([%w_]-)%s+([%w_]-);", ma_replace_scalar)
408
        structstr = structstr:gsub("const<(.-)>%s+([%w_]-)%s+([%w_]-);", ma_replace_scalar)
409
    end
409
    end
410

410

411
    return structstr
411
    return structstr
412
end
412
end
413

413

414
--print(mangle_arrays(DUKEPLAYER_STRUCT, true))
414
--print(mangle_arrays(DUKEPLAYER_STRUCT, true))
415

415

416
--- default defines etc.
416
--- default defines etc.
417
local con_lang = require("con_lang")
417
local con_lang = require("con_lang")
418
local xmath = require("xmath")
418
local xmath = require("xmath")
419

419

420
ffi.cdef([[
420
ffi.cdef([[
421
typedef struct { int32_t _p; } weaponaccess_t;
421
typedef struct { int32_t _p; } weaponaccess_t;
422

422

423
typedef struct {
423
typedef struct {
424
]]..defs_c.bitint_member("UBit32", "bits")..[[
424
]]..defs_c.bitint_member("UBit32", "bits")..[[
425
    int16_t fvel, svel, avel;
425
    int16_t fvel, svel, avel;
426
    int8_t horz, extbits;
426
    int8_t horz, extbits;
427
} input_t;
427
} input_t;
428

428

429
typedef
429
typedef
430
]].. mangle_arrays(ACTOR_STRUCT) ..[[
430
]].. mangle_arrays(ACTOR_STRUCT) ..[[
431
actor_t;
431
actor_t;
432

432

433
typedef
433
typedef
434
]].. mangle_arrays(DUKEPLAYER_STRUCT, true) ..[[
434
]].. mangle_arrays(DUKEPLAYER_STRUCT, true) ..[[
435
DukePlayer_t;
435
DukePlayer_t;
436

436

437
typedef __attribute__((packed)) struct {
437
typedef __attribute__((packed)) struct {
438
    DukePlayer_t *ps;
438
    DukePlayer_t *ps;
439
    input_t *sync;
439
    input_t *sync;
440

440

441
    int32_t netsynctime;
441
    int32_t netsynctime;
442
    int16_t ping, filler;
442
    int16_t ping, filler;
443
    int32_t pcolor, pteam;
443
    int32_t pcolor, pteam;
444
    uint8_t frags[MAXPLAYERS], wchoice[MAX_WEAPONS];
444
    uint8_t frags[MAXPLAYERS], wchoice[MAX_WEAPONS];
445

445

446
    char vote, gotvote, pingcnt, playerquitflag, ready;
446
    char vote, gotvote, pingcnt, playerquitflag, ready;
447
    char user_name[32];
447
    char user_name[32];
448
    uint32_t revision;
448
    uint32_t revision;
449
} playerdata_t;
449
} playerdata_t;
450

450

451
typedef struct {
451
typedef struct {
452
    int32_t cur, count;
452
    int32_t cur, count;
453
    int32_t gunposx, lookhalfang;
453
    int32_t gunposx, lookhalfang;
454
    int32_t gunposy, lookhoriz;
454
    int32_t gunposy, lookhoriz;
455
    int32_t shade;
455
    int32_t shade;
456
} hudweapon_t;
456
} hudweapon_t;
457

457

458
typedef
458
typedef
459
]].. WEAPONDATA_STRUCT ..[[
459
]].. WEAPONDATA_STRUCT ..[[
460
weapondata_t;
460
weapondata_t;
461

461

462
typedef
462
typedef
463
]].. PROJECTILE_STRUCT ..[[
463
]].. PROJECTILE_STRUCT ..[[
464
projectile_t;
464
projectile_t;
465

465

466
typedef struct {
466
typedef struct {
467
    uint32_t _flags;  // XXX: do we want to have this accessible at game time?
467
    uint32_t _flags;  // XXX: do we want to have this accessible at game time?
468
    int32_t _cacherange;
468
    int32_t _cacherange;
469
    projectile_t *_proj;
469
    projectile_t *_proj;
470
    const projectile_t *_defproj;
470
    const projectile_t *_defproj;
471
} tiledata_t;
471
} tiledata_t;
472

472

473
typedef struct {
473
typedef struct {
474
    vec3_t pos;
474
    vec3_t pos;
475
    int32_t dist, clock;
475
    int32_t dist, clock;
476
    int16_t ang, horiz;
476
    int16_t ang, horiz;
477
    int16_t sect;  // NOTE: protected in camera_mt's __newindex
477
    int16_t sect;  // NOTE: protected in camera_mt's __newindex
478
} camera_t;
478
} camera_t;
479
479
480
enum
480
enum
481
{
481
{
482
    MAXMOUSEBUTTONS = 10,
482
    MAXMOUSEBUTTONS = 10,
483
    MAXMOUSEAXES = 2,
483
    MAXMOUSEAXES = 2,
484
    MAXJOYBUTTONS = 32,
484
    MAXJOYBUTTONS = 32,
485
    MAXJOYBUTTONSANDHATS = (32+4),
485
    MAXJOYBUTTONSANDHATS = (32+4),
486
    MAXJOYAXES = 9,
486
    MAXJOYAXES = 9,
487
487
488
    NUMGAMEFUNCTIONS = 56,
488
    NUMGAMEFUNCTIONS = 56,
489
489
490
    // game.h
490
    // game.h
491
    MAXRIDECULE = 10,
491
    MAXRIDECULE = 10,
492
    MAXRIDECULELENGTH = 40,
492
    MAXRIDECULELENGTH = 40,
493
    MAXSAVEGAMES = 10,
-
 
494
    MAXSAVEGAMENAME = 22,
493
    MAXSAVEGAMENAME = 22,
495
    MAXPWLOCKOUT = 128,
494
    MAXPWLOCKOUT = 128,
496
    MAXRTSNAME = 128,
495
    MAXRTSNAME = 128,
497
};
496
};
498
497
499
typedef struct {
498
typedef struct {
500
    int32_t const_visibility,uw_framerate;
499
    int32_t const_visibility,uw_framerate;
501
    int32_t camera_time,folfvel,folavel,folx,foly,fola;
500
    int32_t camera_time,folfvel,folavel,folx,foly,fola;
502
    int32_t reccnt,crosshairscale;
501
    int32_t reccnt,crosshairscale;
503
502
504
    int32_t runkey_mode,statusbarscale,mouseaiming,weaponswitch,drawweapon;   // JBF 20031125
503
    int32_t runkey_mode,statusbarscale,mouseaiming,weaponswitch,drawweapon;   // JBF 20031125
505
    int32_t democams,color,msgdisptime,statusbarmode;
504
    int32_t democams,color,msgdisptime,statusbarmode;
506
    int32_t m_noexits,noexits,autovote,automsg,idplayers;
505
    int32_t m_noexits,noexits,autovote,automsg,idplayers;
507
    int32_t team, viewbob, weaponsway, althud, weaponscale, textscale;
506
    int32_t team, viewbob, weaponsway, althud, weaponscale, textscale;
508
507
509
    int32_t entered_name,screen_tilting,shadows,fta_on,executions,auto_run;
508
    int32_t entered_name,screen_tilting,shadows,fta_on,executions,auto_run;
510
    int32_t coords,tickrate,levelstats,m_coop,coop,screen_size,lockout,crosshair;
509
    int32_t coords,tickrate,levelstats,m_coop,coop,screen_size,lockout,crosshair;
511
    int32_t playerai,angleinterpolation,obituaries;
510
    int32_t playerai,angleinterpolation,obituaries;
512
511
513
    int32_t respawn_monsters,respawn_items,respawn_inventory,recstat,monsters_off,brightness;
512
    int32_t respawn_monsters,respawn_items,respawn_inventory,recstat,monsters_off,brightness;
514
    int32_t m_respawn_items,m_respawn_monsters,m_respawn_inventory,m_recstat,m_monsters_off,detail;
513
    int32_t m_respawn_items,m_respawn_monsters,m_respawn_inventory,m_recstat,m_monsters_off,detail;
515
    int32_t m_ffire,ffire,m_player_skill,m_level_number,m_volume_number,multimode;
514
    int32_t m_ffire,ffire,m_player_skill,m_level_number,m_volume_number,multimode;
516
    int32_t player_skill,level_number,volume_number,m_marker,marker,mouseflip;
515
    int32_t player_skill,level_number,volume_number,m_marker,marker,mouseflip;
517
516
518
    vec2_t m_origin;
517
    vec2_t m_origin;
519
    int32_t playerbest;
518
    int32_t playerbest;
520
519
521
    int32_t configversion, bgstretch;
520
    int32_t configversion, bgstretch;
522
521
523
    int16_t pause_on,from_bonus;
522
    int16_t pause_on,from_bonus;
524
    int16_t camerasprite,last_camsprite;
523
    int16_t camerasprite,last_camsprite;
525
    int16_t last_level,secretlevel;
524
    int16_t last_level,secretlevel;
526
525
527
    struct {
526
    struct {
528
        int32_t UseJoystick;
527
        int32_t UseJoystick;
529
        int32_t UseMouse;
528
        int32_t UseMouse;
530
        int32_t AutoAim;
529
        int32_t AutoAim;
531
        int32_t ShowOpponentWeapons;
530
        int32_t ShowOpponentWeapons;
532
        int32_t MouseDeadZone,MouseBias;
531
        int32_t MouseDeadZone,MouseBias;
533
        int32_t SmoothInput;
532
        int32_t SmoothInput;
534
533
535
        // JBF 20031211: Store the input settings because
534
        // JBF 20031211: Store the input settings because
536
        // (currently) mact can't regurgitate them
535
        // (currently) mact can't regurgitate them
537
        int32_t MouseFunctions[MAXMOUSEBUTTONS][2];
536
        int32_t MouseFunctions[MAXMOUSEBUTTONS][2];
538
        int32_t MouseDigitalFunctions[MAXMOUSEAXES][2];
537
        int32_t MouseDigitalFunctions[MAXMOUSEAXES][2];
539
        int32_t MouseAnalogueAxes[MAXMOUSEAXES];
538
        int32_t MouseAnalogueAxes[MAXMOUSEAXES];
540
        int32_t MouseAnalogueScale[MAXMOUSEAXES];
539
        int32_t MouseAnalogueScale[MAXMOUSEAXES];
541
        int32_t JoystickFunctions[MAXJOYBUTTONSANDHATS][2];
540
        int32_t JoystickFunctions[MAXJOYBUTTONSANDHATS][2];
542
        int32_t JoystickDigitalFunctions[MAXJOYAXES][2];
541
        int32_t JoystickDigitalFunctions[MAXJOYAXES][2];
543
        int32_t JoystickAnalogueAxes[MAXJOYAXES];
542
        int32_t JoystickAnalogueAxes[MAXJOYAXES];
544
        int32_t JoystickAnalogueScale[MAXJOYAXES];
543
        int32_t JoystickAnalogueScale[MAXJOYAXES];
545
        int32_t JoystickAnalogueDead[MAXJOYAXES];
544
        int32_t JoystickAnalogueDead[MAXJOYAXES];
546
        int32_t JoystickAnalogueSaturate[MAXJOYAXES];
545
        int32_t JoystickAnalogueSaturate[MAXJOYAXES];
547
        uint8_t KeyboardKeys[NUMGAMEFUNCTIONS][2];
546
        uint8_t KeyboardKeys[NUMGAMEFUNCTIONS][2];
548

547

549
        //
548
        //
550
        // Sound variables
549
        // Sound variables
551
        //
550
        //
552
        int32_t MasterVolume;
551
        int32_t MasterVolume;
553
        int32_t FXVolume;
552
        int32_t FXVolume;
554
        int32_t MusicVolume;
553
        int32_t MusicVolume;
555
        int32_t SoundToggle;
554
        int32_t SoundToggle;
556
        int32_t MusicToggle;
555
        int32_t MusicToggle;
557
        int32_t VoiceToggle;
556
        int32_t VoiceToggle;
558
        int32_t AmbienceToggle;
557
        int32_t AmbienceToggle;
559

558

560
        int32_t NumVoices;
559
        int32_t NumVoices;
561
        int32_t NumChannels;
560
        int32_t NumChannels;
562
        int32_t NumBits;
561
        int32_t NumBits;
563
        int32_t MixRate;
562
        int32_t MixRate;
564

563

565
        int32_t ReverseStereo;
564
        int32_t ReverseStereo;
566

565

567
        //
566
        //
568
        // Screen variables
567
        // Screen variables
569
        //
568
        //
570

569

571
        int32_t ScreenMode;
570
        int32_t ScreenMode;
572

571

573
        int32_t ScreenWidth;
572
        int32_t ScreenWidth;
574
        int32_t ScreenHeight;
573
        int32_t ScreenHeight;
575
        int32_t ScreenBPP;
574
        int32_t ScreenBPP;
576

575

577
        int32_t ForceSetup;
576
        int32_t ForceSetup;
578
        int32_t NoAutoLoad;
577
        int32_t NoAutoLoad;
579

578

580
        const int32_t scripthandle;
579
        const int32_t scripthandle;
581
        int32_t setupread;
580
        int32_t setupread;
582

581

583
        int32_t CheckForUpdates;
582
        int32_t CheckForUpdates;
584
        int32_t LastUpdateCheck;
583
        int32_t LastUpdateCheck;
585
        int32_t useprecache;
584
        int32_t useprecache;
586
    } config;
585
    } config;
587

586

588
    char overhead_on,last_overhead,showweapons;
587
    char overhead_on,last_overhead,showweapons;
589
    char god,warp_on,cashman,eog,showallmap;
588
    char god,warp_on,cashman,eog,showallmap;
590
    char show_help,scrollmode,noclip;
589
    char show_help,scrollmode,noclip;
591
    char ridecule[MAXRIDECULE][MAXRIDECULELENGTH];
590
    char ridecule[MAXRIDECULE][MAXRIDECULELENGTH];
592
    char savegame[MAXSAVEGAMES][MAXSAVEGAMENAME];
-
 
593
    char pwlockout[MAXPWLOCKOUT],rtsname[MAXRTSNAME];
591
    char pwlockout[MAXPWLOCKOUT],rtsname[MAXRTSNAME];
594
    char display_bonus_screen;
592
    char display_bonus_screen;
595
    char show_level_text;
593
    char show_level_text;
596
    char wchoice[MAX_WEAPONS];
594
    char wchoice[MAX_WEAPONS];
597
} user_defs;
595
} user_defs;
598

596

599
typedef struct {
597
typedef struct {
600
    int32_t partime, designertime;
598
    int32_t partime, designertime;
601
    char *name, *filename, *musicfn;
599
    char *name, *filename, *musicfn;
602
    void *savedstate;
600
    void *savedstate;
603
} map_t;
601
} map_t;
604
]])
602
]])
605

603

606
bcarray.new("weapondata_t", ffiC.MAX_WEAPONS, "weapon", "weapondata_x_MAX_WEAPONS", WEAPON_NAMES)
604
bcarray.new("weapondata_t", ffiC.MAX_WEAPONS, "weapon", "weapondata_x_MAX_WEAPONS", WEAPON_NAMES)
607
bcarray.new("int32_t", con_lang.MAXSESSIONVARS, "sessionvar", "int32_x_MAXSESSIONVARS")
605
bcarray.new("int32_t", con_lang.MAXSESSIONVARS, "sessionvar", "int32_x_MAXSESSIONVARS")
608

606

609
-- EXTERNALLY EXPOSED GAME VARIABLES
607
-- EXTERNALLY EXPOSED GAME VARIABLES
610
ffi.cdef[[
608
ffi.cdef[[
611
const int32_t screenpeek;
609
const int32_t screenpeek;
612
hudweapon_t hudweap;
610
hudweapon_t hudweap;
613
int32_t g_logoFlags;
611
int32_t g_logoFlags;
614
]]
612
]]
615

613

616
-- INTERNAL VARIABLES/FUNCTIONS
614
-- INTERNAL VARIABLES/FUNCTIONS
617
decl("map_t g_mapInfo[$*$];", con_lang.MAXVOLUMES+1, con_lang.MAXLEVELS)
615
decl("map_t g_mapInfo[$*$];", con_lang.MAXVOLUMES+1, con_lang.MAXLEVELS)
618
decl("char g_volumeNames[$][33];", con_lang.MAXVOLUMES)
616
decl("char g_volumeNames[$][33];", con_lang.MAXVOLUMES)
619

617

620
decl[[
618
decl[[
621
const int32_t myconnectindex;
619
const int32_t myconnectindex;
622
int32_t g_RETURN;
620
int32_t g_RETURN;
623
int32_t g_elCONSize;
621
int32_t g_elCONSize;
624
char *g_elCON;
622
char *g_elCON;
625
void El_SetCON(const char *conluacode);
623
void El_SetCON(const char *conluacode);
626
void El_OnError(const char *str);
624
void El_OnError(const char *str);
627

625

628
char *g_elSavecode;
626
char *g_elSavecode;
629
void El_FreeSaveCode(void);
627
void El_FreeSaveCode(void);
630
const char *(*El_SerializeGamevars)(int32_t *slenptr, int32_t levelnum);
628
const char *(*El_SerializeGamevars)(int32_t *slenptr, int32_t levelnum);
631
int32_t (*El_RestoreGamevars)(const char *savecode);
629
int32_t (*El_RestoreGamevars)(const char *savecode);
632
int32_t (*El_GetLabelValue)(const char *label);
630
int32_t (*El_GetLabelValue)(const char *label);
633

631

634
const char *s_buildRev;
632
const char *s_buildRev;
635
const char *g_sizes_of_what[];
633
const char *g_sizes_of_what[];
636
int32_t g_sizes_of[];
634
int32_t g_sizes_of[];
637
int32_t g_elFirstTime;
635
int32_t g_elFirstTime;
638
int32_t g_elCallDepth;
636
int32_t g_elCallDepth;
639
int32_t block_deletesprite;
637
int32_t block_deletesprite;
640
const char **g_elModules;
638
const char **g_elModules;
641
char g_modDir[];
639
char g_modDir[];
642
int32_x_MAXSESSIONVARS g_elSessionVar;
640
int32_x_MAXSESSIONVARS g_elSessionVar;
643
actor_t actor[MAXSPRITES];
641
actor_t actor[MAXSPRITES];
644
camera_t g_camera;
642
camera_t g_camera;
645
user_defs ud;
643
user_defs ud;
646
playerdata_t *const g_player;
644
playerdata_t *const g_player;
647
DukePlayer_t *g_player_ps[MAXPLAYERS];
645
DukePlayer_t *g_player_ps[MAXPLAYERS];
648
weapondata_x_MAX_WEAPONS g_playerWeapon[MAXPLAYERS];
646
weapondata_x_MAX_WEAPONS g_playerWeapon[MAXPLAYERS];
649
weapondata_t g_weaponOverridden[MAX_WEAPONS];
647
weapondata_t g_weaponOverridden[MAX_WEAPONS];
650
int16_t WeaponPickupSprites[MAX_WEAPONS];
648
int16_t WeaponPickupSprites[MAX_WEAPONS];
651
tiledata_t g_tile[MAXTILES];
649
tiledata_t g_tile[MAXTILES];
652
projectile_t SpriteProjectile[MAXSPRITES];
650
projectile_t SpriteProjectile[MAXSPRITES];
653

651

654
int32_t g_noResetVars;
652
int32_t g_noResetVars;
655
void (*A_ResetVars)(int32_t iActor);
653
void (*A_ResetVars)(int32_t iActor);
656

654

657
// Used from lunacon.lua for dynamic {tile,sound} remapping:
655
// Used from lunacon.lua for dynamic {tile,sound} remapping:
658
struct
656
struct
659
{
657
{
660
    const char *str;
658
    const char *str;
661
    int32_t *dynvalptr;
659
    int32_t *dynvalptr;
662
    const int16_t staticval;
660
    const int16_t staticval;
663
} g_dynTileList[], g_dynSoundList[];
661
} g_dynTileList[], g_dynSoundList[];
664

662

665
char *apStrings[];
663
char *apStrings[];
666

664

667
const int32_t g_mostConcurrentPlayers;
665
const int32_t g_mostConcurrentPlayers;
668
int16_t g_deleteQueueSize;
666
int16_t g_deleteQueueSize;
669
int16_t g_blimpSpawnItems[15];
667
int16_t g_blimpSpawnItems[15];
670
int32_t g_scriptVersion;
668
int32_t g_scriptVersion;
671
const int32_t g_frameRate;
669
const int32_t g_frameRate;
672
const int32_t g_currentMenu;
670
const int32_t g_currentMenu;
673
uint16_t g_earthquakeTime;
671
uint16_t g_earthquakeTime;
674
uint32_t g_moveThingsCount;
672
uint32_t g_moveThingsCount;
675
char CheatKeys[2];
673
char CheatKeys[2];
676

674

677
// Must not have functions here that may call events directly or
675
// Must not have functions here that may call events directly or
678
// indirectly. See lunatic_game.c.
676
// indirectly. See lunatic_game.c.
679

677

680
int32_t A_IncurDamage(int32_t sn);  // not bound-checked!
678
int32_t A_IncurDamage(int32_t sn);  // not bound-checked!
681
int32_t G_CheckActivatorMotion(int32_t lotag);
679
int32_t G_CheckActivatorMotion(int32_t lotag);
682
int32_t A_Dodge(spritetype *s);
680
int32_t A_Dodge(spritetype *s);
683
int32_t A_MoveSpriteClipdist(int32_t spritenum, const vec3_t *change, uint32_t cliptype, int32_t clipdist);
681
int32_t A_MoveSpriteClipdist(int32_t spritenum, const vec3_t *change, uint32_t cliptype, int32_t clipdist);
684
void P_DoQuote(int32_t q, DukePlayer_t *p);
682
void P_DoQuote(int32_t q, DukePlayer_t *p);
685
void P_SetGamePalette(DukePlayer_t *player, uint32_t palid, int32_t set);
683
void P_SetGamePalette(DukePlayer_t *player, uint32_t palid, int32_t set);
686
void G_AddUserQuote(const char *daquote);
684
void G_AddUserQuote(const char *daquote);
687
void G_ClearCameraView(DukePlayer_t *ps);
685
void G_ClearCameraView(DukePlayer_t *ps);
688
void VM_DrawTileGeneric(int32_t x, int32_t y, int32_t zoom, int32_t tilenum,
686
void VM_DrawTileGeneric(int32_t x, int32_t y, int32_t zoom, int32_t tilenum,
689
                       int32_t shade, int32_t orientation, int32_t p);
687
                       int32_t shade, int32_t orientation, int32_t p);
690
void G_InitTimer(int32_t ticspersec);
688
void G_InitTimer(int32_t ticspersec);
691
void G_GetTimeDate(int32_t *vals);
689
void G_GetTimeDate(int32_t *vals);
692
int32_t G_ToggleWallInterpolation(int32_t w, int32_t doset);
690
int32_t G_ToggleWallInterpolation(int32_t w, int32_t doset);
693
int32_t G_StartTrack(int32_t level);
691
int32_t G_StartTrack(int32_t level);
694
int32_t VM_CheckSquished2(int32_t i, int32_t snum);
692
int32_t VM_CheckSquished2(int32_t i, int32_t snum);
695
void Menu_Change(int32_t cm);
693
void Menu_Change(int32_t cm);
696

694

697
const char *KB_ScanCodeToString(uint8_t scancode);
695
const char *KB_ScanCodeToString(uint8_t scancode);
698

696

699
int32_t A_CheckAnySoundPlaying(int32_t i);
697
int32_t A_CheckAnySoundPlaying(int32_t i);
700
int32_t S_CheckSoundPlaying(int32_t i, int32_t num);
698
int32_t S_CheckSoundPlaying(int32_t i, int32_t num);
701
void S_StopEnvSound(int32_t num, int32_t i);
699
void S_StopEnvSound(int32_t num, int32_t i);
702
void S_StopAllSounds(void);
700
void S_StopAllSounds(void);
703
void S_ChangeSoundPitch(int32_t num, int32_t i, int32_t pitchoffset);
701
void S_ChangeSoundPitch(int32_t num, int32_t i, int32_t pitchoffset);
704
int32_t S_GetMusicPosition(void);
702
int32_t S_GetMusicPosition(void);
705
void S_SetMusicPosition(int32_t position);
703
void S_SetMusicPosition(int32_t position);
706

704

707
int32_t minitext_(int32_t x,int32_t y,const char *t,int32_t s,int32_t p,int32_t sb);
705
int32_t minitext_(int32_t x,int32_t y,const char *t,int32_t s,int32_t p,int32_t sb);
708
void G_DrawTXDigiNumZ(int32_t starttile, int32_t x,int32_t y,int32_t n,int32_t s,int32_t pal,
706
void G_DrawTXDigiNumZ(int32_t starttile, int32_t x,int32_t y,int32_t n,int32_t s,int32_t pal,
709
                      int32_t cs,int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t z);
707
                      int32_t cs,int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t z);
710
void G_PrintGameText(int32_t tile, int32_t x, int32_t y, const char *t,
708
void G_PrintGameText(int32_t tile, int32_t x, int32_t y, const char *t,
711
                     int32_t s, int32_t p, int32_t o,
709
                     int32_t s, int32_t p, int32_t o,
712
                     int32_t x1, int32_t y1, int32_t x2, int32_t y2,
710
                     int32_t x1, int32_t y1, int32_t x2, int32_t y2,
713
                     int32_t z, int32_t a);
711
                     int32_t z, int32_t a);
714
vec2_t G_ScreenText(const int32_t font,
712
vec2_t G_ScreenText(const int32_t font,
715
                    int32_t x, int32_t y, const int32_t z, const int32_t blockangle, const int32_t charangle,
713
                    int32_t x, int32_t y, const int32_t z, const int32_t blockangle, const int32_t charangle,
716
                    const char *str, const int32_t shade, int32_t pal, int32_t o, int32_t alpha,
714
                    const char *str, const int32_t shade, int32_t pal, int32_t o, int32_t alpha,
717
                    int32_t xspace, int32_t yline, int32_t xbetween, int32_t ybetween, const int32_t f,
715
                    int32_t xspace, int32_t yline, int32_t xbetween, int32_t ybetween, const int32_t f,
718
                    const int32_t x1, const int32_t y1, const int32_t x2, const int32_t y2);
716
                    const int32_t x1, const int32_t y1, const int32_t x2, const int32_t y2);
719
vec2_t G_ScreenTextSize(const int32_t font,
717
vec2_t G_ScreenTextSize(const int32_t font,
720
                        int32_t x, int32_t y, const int32_t z, const int32_t blockangle,
718
                        int32_t x, int32_t y, const int32_t z, const int32_t blockangle,
721
                        const char *str, const int32_t o,
719
                        const char *str, const int32_t o,
722
                        int32_t xspace, int32_t yline, int32_t xbetween, int32_t ybetween,
720
                        int32_t xspace, int32_t yline, int32_t xbetween, int32_t ybetween,
723
                        const int32_t f,
721
                        const int32_t f,
724
                        int32_t x1, int32_t y1, int32_t x2, int32_t y2);
722
                        int32_t x1, int32_t y1, int32_t x2, int32_t y2);
725
const char* G_PrintYourTime(void);
723
const char* G_PrintYourTime(void);
726
const char* G_PrintParTime(void);
724
const char* G_PrintParTime(void);
727
const char* G_PrintDesignerTime(void);
725
const char* G_PrintDesignerTime(void);
728
const char* G_PrintBestTime(void);
726
const char* G_PrintBestTime(void);
729

727

730
void G_UpdateScreenArea(void);
728
void G_UpdateScreenArea(void);
731
void G_SaveMapState(void);
729
void G_SaveMapState(void);
732
void G_RestoreMapState(void);
730
void G_RestoreMapState(void);
733
void G_FreeMapState(int32_t mapnum);
731
void G_FreeMapState(int32_t mapnum);
734
]]
732
]]
735

733

736
decl[[
734
decl[[
737
int32_t kopen4loadfrommod(const char *filename, char searchfirst);
735
int32_t kopen4loadfrommod(const char *filename, char searchfirst);
738

736

739
char **g_scriptModules;
737
char **g_scriptModules;
740
int32_t g_scriptModulesNum;
738
int32_t g_scriptModulesNum;
741

739

742
const char *G_ConFile(void);
740
const char *G_ConFile(void);
743
void G_DoGameStartup(const int32_t *params);
741
void G_DoGameStartup(const int32_t *params);
744
int32_t C_DefineSound(int32_t sndidx, const char *fn, int32_t args [5]);
742
int32_t C_DefineSound(int32_t sndidx, const char *fn, int32_t args [5]);
745
void C_DefineMusic(int32_t vol, int32_t lev, const char *fn);
743
void C_DefineMusic(int32_t vol, int32_t lev, const char *fn);
746
void C_DefineQuote(int32_t qnum, const char *qstr);
744
void C_DefineQuote(int32_t qnum, const char *qstr);
747
void C_DefineVolumeName(int32_t vol, const char *name);
745
void C_DefineVolumeName(int32_t vol, const char *name);
748
void C_DefineVolumeFlags(int32_t vol, int32_t flags);
746
void C_DefineVolumeFlags(int32_t vol, int32_t flags);
749
void C_UndefineVolume(int32_t vol);
747
void C_UndefineVolume(int32_t vol);
750
void C_DefineSkillName(int32_t skill, const char *name);
748
void C_DefineSkillName(int32_t skill, const char *name);
751
void C_UndefineSkill(int32_t skill);
749
void C_UndefineSkill(int32_t skill);
752
void C_DefineLevelName(int32_t vol, int32_t lev, const char *fn,
750
void C_DefineLevelName(int32_t vol, int32_t lev, const char *fn,
753
                       int32_t partime, int32_t designertime,
751
                       int32_t partime, int32_t designertime,
754
                       const char *levelname);
752
                       const char *levelname);
755
void C_UndefineLevel(int32_t vol, int32_t lev);
753
void C_UndefineLevel(int32_t vol, int32_t lev);
756
void C_DefineProjectile(int32_t j, int32_t what, int32_t val);
754
void C_DefineProjectile(int32_t j, int32_t what, int32_t val);
757
void C_DefineGameFuncName(int32_t idx, const char *name);
755
void C_DefineGameFuncName(int32_t idx, const char *name);
758
void C_DefineGameType(int32_t idx, int32_t flags, const char *name);
756
void C_DefineGameType(int32_t idx, int32_t flags, const char *name);
759
int32_t C_SetDefName(const char *name);
757
int32_t C_SetDefName(const char *name);
760
void C_SetCfgName(const char *cfgname);
758
void C_SetCfgName(const char *cfgname);
761

759

762
int32_t SCRIPT_GetNumber(int32_t scripthandle, const char *sectionname, const char *entryname, int32_t *number);
760
int32_t SCRIPT_GetNumber(int32_t scripthandle, const char *sectionname, const char *entryname, int32_t *number);
763
void SCRIPT_PutNumber(int32_t scripthandle, const char *sectionname, const char *entryname, int32_t number,
761
void SCRIPT_PutNumber(int32_t scripthandle, const char *sectionname, const char *entryname, int32_t number,
764
                      int32_t hexadecimal, int32_t defaultvalue);
762
                      int32_t hexadecimal, int32_t defaultvalue);
765
]]
763
]]
766

764

767

765

768
-- http://lua-users.org/wiki/SandBoxes says "potentially unsafe"
766
-- http://lua-users.org/wiki/SandBoxes says "potentially unsafe"
769
-- as it allows to see implementations of functions.
767
-- as it allows to see implementations of functions.
770
--local string_dump = string.dump
768
--local string_dump = string.dump
771
string.dump = nil
769
string.dump = nil
772

770

773

771

774
-- sanity-check struct type sizes
772
-- sanity-check struct type sizes
775
local good = true
773
local good = true
776
for i=0,10 do
774
for i=0,10 do
777
    local what = ffi.string(ffiC.g_sizes_of_what[i])
775
    local what = ffi.string(ffiC.g_sizes_of_what[i])
778
    local fsz = ffi.sizeof(what)
776
    local fsz = ffi.sizeof(what)
779
    local csz = ffiC.g_sizes_of[i]
777
    local csz = ffiC.g_sizes_of[i]
780
    if (ffiC._DEBUG_LUNATIC ~= 0) then
778
    if (ffiC._DEBUG_LUNATIC ~= 0) then
781
        print(i..": "..what..": C sizeof = "..tostring(csz)..", FFI sizeof = "..tostring(fsz))
779
        print(i..": "..what..": C sizeof = "..tostring(csz)..", FFI sizeof = "..tostring(fsz))
782
    end
780
    end
783
    if (fsz ~= csz) then
781
    if (fsz ~= csz) then
784
        good = false;
782
        good = false;
785
    end
783
    end
786
end
784
end
787

785

788
if (not good) then
786
if (not good) then
789
    error("Some sizes don't match between C and LuaJIT/FFI.")
787
    error("Some sizes don't match between C and LuaJIT/FFI.")
790
end
788
end
791

789

792

790

793
--== "player" global, needed by the "control" module ==--
791
--== "player" global, needed by the "control" module ==--
794

792

795
local player_static_members = defs_c.static_members_tab()
793
local player_static_members = defs_c.static_members_tab()
796

794

797
--[[
795
--[[
798
player_static_members._INPUT_BITS = defs_c.conststruct
796
player_static_members._INPUT_BITS = defs_c.conststruct
799
{
797
{
800
    JUMP =                      1,
798
    JUMP =                      1,
801
    CROUCH =                    2,
799
    CROUCH =                    2,
802
    FIRE =                      4,
800
    FIRE =                      4,
803
    AIM_UP =                    8,
801
    AIM_UP =                    8,
804
    AIM_DOWN =                 16,
802
    AIM_DOWN =                 16,
805
    RUNNING =                  32,
803
    RUNNING =                  32,
806
    LOOK_LEFT =                64,
804
    LOOK_LEFT =                64,
807
    LOOK_RIGHT =              128,
805
    LOOK_RIGHT =              128,
808
    -- weapons...
806
    -- weapons...
809
    STEROIDS =               4096,
807
    STEROIDS =               4096,
810
    LOOK_UP =                8192,
808
    LOOK_UP =                8192,
811
    LOOK_DOWN =             16384,
809
    LOOK_DOWN =             16384,
812
    NIGHTVISION =           32768,
810
    NIGHTVISION =           32768,
813
    MEDKIT =                65536,
811
    MEDKIT =                65536,
814
    RESERVED =             131072,
812
    RESERVED =             131072,
815
    CENTER_VIEW =          262144,
813
    CENTER_VIEW =          262144,
816
    HOLSTER_WEAPON =       524288,
814
    HOLSTER_WEAPON =       524288,
817
    INVENTORY_LEFT =      1048576,
815
    INVENTORY_LEFT =      1048576,
818
    PAUSE =               2097152,
816
    PAUSE =               2097152,
819
    QUICK_KICK =          4194304,
817
    QUICK_KICK =          4194304,
820
    AIM_MODE =            8388608,
818
    AIM_MODE =            8388608,
821
    HOLODUKE =           16777216,
819
    HOLODUKE =           16777216,
822
    JETPACK =            33554432,
820
    JETPACK =            33554432,
823
    QUIT =               67108864,
821
    QUIT =               67108864,
824
    INVENTORY_RIGHT =   134217728,
822
    INVENTORY_RIGHT =   134217728,
825
    TURN_AROUND =       268435456,
823
    TURN_AROUND =       268435456,
826
    OPEN =              536870912,
824
    OPEN =              536870912,
827
    INVENTORY =        1073741824,
825
    INVENTORY =        1073741824,
828
    ESC =              2147483648,
826
    ESC =              2147483648,
829
}
827
}
830

828

831
player_static_members._INPUT_EXT_BITS = defs_c.conststruct
829
player_static_members._INPUT_EXT_BITS = defs_c.conststruct
832
{
830
{
833
    MOVE_FORWARD =  1,
831
    MOVE_FORWARD =  1,
834
    MOVE_BACKWARD = 2,
832
    MOVE_BACKWARD = 2,
835
    STRAFE_LEFT =   4,
833
    STRAFE_LEFT =   4,
836
    STRAFE_RIGHT =  8,
834
    STRAFE_RIGHT =  8,
837
    TURN_LEFT =    16,
835
    TURN_LEFT =    16,
838
    TURN_RIGHT =   32,
836
    TURN_RIGHT =   32,
839
}
837
}
840
--]]
838
--]]
841

839

842
local band = bit.band
840
local band = bit.band
843
local lsh = bit.lshift
841
local lsh = bit.lshift
844
local ivec3 = xmath.ivec3
842
local ivec3 = xmath.ivec3
845

843

846
do
844
do
847
    -- player.all() iterator
845
    -- player.all() iterator
848
    local function iter_allplayers(_nil, pli)
846
    local function iter_allplayers(_nil, pli)
849
        if (pli+1 < ffiC.g_mostConcurrentPlayers) then
847
        if (pli+1 < ffiC.g_mostConcurrentPlayers) then
850
            return pli+1
848
            return pli+1
851
        end
849
        end
852
    end
850
    end
853

851

854
    function player_static_members.all()
852
    function player_static_members.all()
855
        return iter_allplayers, nil, -1
853
        return iter_allplayers, nil, -1
856
    end
854
    end
857

855

858
    -- player.holdskey(pli, keyname)
856
    -- player.holdskey(pli, keyname)
859
    local KEYS = {  -- SK_CROUCH etc. -- "sync keys"
857
    local KEYS = {  -- SK_CROUCH etc. -- "sync keys"
860
        CROUCH = lsh(1,1),
858
        CROUCH = lsh(1,1),
861
        RUN = lsh(1,5),
859
        RUN = lsh(1,5),
862
        OPEN = lsh(1,29),
860
        OPEN = lsh(1,29),
863
    }
861
    }
864

862

865
    function player_static_members.holdskey(pli, keyname)
863
    function player_static_members.holdskey(pli, keyname)
866
        bcheck.player_idx(pli)
864
        bcheck.player_idx(pli)
867
        if (KEYS[keyname] == nil) then
865
        if (KEYS[keyname] == nil) then
868
            error("invalid key name: "..tostring(keyname), 2)
866
            error("invalid key name: "..tostring(keyname), 2)
869
        end
867
        end
870

868

871
        return ffiC.g_player[pli].sync.bitsbits:test(KEYS[keyname])
869
        return ffiC.g_player[pli].sync.bitsbits:test(KEYS[keyname])
872
    end
870
    end
873
end
871
end
874

872

875
local player_holdskey = player_static_members.holdskey
873
local player_holdskey = player_static_members.holdskey
876

874

877
-- Actor flags
875
-- Actor flags
878
local actor_static_members = defs_c.static_members_tab()
876
local actor_static_members = defs_c.static_members_tab()
879
do
877
do
880
    local our_SFLAG = {}
878
    local our_SFLAG = {}
881
    local ext_SFLAG = con_lang.labels[4]  -- external actor flags only
879
    local ext_SFLAG = con_lang.labels[4]  -- external actor flags only
882
    local USER_MASK = 0
880
    local USER_MASK = 0
883

881

884
    for name, flag in pairs(ext_SFLAG) do
882
    for name, flag in pairs(ext_SFLAG) do
885
        our_SFLAG[name:sub(7)] = flag  -- strip "SFLAG_"
883
        our_SFLAG[name:sub(7)] = flag  -- strip "SFLAG_"
886
        USER_MASK = bit.bor(USER_MASK, flag)
884
        USER_MASK = bit.bor(USER_MASK, flag)
887
    end
885
    end
888

886

889
    -- Add a couple of convenience defines.
887
    -- Add a couple of convenience defines.
890
    our_SFLAG.enemy = con_lang.SFLAG.SFLAG_BADGUY
888
    our_SFLAG.enemy = con_lang.SFLAG.SFLAG_BADGUY
891
    our_SFLAG.enemystayput = con_lang.SFLAG.SFLAG_BADGUY + con_lang.SFLAG.SFLAG_BADGUYSTAYPUT
889
    our_SFLAG.enemystayput = con_lang.SFLAG.SFLAG_BADGUY + con_lang.SFLAG.SFLAG_BADGUYSTAYPUT
892
    our_SFLAG.rotfixed = con_lang.SFLAG.SFLAG_ROTFIXED
890
    our_SFLAG.rotfixed = con_lang.SFLAG.SFLAG_ROTFIXED
893

891

894
    -- Callback function chaining control flags.
892
    -- Callback function chaining control flags.
895
    our_SFLAG.replace = 0x08000000
893
    our_SFLAG.replace = 0x08000000
896
    our_SFLAG.replace_soft = 0x08000000  -- compat
894
    our_SFLAG.replace_soft = 0x08000000  -- compat
897
    our_SFLAG.replace_hard = 0x08000000  -- compat, deprecated
895
    our_SFLAG.replace_hard = 0x08000000  -- compat, deprecated
898
    our_SFLAG.chain_beg = 0x20000000
896
    our_SFLAG.chain_beg = 0x20000000
899
    our_SFLAG.chain_end = 0x40000000
897
    our_SFLAG.chain_end = 0x40000000
900
    our_SFLAG._CHAIN_MASK_ACTOR = 0x78000000
898
    our_SFLAG._CHAIN_MASK_ACTOR = 0x78000000
901
    our_SFLAG._CHAIN_MASK_EVENT = 0x68000000
899
    our_SFLAG._CHAIN_MASK_EVENT = 0x68000000
902

900

903
    -- XXX: CON doesn't export BADGUYSTAYPUT or ROTFIXED SFLAGs, but they are considered
901
    -- XXX: CON doesn't export BADGUYSTAYPUT or ROTFIXED SFLAGs, but they are considered
904
    -- external for Lunatic.
902
    -- external for Lunatic.
905
    our_SFLAG.USER_MASK = bit.bor(USER_MASK, our_SFLAG.enemystayput, our_SFLAG.rotfixed)
903
    our_SFLAG.USER_MASK = bit.bor(USER_MASK, our_SFLAG.enemystayput, our_SFLAG.rotfixed)
906

904

907
    actor_static_members.FLAGS = defs_c.conststruct(our_SFLAG)
905
    actor_static_members.FLAGS = defs_c.conststruct(our_SFLAG)
908

906

909
    -- Sprite status numbers. Kept in 'actor', because it's more of a game-side
907
    -- Sprite status numbers. Kept in 'actor', because it's more of a game-side
910
    -- concept (event though status lists are implemented in the engine), and
908
    -- concept (event though status lists are implemented in the engine), and
911
    -- to prevent confusion with sprite.CSTAT.
909
    -- to prevent confusion with sprite.CSTAT.
912
    local our_STAT = {}
910
    local our_STAT = {}
913

911

914
    for name, statnum in pairs(con_lang.STAT) do
912
    for name, statnum in pairs(con_lang.STAT) do
915
        -- Strip 'STAT_'.
913
        -- Strip 'STAT_'.
916
        our_STAT[name:sub(6)] = statnum
914
        our_STAT[name:sub(6)] = statnum
917
    end
915
    end
918

916

919
    actor_static_members.STAT = defs_c.conststruct(our_STAT)
917
    actor_static_members.STAT = defs_c.conststruct(our_STAT)
920

918

921
    actor_static_members.MOVFLAGS = defs_c.conststruct
919
    actor_static_members.MOVFLAGS = defs_c.conststruct
922
    {
920
    {
923
        -- NOTE: no underscores, like in DEFS.CON.
921
        -- NOTE: no underscores, like in DEFS.CON.
924
        faceplayer = 1,
922
        faceplayer = 1,
925
        geth = 2,
923
        geth = 2,
926
        getv = 4,
924
        getv = 4,
927
        randomangle = 8,
925
        randomangle = 8,
928
        faceplayerslow = 16,
926
        faceplayerslow = 16,
929
        spin = 32,
927
        spin = 32,
930
        faceplayersmart = 64,
928
        faceplayersmart = 64,
931
        fleeenemy = 128,
929
        fleeenemy = 128,
932
        jumptoplayer_only = 256,
930
        jumptoplayer_only = 256,
933
        jumptoplayer_bits = 257,  -- NOTE: two bits set!
931
        jumptoplayer_bits = 257,  -- NOTE: two bits set!
934
        jumptoplayer = 257,
932
        jumptoplayer = 257,
935
        seekplayer = 512,
933
        seekplayer = 512,
936
        furthestdir = 1024,
934
        furthestdir = 1024,
937
        dodgebullet = 4096,
935
        dodgebullet = 4096,
938
    }
936
    }
939
end
937
end
940

938

941
function actor_static_members.fall(i)
939
function actor_static_members.fall(i)
942
    check_sprite_idx(i)
940
    check_sprite_idx(i)
943
    CF.VM_FallSprite(i)
941
    CF.VM_FallSprite(i)
944
end
942
end
945

943

946
-- actor.move(i, vec, cliptype [, clipdist])
944
-- actor.move(i, vec, cliptype [, clipdist])
947
function actor_static_members.move(i, vec, cliptype, clipdist)
945
function actor_static_members.move(i, vec, cliptype, clipdist)
948
    check_sprite_idx(i)
946
    check_sprite_idx(i)
949
    local vel = ivec3(vec.x, vec.y, vec.z)
947
    local vel = ivec3(vec.x, vec.y, vec.z)
950
    return ffiC.A_MoveSpriteClipdist(i, vel, cliptype, clipdist or -1)
948
    return ffiC.A_MoveSpriteClipdist(i, vel, cliptype, clipdist or -1)
951
end
949
end
952

950

953
-- Delete sprite with index <i>.
951
-- Delete sprite with index <i>.
954
function actor_static_members.delete(i)
952
function actor_static_members.delete(i)
955
    check_sprite_idx(i)
953
    check_sprite_idx(i)
956

954

957
    if (ffiC.sprite[i].statnum == ffiC.MAXSTATUS) then
955
    if (ffiC.sprite[i].statnum == ffiC.MAXSTATUS) then
958
        error("Attempt to delete a sprite already not in the game world", 2)
956
        error("Attempt to delete a sprite already not in the game world", 2)
959
    end
957
    end
960

958

961
    if (ffiC.block_deletesprite ~= 0) then
959
    if (ffiC.block_deletesprite ~= 0) then
962
        error("Attempt to delete sprite in EVENT_EGS", 2)
960
        error("Attempt to delete sprite in EVENT_EGS", 2)
963
    end
961
    end
964

962

965
    -- TODO_MP
963
    -- TODO_MP
966
    if (ffiC.g_player_ps[0].i == i) then
964
    if (ffiC.g_player_ps[0].i == i) then
967
        error("Attempt to delete player 0's APLAYER sprite", 2)
965
        error("Attempt to delete player 0's APLAYER sprite", 2)
968
    end
966
    end
969

967

970
    CF.A_DeleteSprite(i)
968
    CF.A_DeleteSprite(i)
971
end
969
end
972

970

973
local tile_static_members = defs_c.static_members_tab()
971
local tile_static_members = defs_c.static_members_tab()
974
do
972
do
975
    tile_static_members.sizx = defs_c.creategtab_membidx(ffiC.tilesiz, "x", ffiC.MAXTILES, "tilesizx[]")
973
    tile_static_members.sizx = defs_c.creategtab_membidx(ffiC.tilesiz, "x", ffiC.MAXTILES, "tilesizx[]")
976
    tile_static_members.sizy = defs_c.creategtab_membidx(ffiC.tilesiz, "y", ffiC.MAXTILES, "tilesizy[]")
974
    tile_static_members.sizy = defs_c.creategtab_membidx(ffiC.tilesiz, "y", ffiC.MAXTILES, "tilesizy[]")
977
end
975
end
978

976

979
-- XXX: error message will say "g_player_ps"
977
-- XXX: error message will say "g_player_ps"
980
player = setmtonce({}, defs_c.GenStructMetatable("g_player_ps", "g_mostConcurrentPlayers", player_static_members))
978
player = setmtonce({}, defs_c.GenStructMetatable("g_player_ps", "g_mostConcurrentPlayers", player_static_members))
981

979

982
-- needed by "control"
980
-- needed by "control"
983
actor = setmtonce({}, defs_c.GenStructMetatable("actor", "MAXSPRITES", actor_static_members))
981
actor = setmtonce({}, defs_c.GenStructMetatable("actor", "MAXSPRITES", actor_static_members))
984
-- Some bitwise NOTs of various actor flag masks.
982
-- Some bitwise NOTs of various actor flag masks.
985
local BNOT = {
983
local BNOT = {
986
    USER_MASK = bit.bnot(actor.FLAGS.USER_MASK),
984
    USER_MASK = bit.bnot(actor.FLAGS.USER_MASK),
987
    CHAIN_MASK_ACTOR = bit.bnot(actor.FLAGS._CHAIN_MASK_ACTOR),
985
    CHAIN_MASK_ACTOR = bit.bnot(actor.FLAGS._CHAIN_MASK_ACTOR),
988
    CHAIN_MASK_EVENT = bit.bnot(actor.FLAGS._CHAIN_MASK_EVENT),
986
    CHAIN_MASK_EVENT = bit.bnot(actor.FLAGS._CHAIN_MASK_EVENT),
989
}
987
}
990

988

991
local projectile = defs_c.creategtab_membidx_ptr(ffiC.g_tile, "_proj", ffiC.MAXTILES, "projectile")
989
local projectile = defs_c.creategtab_membidx_ptr(ffiC.g_tile, "_proj", ffiC.MAXTILES, "projectile")
992
local g_tile = setmtonce({}, defs_c.GenStructMetatable("g_tile", "MAXTILES", tile_static_members))
990
local g_tile = setmtonce({}, defs_c.GenStructMetatable("g_tile", "MAXTILES", tile_static_members))
993

991

994
--== Custom operations for BUILD data structures ==--
992
--== Custom operations for BUILD data structures ==--
995
-- Among other things, declares struct action and struct move, and their
993
-- Among other things, declares struct action and struct move, and their
996
-- ID-wrapped types con_action_t and con_move_t
994
-- ID-wrapped types con_action_t and con_move_t
997
local con = require("control")
995
local con = require("control")
998

996

999
do
997
do
1000
    local isenemytile = con.isenemytile
998
    local isenemytile = con.isenemytile
1001

999

1002
    -- Add game-side metamethods to "spritetype" and register it with "metatype"
1000
    -- Add game-side metamethods to "spritetype" and register it with "metatype"
1003
    local spr_mt_index_add = {
1001
    local spr_mt_index_add = {
1004
        isenemy = function(s)
1002
        isenemy = function(s)
1005
            return isenemytile(s.picnum)
1003
            return isenemytile(s.picnum)
1006
        end,
1004
        end,
1007
    }
1005
    }
1008

1006

1009
    defs_c.finish_spritetype(spr_mt_index_add)
1007
    defs_c.finish_spritetype(spr_mt_index_add)
1010
end
1008
end
1011

1009

1012
-- Check a literal numeric action or move value.
1010
-- Check a literal numeric action or move value.
1013
local function check_literal_am(am, typename)
1011
local function check_literal_am(am, typename)
1014
    if (type(am) ~= "number") then
1012
    if (type(am) ~= "number") then
1015
        error("bad argument: expected number or "..typename, 3)
1013
        error("bad argument: expected number or "..typename, 3)
1016
    end
1014
    end
1017

1015

1018
    -- Negative values are generated as con.action/con.move IDs.
1016
    -- Negative values are generated as con.action/con.move IDs.
1019
    if (not (am >= 0 and am <= 32767)) then
1017
    if (not (am >= 0 and am <= 32767)) then
1020
        error("bad argument: expected number in [0 .. 32767]", 3)
1018
        error("bad argument: expected number in [0 .. 32767]", 3)
1021
    end
1019
    end
1022
end
1020
end
1023

1021

1024
-- An unrestricted actor_t pointer, for internal use:
1022
-- An unrestricted actor_t pointer, for internal use:
1025
local actor_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(ACTOR_STRUCT)))
1023
local actor_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(ACTOR_STRUCT)))
1026
local player_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(DUKEPLAYER_STRUCT)))
1024
local player_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(DUKEPLAYER_STRUCT)))
1027
local projectile_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(PROJECTILE_STRUCT)))
1025
local projectile_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(PROJECTILE_STRUCT)))
1028
-- An unrestricted weapondata_t pointer, but with the member names stripped of
1026
-- An unrestricted weapondata_t pointer, but with the member names stripped of
1029
-- the leading underscore, too:
1027
-- the leading underscore, too:
1030
local weapondata_ptr_ct = ffi.typeof("$ *", ffi.typeof((strip_const(WEAPONDATA_STRUCT):gsub(" _"," "))))
1028
local weapondata_ptr_ct = ffi.typeof("$ *", ffi.typeof((strip_const(WEAPONDATA_STRUCT):gsub(" _"," "))))
1031

1029

1032
local con_action_ct = ffi.typeof("const con_action_t")
1030
local con_action_ct = ffi.typeof("const con_action_t")
1033
local con_move_ct = ffi.typeof("const con_move_t")
1031
local con_move_ct = ffi.typeof("const con_move_t")
1034
local con_ai_ct = ffi.typeof("const con_ai_t")
1032
local con_ai_ct = ffi.typeof("const con_ai_t")
1035

1033

1036
-- All-zero bare action and move.
1034
-- All-zero bare action and move.
1037
local nullac, nullmv = ffi.new("const struct action"), ffi.new("const struct move")
1035
local nullac, nullmv = ffi.new("const struct action"), ffi.new("const struct move")
1038
-- All-zero action and move with IDs. Mostly for CON support.
1036
-- All-zero action and move with IDs. Mostly for CON support.
1039
local literal_act = { [0]=con_action_ct(0), [1]=con_action_ct(1) }
1037
local literal_act = { [0]=con_action_ct(0), [1]=con_action_ct(1) }
1040
local literal_mov = { [0]=con_move_ct(0), [1]=con_move_ct(1) }
1038
local literal_mov = { [0]=con_move_ct(0), [1]=con_move_ct(1) }
1041

1039

1042
local function get_actor_idx(a)
1040
local function get_actor_idx(a)
1043
    local i = ffi.cast(actor_ptr_ct, a)-ffi.cast(actor_ptr_ct, ffiC.actor)
1041
    local i = ffi.cast(actor_ptr_ct, a)-ffi.cast(actor_ptr_ct, ffiC.actor)
1044
--    assert(not (i >= ffiC.MAXSPRITES+0ULL))
1042
--    assert(not (i >= ffiC.MAXSPRITES+0ULL))
1045
    return i
1043
    return i
1046
end
1044
end
1047

1045

1048
local actor_methods = {
1046
local actor_methods = {
1049
    -- action
1047
    -- action
1050
    set_action = function(a, act)
1048
    set_action = function(a, act)
1051
        a = ffi.cast(actor_ptr_ct, a)
1049
        a = ffi.cast(actor_ptr_ct, a)
1052

1050

1053
        if (ffi.istype(con_action_ct, act)) then
1051
        if (ffi.istype(con_action_ct, act)) then
1054
            a.t_data[4] = act.id
1052
            a.t_data[4] = act.id
1055
            a.ac = act.ac
1053
            a.ac = act.ac
1056
        else
1054
        else
1057
            check_literal_am(act, "action")
1055
            check_literal_am(act, "action")
1058
            a.t_data[4] = act
1056
            a.t_data[4] = act
1059
            a.ac = nullac
1057
            a.ac = nullac
1060
        end
1058
        end
1061

1059

1062
        a.t_data[2] = 0
1060
        a.t_data[2] = 0
1063
        a.t_data[3] = 0
1061
        a.t_data[3] = 0
1064
    end,
1062
    end,
1065

1063

1066
    has_action = function(a, act)
1064
    has_action = function(a, act)
1067
        a = ffi.cast(actor_ptr_ct, a)
1065
        a = ffi.cast(actor_ptr_ct, a)
1068

1066

1069
        if (ffi.istype(con_action_ct, act)) then
1067
        if (ffi.istype(con_action_ct, act)) then
1070
            return (a.t_data[4]==act.id)
1068
            return (a.t_data[4]==act.id)
1071
        else
1069
        else
1072
            check_literal_am(act, "action")
1070
            check_literal_am(act, "action")
1073
            return (a.t_data[4]==act)
1071
            return (a.t_data[4]==act)
1074
        end
1072
        end
1075
    end,
1073
    end,
1076

1074

1077
    -- count
1075
    -- count
1078
    set_count = function(a, count)
1076
    set_count = function(a, count)
1079
        ffi.cast(actor_ptr_ct, a).t_data[0] = count
1077
        ffi.cast(actor_ptr_ct, a).t_data[0] = count
1080
    end,
1078
    end,
1081

1079

1082
    get_count = function(a)
1080
    get_count = function(a)
1083
        return ffi.cast(actor_ptr_ct, a).t_data[0]
1081
        return ffi.cast(actor_ptr_ct, a).t_data[0]
1084
    end,
1082
    end,
1085

1083

1086
    -- action count
1084
    -- action count
1087
    reset_acount = function(a)
1085
    reset_acount = function(a)
1088
        ffi.cast(actor_ptr_ct, a).t_data[2] = 0
1086
        ffi.cast(actor_ptr_ct, a).t_data[2] = 0
1089
    end,
1087
    end,
1090

1088

1091
    get_acount = function(a)
1089
    get_acount = function(a)
1092
        return ffi.cast(actor_ptr_ct, a).t_data[2]
1090
        return ffi.cast(actor_ptr_ct, a).t_data[2]
1093
    end,
1091
    end,
1094

1092

1095
    -- Override action delay. The action ID is kept.
1093
    -- Override action delay. The action ID is kept.
1096
    set_action_delay = function(a, delay)
1094
    set_action_delay = function(a, delay)
1097
        ffi.cast(actor_ptr_ct, a).ac.delay = delay
1095
        ffi.cast(actor_ptr_ct, a).ac.delay = delay
1098
    end,
1096
    end,
1099

1097

1100
    -- move
1098
    -- move
1101
    set_move = function(a, mov, movflags)
1099
    set_move = function(a, mov, movflags)
1102
        a = ffi.cast(actor_ptr_ct, a)
1100
        a = ffi.cast(actor_ptr_ct, a)
1103

1101

1104
        if (ffi.istype(con_move_ct, mov)) then
1102
        if (ffi.istype(con_move_ct, mov)) then
1105
            a.t_data[1] = mov.id
1103
            a.t_data[1] = mov.id
1106
            a.mv = mov.mv
1104
            a.mv = mov.mv
1107
        else
1105
        else
1108
            check_literal_am(mov, "move")
1106
            check_literal_am(mov, "move")
1109
            a.t_data[1] = mov
1107
            a.t_data[1] = mov
1110
            a.mv = nullmv
1108
            a.mv = nullmv
1111
        end
1109
        end
1112

1110

1113
        a.t_data[0] = 0
1111
        a.t_data[0] = 0
1114
        a.movflags = movflags or 0
1112
        a.movflags = movflags or 0
1115
        local spr = ffiC.sprite[get_actor_idx(a)]
1113
        local spr = ffiC.sprite[get_actor_idx(a)]
1116

1114

1117
        if (not spr:isenemy() or spr.extra > 0) then
1115
        if (not spr:isenemy() or spr.extra > 0) then
1118
            if (bit.band(a.movflags, 8) ~= 0) then  -- random_angle
1116
            if (bit.band(a.movflags, 8) ~= 0) then  -- random_angle
1119
                spr.ang = bit.band(ffiC.krand(), 2047)
1117
                spr.ang = bit.band(ffiC.krand(), 2047)
1120
            end
1118
            end
1121
        end
1119
        end
1122
    end,
1120
    end,
1123

1121

1124
    has_move = function(a, mov)
1122
    has_move = function(a, mov)
1125
        a = ffi.cast(actor_ptr_ct, a)
1123
        a = ffi.cast(actor_ptr_ct, a)
1126

1124

1127
        if (ffi.istype(con_move_ct, mov)) then
1125
        if (ffi.istype(con_move_ct, mov)) then
1128
            return (a.t_data[1]==mov.id)
1126
            return (a.t_data[1]==mov.id)
1129
        else
1127
        else
1130
            check_literal_am(mov, "move")
1128
            check_literal_am(mov, "move")
1131
            return (a.t_data[1]==mov)
1129
            return (a.t_data[1]==mov)
1132
        end
1130
        end
1133
    end,
1131
    end,
1134

1132

1135
    -- Override velocity, keeping move ID.
1133
    -- Override velocity, keeping move ID.
1136
    set_hvel = function(a, hvel)
1134
    set_hvel = function(a, hvel)
1137
        ffi.cast(actor_ptr_ct, a).mv.hvel = hvel
1135
        ffi.cast(actor_ptr_ct, a).mv.hvel = hvel
1138
    end,
1136
    end,
1139

1137

1140
    set_vvel = function(a, vvel)
1138
    set_vvel = function(a, vvel)
1141
        ffi.cast(actor_ptr_ct, a).mv.vvel = vvel
1139
        ffi.cast(actor_ptr_ct, a).mv.vvel = vvel
1142
    end,
1140
    end,
1143

1141

1144
    -- ai
1142
    -- ai
1145
    set_ai = function(a, ai)
1143
    set_ai = function(a, ai)
1146
        local oa = a
1144
        local oa = a
1147
        a = ffi.cast(actor_ptr_ct, a)
1145
        a = ffi.cast(actor_ptr_ct, a)
1148

1146

1149
        -- TODO: literal number AIs?
1147
        -- TODO: literal number AIs?
1150
        if (not ffi.istype(con_ai_ct, ai)) then
1148
        if (not ffi.istype(con_ai_ct, ai)) then
1151
            error("bad argument: expected ai", 2)
1149
            error("bad argument: expected ai", 2)
1152
        end
1150
        end
1153

1151

1154
        -- NOTE: compare with gameexec.c, "CON_AI:"
1152
        -- NOTE: compare with gameexec.c, "CON_AI:"
1155
        a.t_data[5] = ai.id
1153
        a.t_data[5] = ai.id
1156

1154

1157
        oa:set_action(ai.act)
1155
        oa:set_action(ai.act)
1158
        oa:set_move(ai.mov, ai.movflags)
1156
        oa:set_move(ai.mov, ai.movflags)
1159

1157

1160
        -- Already reset with set_move():
1158
        -- Already reset with set_move():
1161
--        a.t_data[0] = 0
1159
--        a.t_data[0] = 0
1162
    end,
1160
    end,
1163

1161

1164
    has_ai = function(a, ai)
1162
    has_ai = function(a, ai)
1165
        a = ffi.cast(actor_ptr_ct, a)
1163
        a = ffi.cast(actor_ptr_ct, a)
1166

1164

1167
        if (ffi.istype(con_ai_ct, ai)) then
1165
        if (ffi.istype(con_ai_ct, ai)) then
1168
            return (a.t_data[5]==ai.id)
1166
            return (a.t_data[5]==ai.id)
1169
        else
1167
        else
1170
            check_literal_am(ai, "ai")
1168
            check_literal_am(ai, "ai")
1171
            return (a.t_data[5]==ai)
1169
            return (a.t_data[5]==ai)
1172
        end
1170
        end
1173
    end,
1171
    end,
1174

1172

1175
    -- Getters/setters.
1173
    -- Getters/setters.
1176
    _get_t_data = function(a, idx)
1174
    _get_t_data = function(a, idx)
1177
        if (not (idx >= 0 and idx < 10)) then
1175
        if (not (idx >= 0 and idx < 10)) then
1178
            error("invalid t_data index "..idx, 2)
1176
            error("invalid t_data index "..idx, 2)
1179
        end
1177
        end
1180
        return ffi.cast(actor_ptr_ct, a).t_data[idx]
1178
        return ffi.cast(actor_ptr_ct, a).t_data[idx]
1181
    end,
1179
    end,
1182

1180

1183
    _set_t_data = function(a, idx, val)
1181
    _set_t_data = function(a, idx, val)
1184
        if (not (idx >= 0 and idx < 10)) then
1182
        if (not (idx >= 0 and idx < 10)) then
1185
            error("invalid t_data index "..idx, 2)
1183
            error("invalid t_data index "..idx, 2)
1186
        end
1184
        end
1187
        ffi.cast(actor_ptr_ct, a).t_data[idx] = val
1185
        ffi.cast(actor_ptr_ct, a).t_data[idx] = val
1188
    end,
1186
    end,
1189

1187

1190
    set_picnum = function(a, picnum)
1188
    set_picnum = function(a, picnum)
1191
        if (not (picnum < 0)) then
1189
        if (not (picnum < 0)) then
1192
            check_tile_idx(picnum)
1190
            check_tile_idx(picnum)
1193
        end
1191
        end
1194
        ffi.cast(actor_ptr_ct, a).picnum = picnum
1192
        ffi.cast(actor_ptr_ct, a).picnum = picnum
1195
    end,
1193
    end,
1196

1194

1197
    set_owner = function(a, owner)
1195
    set_owner = function(a, owner)
1198
        -- XXX: is it permissible to set to -1?
1196
        -- XXX: is it permissible to set to -1?
1199
        check_sprite_idx(owner)
1197
        check_sprite_idx(owner)
1200
        ffi.cast(actor_ptr_ct, a).owner = owner
1198
        ffi.cast(actor_ptr_ct, a).owner = owner
1201
    end,
1199
    end,
1202

1200

1203
    --- Custom methods ---
1201
    --- Custom methods ---
1204

1202

1205
    -- Checkers for whether the movement update made the actor hit
1203
    -- Checkers for whether the movement update made the actor hit
1206
    -- something.
1204
    -- something.
1207

1205

1208
    checkhit = function(a)
1206
    checkhit = function(a)
1209
        -- Check whether we hit *anything*, including ceiling/floor.
1207
        -- Check whether we hit *anything*, including ceiling/floor.
1210
        return a._movflagbits:test(49152)
1208
        return a._movflagbits:test(49152)
1211
    end,
1209
    end,
1212

1210

1213
    checkbump = function(a)
1211
    checkbump = function(a)
1214
        -- Check whether we bumped into a wall or sprite.
1212
        -- Check whether we bumped into a wall or sprite.
1215
        return (a._movflagbits:mask(49152) >= 32768)
1213
        return (a._movflagbits:mask(49152) >= 32768)
1216
    end,
1214
    end,
1217

1215

1218
    hitwall = function(a)
1216
    hitwall = function(a)
1219
        if (a._movflagbits:mask(49152) == 32768) then
1217
        if (a._movflagbits:mask(49152) == 32768) then
1220
            return a._movflagbits:mask(32767)
1218
            return a._movflagbits:mask(32767)
1221
        end
1219
        end
1222
    end,
1220
    end,
1223

1221

1224
    hitsprite = function(a)
1222
    hitsprite = function(a)
1225
        if (a._movflagbits:mask(49152) == 49152) then
1223
        if (a._movflagbits:mask(49152) == 49152) then
1226
            return a._movflagbits:mask(32767)
1224
            return a._movflagbits:mask(32767)
1227
        end
1225
        end
1228
    end,
1226
    end,
1229

1227

1230
    -- NOTE: no 'hitsector' or 'hitceiling' / 'hitfloor' for now because
1228
    -- NOTE: no 'hitsector' or 'hitceiling' / 'hitfloor' for now because
1231
    -- more research is needed as to what the best way of checking c/f is.
1229
    -- more research is needed as to what the best way of checking c/f is.
1232
}
1230
}
1233

1231

1234
local actor_mt = {
1232
local actor_mt = {
1235
    __index = function(a, key)
1233
    __index = function(a, key)
1236
        if (actor_methods[key] ~= nil) then
1234
        if (actor_methods[key] ~= nil) then
1237
            return actor_methods[key]
1235
            return actor_methods[key]
1238
        elseif (key == "proj") then
1236
        elseif (key == "proj") then
1239
            return ffiC.SpriteProjectile[get_actor_idx(a)]
1237
            return ffiC.SpriteProjectile[get_actor_idx(a)]
1240
        else
1238
        else
1241
            error("invalid indexing key to actor object", 2)
1239
            error("invalid indexing key to actor object", 2)
1242
        end
1240
        end
1243
    end,
1241
    end,
1244
}
1242
}
1245

1243

1246
ffi.metatype("actor_t", actor_mt)
1244
ffi.metatype("actor_t", actor_mt)
1247

1245

1248

1246

1249
--- PER-PLAYER WEAPON SETTINGS
1247
--- PER-PLAYER WEAPON SETTINGS
1250
local wd_sound_member = {}
1248
local wd_sound_member = {}
1251
for _, declstr in pairs(con_lang.wdata_members) do
1249
for _, declstr in pairs(con_lang.wdata_members) do
1252
    local member = declstr:match("const int32_t _(.*sound)$")
1250
    local member = declstr:match("const int32_t _(.*sound)$")
1253
    if (member) then
1251
    if (member) then
1254
        wd_sound_member[member] = true
1252
        wd_sound_member[member] = true
1255
        if (ffiC._DEBUG_LUNATIC ~= 0) then
1253
        if (ffiC._DEBUG_LUNATIC ~= 0) then
1256
            printf("weapondata_t member %s denotes a sound", member)
1254
            printf("weapondata_t member %s denotes a sound", member)
1257
        end
1255
        end
1258
    end
1256
    end
1259
end
1257
end
1260

1258

1261
local weapondata_mt = {
1259
local weapondata_mt = {
1262
    __index = function(wd, member)
1260
    __index = function(wd, member)
1263
        -- Handle protected members that are renamed (e.g. shoots/_shoots).
1261
        -- Handle protected members that are renamed (e.g. shoots/_shoots).
1264
        return ffi.cast(weapondata_ptr_ct, wd)[0][member]
1262
        return ffi.cast(weapondata_ptr_ct, wd)[0][member]
1265
    end,
1263
    end,
1266

1264

1267
    __newindex = function(wd, member, val)
1265
    __newindex = function(wd, member, val)
1268
        -- Set to 'true' if we set a tile or sound member.
1266
        -- Set to 'true' if we set a tile or sound member.
1269
        local didit = false
1267
        local didit = false
1270

1268

1271
        check_type(member, "string")  -- MEMBER_IS_STRING
1269
        check_type(member, "string")  -- MEMBER_IS_STRING
1272
        check_number(val)
1270
        check_number(val)
1273

1271

1274
        if (wd_sound_member[member]) then  -- XXX: sound2time is a time, not a sound
1272
        if (wd_sound_member[member]) then  -- XXX: sound2time is a time, not a sound
1275
            if (val < 0) then
1273
            if (val < 0) then
1276
                val = 0  -- Set to 0 if negative (e.g. CrackDown).
1274
                val = 0  -- Set to 0 if negative (e.g. CrackDown).
1277
            else
1275
            else
1278
                check_sound_idx(val)
1276
                check_sound_idx(val)
1279
            end
1277
            end
1280
            didit = true
1278
            didit = true
1281
        elseif (member=="workslike") then
1279
        elseif (member=="workslike") then
1282
            check_weapon_idx(val)
1280
            check_weapon_idx(val)
1283
        elseif (member=="spawn" or member=="shoots") then
1281
        elseif (member=="spawn" or member=="shoots") then
1284
            if (val < 0) then
1282
            if (val < 0) then
1285
                -- Set to 0 if oob (e.g. CrackDown). This is a bit problematic
1283
                -- Set to 0 if oob (e.g. CrackDown). This is a bit problematic
1286
                -- for .shoots because it's used unconditionally except in one
1284
                -- for .shoots because it's used unconditionally except in one
1287
                -- case (see player.c).
1285
                -- case (see player.c).
1288
                val = 0
1286
                val = 0
1289
            else
1287
            else
1290
                check_tile_idx(val)
1288
                check_tile_idx(val)
1291
            end
1289
            end
1292
            didit = true
1290
            didit = true
1293
        end
1291
        end
1294
1292
1295
        -- DEBUG:
1293
        -- DEBUG:
1296
--        printf("assigning %s to weapon's %s", tostring(val), member)
1294
--        printf("assigning %s to weapon's %s", tostring(val), member)
1297
1295
1298
        -- NOTE: we're indexing a *pointer* with the user-supplied 'member',
1296
        -- NOTE: we're indexing a *pointer* with the user-supplied 'member',
1299
        -- which could be dangerouns if it could be a number. However, we have
1297
        -- which could be dangerouns if it could be a number. However, we have
1300
        -- assured that is is not in MEMBER_IS_STRING above.
1298
        -- assured that is is not in MEMBER_IS_STRING above.
1301
        ffi.cast(weapondata_ptr_ct, wd)[member] = val
1299
        ffi.cast(weapondata_ptr_ct, wd)[member] = val
1302
1300
1303
        if (didit and ffiC.g_elCallDepth==0) then
1301
        if (didit and ffiC.g_elCallDepth==0) then
1304
            -- Signal that we overrode this member at CON translation time.
1302
            -- Signal that we overrode this member at CON translation time.
1305
1303
1306
            -- Get weapon index as pointer difference first. PLAYER_0.
1304
            -- Get weapon index as pointer difference first. PLAYER_0.
1307
            local wi = ffi.cast(weapondata_ptr_ct, wd)
1305
            local wi = ffi.cast(weapondata_ptr_ct, wd)
1308
                     - ffi.cast(weapondata_ptr_ct, ffiC.g_playerWeapon)
1306
                     - ffi.cast(weapondata_ptr_ct, ffiC.g_playerWeapon)
1309
            assert(wi >= 0 and wi < ffiC.MAX_WEAPONS)
1307
            assert(wi >= 0 and wi < ffiC.MAX_WEAPONS)
1310
1308
1311
            -- Set g_weaponOverridden[wi][member], but without invoking
1309
            -- Set g_weaponOverridden[wi][member], but without invoking
1312
            -- weapondata_t's __newindex metamethod (i.e. us)!
1310
            -- weapondata_t's __newindex metamethod (i.e. us)!
1313
            ffi.cast(weapondata_ptr_ct, ffiC.g_weaponOverridden[wi])[member] = 1
1311
            ffi.cast(weapondata_ptr_ct, ffiC.g_weaponOverridden[wi])[member] = 1
1314
        end
1312
        end
1315
    end,
1313
    end,
1316
}
1314
}
1317
ffi.metatype("weapondata_t", weapondata_mt)
1315
ffi.metatype("weapondata_t", weapondata_mt)
1318
1316
1319
local weaponaccess_mt = {
1317
local weaponaccess_mt = {
1320
    -- Syntax like "player[0].weapon.KNEE.shoots" possible because
1318
    -- Syntax like "player[0].weapon.KNEE.shoots" possible because
1321
    -- g_playerWeapon[] is declared as an array of corresponding bcarray types
1319
    -- g_playerWeapon[] is declared as an array of corresponding bcarray types
1322
    -- for us.
1320
    -- for us.
1323
    __index = function(wa, key)
1321
    __index = function(wa, key)
1324
        if (type(key)~="number" and type(key)~="string") then
1322
        if (type(key)~="number" and type(key)~="string") then
1325
            error("must access weapon either by number or by name")
1323
            error("must access weapon either by number or by name")
1326
        end
1324
        end
1327
1325
1328
        return ffiC.g_playerWeapon[wa._p][key]
1326
        return ffiC.g_playerWeapon[wa._p][key]
1329
    end,
1327
    end,
1330
}
1328
}
1331
ffi.metatype("weaponaccess_t", weaponaccess_mt)
1329
ffi.metatype("weaponaccess_t", weaponaccess_mt)
1332
1330
1333
1331
1334
local function clamp(num, min, max)
1332
local function clamp(num, min, max)
1335
    return num < min and min
1333
    return num < min and min
1336
        or num > max and max
1334
        or num > max and max
1337
        or num
1335
        or num
1338
end
1336
end
1339
1337
1340
local function clamp0to1(num)
1338
local function clamp0to1(num)
1341
    check_number(num, 4)
1339
    check_number(num, 4)
1342
    return clamp(num, 0, 1)
1340
    return clamp(num, 0, 1)
1343
end
1341
end
1344
1342
1345
local player_methods = {
1343
local player_methods = {
1346
    -- CON-like addammo/addweapon, but without the non-local control flow
1344
    -- CON-like addammo/addweapon, but without the non-local control flow
1347
    -- (returns true if weapon's ammo was at the max. instead).
1345
    -- (returns true if weapon's ammo was at the max. instead).
1348
    addammo = con._addammo,
1346
    addammo = con._addammo,
1349
    addweapon = con._addweapon,
1347
    addweapon = con._addweapon,
1350
1348
1351
    stomp = con._pstomp,
1349
    stomp = con._pstomp,
1352
1350
1353
    holdskey = function(p, keyname)
1351
    holdskey = function(p, keyname)
1354
        -- XXX: on invalid <keyname>, error will point to this next line:
1352
        -- XXX: on invalid <keyname>, error will point to this next line:
1355
        return player_holdskey(p.weapon._p, keyname)
1353
        return player_holdskey(p.weapon._p, keyname)
1356
    end,
1354
    end,
1357
1355
1358
    has_weapon = function(p, weap)
1356
    has_weapon = function(p, weap)
1359
        return p.gotweaponbits:test(lsh(1,weap))
1357
        return p.gotweaponbits:test(lsh(1,weap))
1360
    end,
1358
    end,
1361
1359
1362
    give_weapon = function(p, weap)
1360
    give_weapon = function(p, weap)
1363
        p.gotweaponbits:set(lsh(1,weap))
1361
        p.gotweaponbits:set(lsh(1,weap))
1364
    end,
1362
    end,
1365
1363
1366
    take_weapon = function(p, weap)
1364
    take_weapon = function(p, weap)
1367
        p.gotweaponbits:clear(lsh(1,weap))
1365
        p.gotweaponbits:clear(lsh(1,weap))
1368
    end,
1366
    end,
1369
1367
1370
    -- Give or take weapon, for implementation of CON's .gotweapon access.
1368
    -- Give or take weapon, for implementation of CON's .gotweapon access.
1371
    _gt_weapon = function(p, weap, give_p)
1369
    _gt_weapon = function(p, weap, give_p)
1372
        if (give_p ~= 0) then
1370
        if (give_p ~= 0) then
1373
            p:give_weapon(weap)
1371
            p:give_weapon(weap)
1374
        else
1372
        else
1375
            p:take_weapon(weap)
1373
            p:take_weapon(weap)
1376
        end
1374
        end
1377
    end,
1375
    end,
1378
1376
1379
    whack = function(p, no_return_to_center)
1377
    whack = function(p, no_return_to_center)
1380
        p.horiz = p.horiz + 64
1378
        p.horiz = p.horiz + 64
1381
        if (not no_return_to_center) then
1379
        if (not no_return_to_center) then
1382
            p.return_to_center = 9
1380
            p.return_to_center = 9
1383
        end
1381
        end
1384
        local n = bit.arshift(128-band(ffiC.krand(),255), 1)
1382
        local n = bit.arshift(128-band(ffiC.krand(),255), 1)
1385
        p.rotscrnang = n
1383
        p.rotscrnang = n
1386
        p.look_ang = n
1384
        p.look_ang = n
1387
    end,
1385
    end,
1388
1386
1389
    -- External, improved 'palfrom'.
1387
    -- External, improved 'palfrom'.
1390
    -- <speed>: possibly fractional speed of tint fading, in pals.f decrements per gametic.
1388
    -- <speed>: possibly fractional speed of tint fading, in pals.f decrements per gametic.
1391
    --          XXX: exposes internals.
1389
    --          XXX: exposes internals.
1392
    -- <prio>: a value from -128 to 127, higher ones trump lower or equal ones
1390
    -- <prio>: a value from -128 to 127, higher ones trump lower or equal ones
1393
    fadecol = function(p, fadefrac, r, g, b, speed, prio)
1391
    fadecol = function(p, fadefrac, r, g, b, speed, prio)
1394
        -- Validate inargs: clamp f,r,g,b to [0 .. 1] first and multiply by
1392
        -- Validate inargs: clamp f,r,g,b to [0 .. 1] first and multiply by
1395
        -- 63 for the internal handling.
1393
        -- 63 for the internal handling.
1396
        fadefrac = clamp0to1(fadefrac)*63
1394
        fadefrac = clamp0to1(fadefrac)*63
1397
        -- NOTE: a fadefrac of now <1 is allowed, e.g. for clearing the tint.
1395
        -- NOTE: a fadefrac of now <1 is allowed, e.g. for clearing the tint.
1398
        r = clamp0to1(r)*63
1396
        r = clamp0to1(r)*63
1399
        g = clamp0to1(g)*63
1397
        g = clamp0to1(g)*63
1400
        b = clamp0to1(b)*63
1398
        b = clamp0to1(b)*63
1401
1399
1402
        if (speed ~= nil) then
1400
        if (speed ~= nil) then
1403
            check_number(speed)
1401
            check_number(speed)
1404
            -- Clamp to sensible values; the speed is stored in an int8_t
1402
            -- Clamp to sensible values; the speed is stored in an int8_t
1405
            -- (see below).
1403
            -- (see below).
1406
            speed = clamp(speed, 1/128, 127)
1404
            speed = clamp(speed, 1/128, 127)
1407
        else
1405
        else
1408
            speed = 1
1406
            speed = 1
1409
        end
1407
        end
1410
1408
1411
        if (prio ~= nil) then
1409
        if (prio ~= nil) then
1412
            check_number(prio)
1410
            check_number(prio)
1413
1411
1414
            if (not (prio >= -128 and prio < 127)) then
1412
            if (not (prio >= -128 and prio < 127)) then
1415
                error("invalid argument #6 (priority): must be in [-128 .. 127]", 2)
1413
                error("invalid argument #6 (priority): must be in [-128 .. 127]", 2)
1416
            end
1414
            end
1417
        else
1415
        else
1418
            prio = 0
1416
            prio = 0
1419
        end
1417
        end
1420
1418
1421
        -- Check if a currently active tint has higher priority.
1419
        -- Check if a currently active tint has higher priority.
1422
        if (p._pals.f > 0 and p._palsfadeprio > prio) then
1420
        if (p._pals.f > 0 and p._palsfadeprio > prio) then
1423
            return
1421
            return
1424
        end
1422
        end
1425
1423
1426
        -- The passed tint can be applied now.
1424
        -- The passed tint can be applied now.
1427
        p:_palfrom(fadefrac, r, g, b)
1425
        p:_palfrom(fadefrac, r, g, b)
1428
        p._palsfadeprio = prio
1426
        p._palsfadeprio = prio
1429
1427
1430
        -- Calculate .palsfade{speed,next}
1428
        -- Calculate .palsfade{speed,next}
1431
        if (speed >= 1) then
1429
        if (speed >= 1) then
1432
            -- Will round to the nearest integer ("banker's
1430
            -- Will round to the nearest integer ("banker's
1433
            -- rounding"). NOTE: This is not correct for all numbers, see
1431
            -- rounding"). NOTE: This is not correct for all numbers, see
1434
            -- http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
1432
            -- http://blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1
1435
            p._palsfadespeed = speed + 0.5
1433
            p._palsfadespeed = speed + 0.5
1436
            p._palsfadenext = 0
1434
            p._palsfadenext = 0
1437
        else
1435
        else
1438
            -- NOTE: Values that round to 0 have are equivalent behavior to
1436
            -- NOTE: Values that round to 0 have are equivalent behavior to
1439
            -- passing a <speed> of 1.
1437
            -- passing a <speed> of 1.
1440
            local negspeedrecip = -((1/speed) + 0.5)  -- [-128.5 .. 1/127+0.5]
1438
            local negspeedrecip = -((1/speed) + 0.5)  -- [-128.5 .. 1/127+0.5]
1441
            p._palsfadespeed = negspeedrecip
1439
            p._palsfadespeed = negspeedrecip
1442
            p._palsfadenext = negspeedrecip
1440
            p._palsfadenext = negspeedrecip
1443
        end
1441
        end
1444
    end,
1442
    end,
1445
1443
1446
    -- INTERNAL and CON-only.
1444
    -- INTERNAL and CON-only.
1447
    _palfrom = function(p, f, r,g,b)
1445
    _palfrom = function(p, f, r,g,b)
1448
        local pals = p._pals
1446
        local pals = p._pals
1449
        -- Assume that CON's palfrom starts with prio 0 and speed 0.
1447
        -- Assume that CON's palfrom starts with prio 0 and speed 0.
1450
        if (pals.f == 0 or p._palsfadeprio <= 0) then
1448
        if (pals.f == 0 or p._palsfadeprio <= 0) then
1451
            pals.f = f
1449
            pals.f = f
1452
            pals.r, pals.g, pals.b = r, g, b
1450
            pals.r, pals.g, pals.b = r, g, b
1453
            p._palsfadespeed, p._palsfadenext = 0, 0
1451
            p._palsfadespeed, p._palsfadenext = 0, 0
1454
        end
1452
        end
1455
    end,
1453
    end,
1456
}
1454
}
1457
1455
1458
local player_mt = {
1456
local player_mt = {
1459
    __index = function(p, key)
1457
    __index = function(p, key)
1460
        if (player_methods[key] ~= nil) then
1458
        if (player_methods[key] ~= nil) then
1461
            return player_methods[key]
1459
            return player_methods[key]
1462
        elseif (key == "_input") then
1460
        elseif (key == "_input") then
1463
            return ffiC.g_player[p.weapon._p].sync[0]
1461
            return ffiC.g_player[p.weapon._p].sync[0]
1464
        else
1462
        else
1465
            -- Read access to protected player members.
1463
            -- Read access to protected player members.
1466
            return ffi.cast(player_ptr_ct, p)[0][key]
1464
            return ffi.cast(player_ptr_ct, p)[0][key]
1467
        end
1465
        end
1468
    end,
1466
    end,
1469
1467
1470
    __newindex = function(p, key, val)
1468
    __newindex = function(p, key, val)
1471
        -- Write access to protected player members.
1469
        -- Write access to protected player members.
1472
1470
1473
        local allowneg = DukePlayer_prot_allowneg[key]
1471
        local allowneg = DukePlayer_prot_allowneg[key]
1474
        assert(type(allowneg)=="boolean")
1472
        assert(type(allowneg)=="boolean")
1475
1473
1476
        if (allowneg==false or not (val == -1)) then
1474
        if (allowneg==false or not (val == -1)) then
1477
            DukePlayer_prot_chkfunc[key](val)
1475
            DukePlayer_prot_chkfunc[key](val)
1478
        end
1476
        end
1479
        ffi.cast(player_ptr_ct, p)[key] = val
1477
        ffi.cast(player_ptr_ct, p)[key] = val
1480
    end,
1478
    end,
1481
}
1479
}
1482
1480
1483
ffi.metatype("DukePlayer_t", player_mt)
1481
ffi.metatype("DukePlayer_t", player_mt)
1484
1482
1485
local function GenProjectileSetFunc(Member, checkfunc)
1483
local function GenProjectileSetFunc(Member, checkfunc)
1486
    return function(self, idx)
1484
    return function(self, idx)
1487
        if (not (idx == -1)) then
1485
        if (not (idx == -1)) then
1488
            checkfunc(idx)
1486
            checkfunc(idx)
1489
        end
1487
        end
1490
        ffi.cast(projectile_ptr_ct, self)[Member] = idx
1488
        ffi.cast(projectile_ptr_ct, self)[Member] = idx
1491
    end
1489
    end
1492
end
1490
end
1493
1491
1494
local projectile_mt = {
1492
local projectile_mt = {
1495
    __index = {
1493
    __index = {
1496
        set_spawns = GenProjectileSetFunc("spawns", check_tile_idx),
1494
        set_spawns = GenProjectileSetFunc("spawns", check_tile_idx),
1497
        set_decal = GenProjectileSetFunc("decal", check_tile_idx),
1495
        set_decal = GenProjectileSetFunc("decal", check_tile_idx),
1498
        set_trail = GenProjectileSetFunc("trail", check_tile_idx),
1496
        set_trail = GenProjectileSetFunc("trail", check_tile_idx),
1499
1497
1500
        set_sound = GenProjectileSetFunc("sound", check_sound_idx),
1498
        set_sound = GenProjectileSetFunc("sound", check_sound_idx),
1501
        set_isound = GenProjectileSetFunc("isound", check_sound_idx),
1499
        set_isound = GenProjectileSetFunc("isound", check_sound_idx),
1502
        set_bsound = GenProjectileSetFunc("bsound", check_sound_idx),
1500
        set_bsound = GenProjectileSetFunc("bsound", check_sound_idx),
1503
    },
1501
    },
1504
}
1502
}
1505
ffi.metatype("projectile_t", projectile_mt)
1503
ffi.metatype("projectile_t", projectile_mt)
1506
1504
1507
local user_defs_mt = {
1505
local user_defs_mt = {
1508
    __index = {
1506
    __index = {
1509
        set_screen_size = function(ud, screen_size)
1507
        set_screen_size = function(ud, screen_size)
1510
            if (ud.screen_size ~= screen_size) then
1508
            if (ud.screen_size ~= screen_size) then
1511
                ud.screen_size = screen_size
1509
                ud.screen_size = screen_size
1512
                ffiC.G_UpdateScreenArea()
1510
                ffiC.G_UpdateScreenArea()
1513
            end
1511
            end
1514
        end,
1512
        end,
1515
1513
1516
        set_volume_number = function(ud, volume_number)
1514
        set_volume_number = function(ud, volume_number)
1517
            -- NOTE: allow volume_number==MAXVOLUMES.
1515
            -- NOTE: allow volume_number==MAXVOLUMES.
1518
            if (not (volume_number==con_lang.MAXVOLUMES)) then
1516
            if (not (volume_number==con_lang.MAXVOLUMES)) then
1519
                bcheck.volume_idx(volume_number)
1517
                bcheck.volume_idx(volume_number)
1520
            end
1518
            end
1521
            ud.volume_number = volume_number
1519
            ud.volume_number = volume_number
1522
        end,
1520
        end,
1523
1521
1524
        set_m_volume_number = function(ud, volume_number)
1522
        set_m_volume_number = function(ud, volume_number)
1525
            -- NOTE: allow volume_number==MAXVOLUMES.
1523
            -- NOTE: allow volume_number==MAXVOLUMES.
1526
            if (not (volume_number==con_lang.MAXVOLUMES)) then
1524
            if (not (volume_number==con_lang.MAXVOLUMES)) then
1527
                bcheck.volume_idx(volume_number)
1525
                bcheck.volume_idx(volume_number)
1528
            end
1526
            end
1529
            ud.m_volume_number = volume_number
1527
            ud.m_volume_number = volume_number
1530
        end,
1528
        end,
1531
1529
1532
        set_level_number = function(ud, level_number)
1530
        set_level_number = function(ud, level_number)
1533
            bcheck.level_idx(level_number)
1531
            bcheck.level_idx(level_number)
1534
            ud.level_number = level_number
1532
            ud.level_number = level_number
1535
        end,
1533
        end,
1536
    },
1534
    },
1537
}
1535
}
1538
ffi.metatype("user_defs", user_defs_mt)
1536
ffi.metatype("user_defs", user_defs_mt)
1539
1537
1540
--- CUSTOM "gv" VARIABLES
1538
--- CUSTOM "gv" VARIABLES
1541
local camera_mt = {
1539
local camera_mt = {
1542
    -- TODO: "set position" method, which also updates the sectnum
1540
    -- TODO: "set position" method, which also updates the sectnum
1543
    __index = ffiC.g_camera,
1541
    __index = ffiC.g_camera,
1544
    __newindex = function(_, key, val)
1542
    __newindex = function(_, key, val)
1545
        if (key=="sect") then
1543
        if (key=="sect") then
1546
            check_sector_idx(val)
1544
            check_sector_idx(val)
1547
        end
1545
        end
1548
        ffiC.g_camera[key] = val
1546
        ffiC.g_camera[key] = val
1549
    end,
1547
    end,
1550
}
1548
}
1551
1549
1552
gv_access.cam = setmtonce({}, camera_mt)
1550
gv_access.cam = setmtonce({}, camera_mt)
1553
gv_access._ud = ffiC.ud
1551
gv_access._ud = ffiC.ud
1554
1552
1555
-- Support for some CON global system gamevars. RETURN handled separately.
1553
-- Support for some CON global system gamevars. RETURN handled separately.
1556
gv_access._csv = ffi.new "struct { int32_t LOTAG, HITAG, TEXTURE; }"
1554
gv_access._csv = ffi.new "struct { int32_t LOTAG, HITAG, TEXTURE; }"
1557
1555
1558
gv_access.REND = defs_c.conststruct
1556
gv_access.REND = defs_c.conststruct
1559
{
1557
{
1560
    CLASSIC = 0,
1558
    CLASSIC = 0,
1561
    POLYMOST = 3,
1559
    POLYMOST = 3,
1562
    POLYMER = 4,
1560
    POLYMER = 4,
1563
}
1561
}
1564
1562
1565
gv_access.WEAPON = lprivate.WEAPON
1563
gv_access.WEAPON = lprivate.WEAPON
1566
gv_access.GET = lprivate.GET
1564
gv_access.GET = lprivate.GET
1567
1565
1568
function gv_access._get_yxaspect()
1566
function gv_access._get_yxaspect()
1569
    return ffiC.yxaspect
1567
    return ffiC.yxaspect
1570
end
1568
end
1571
1569
1572
function gv_access._get_viewingrange()
1570
function gv_access._get_viewingrange()
1573
    return ffiC.viewingrange
1571
    return ffiC.viewingrange
1574
end
1572
end
1575
1573
1576
function gv_access._currentFramerate()
1574
function gv_access._currentFramerate()
1577
    return ffiC.g_frameRate
1575
    return ffiC.g_frameRate
1578
end
1576
end
1579
1577
1580
function gv_access._currentMenu()
1578
function gv_access._currentMenu()
1581
    return ffiC.g_currentMenu
1579
    return ffiC.g_currentMenu
1582
end
1580
end
1583
1581
1584
function gv_access._changeMenu(cm)
1582
function gv_access._changeMenu(cm)
1585
    ffiC.Menu_Change(cm)
1583
    ffiC.Menu_Change(cm)
1586
end
1584
end
1587
1585
1588
function gv_access._set_guniqhudid(id)
1586
function gv_access._set_guniqhudid(id)
1589
    local MAXUNIQHUDID = 256  -- KEEPINSYNC build.h
1587
    local MAXUNIQHUDID = 256  -- KEEPINSYNC build.h
1590
    if (not (id >= 0 and id < MAXUNIQHUDID)) then
1588
    if (not (id >= 0 and id < MAXUNIQHUDID)) then
1591
        error("invalid unique HUD ID "..id)
1589
        error("invalid unique HUD ID "..id)
1592
    end
1590
    end
1593
    ffiC.guniqhudid = id
1591
    ffiC.guniqhudid = id
1594
end
1592
end
1595
1593
1596
function gv_access.currentEpisode()
1594
function gv_access.currentEpisode()
1597
    return ffiC.ud.volume_number + 1
1595
    return ffiC.ud.volume_number + 1
1598
end
1596
end
1599
1597
1600
function gv_access.currentLevel()
1598
function gv_access.currentLevel()
1601
    return ffiC.ud.level_number + 1
1599
    return ffiC.ud.level_number + 1
1602
end
1600
end
1603
1601
1604
function gv_access.doQuake(gametics, snd)
1602
function gv_access.doQuake(gametics, snd)
1605
    ffiC.g_earthquakeTime = gametics
1603
    ffiC.g_earthquakeTime = gametics
1606
    if (snd ~= nil) then
1604
    if (snd ~= nil) then
1607
        con._globalsound(ffiC.screenpeek, snd)
1605
        con._globalsound(ffiC.screenpeek, snd)
1608
    end
1606
    end
1609
end
1607
end
1610
1608
1611
-- Declare all con_lang.labels constants in the global FFI namespace.
1609
-- Declare all con_lang.labels constants in the global FFI namespace.
1612
for i=1,#con_lang.labels do
1610
for i=1,#con_lang.labels do
1613
    if (getmetatable(con_lang.labels[i]) ~= "noffiC") then
1611
    if (getmetatable(con_lang.labels[i]) ~= "noffiC") then
1614
        local strbuf = {"enum {"}
1612
        local strbuf = {"enum {"}
1615
        for label, val in pairs(con_lang.labels[i]) do
1613
        for label, val in pairs(con_lang.labels[i]) do
1616
            strbuf[#strbuf+1] = string.format("%s = %d,", label, val)
1614
            strbuf[#strbuf+1] = string.format("%s = %d,", label, val)
1617
        end
1615
        end
1618
        strbuf[#strbuf+1] = "};"
1616
        strbuf[#strbuf+1] = "};"
1619
1617
1620
        ffi.cdef(table.concat(strbuf))
1618
        ffi.cdef(table.concat(strbuf))
1621
    end
1619
    end
1622
end
1620
end
1623
1621
1624
1622
1625
---=== Set up restricted global environment ===---
1623
---=== Set up restricted global environment ===---
1626
1624
1627
local allowed_modules = {
1625
local allowed_modules = {
1628
    coroutine=coroutine, bit=bit, table=table, math=math, string=string,
1626
    coroutine=coroutine, bit=bit, table=table, math=math, string=string,
1629
1627
1630
    os = {
1628
    os = {
1631
        clock = function() return gv_.gethiticks()*0.001 end,
1629
        clock = function() return gv_.gethiticks()*0.001 end,
1632
    },
1630
    },
1633
1631
1634
    randgen = randgen,
1632
    randgen = randgen,
1635
    engine = require("engine"),
1633
    engine = require("engine"),
1636
    stat = require("stat"),
1634
    stat = require("stat"),
1637
    bitar = require("bitar"),
1635
    bitar = require("bitar"),
1638
    xmath = xmath,
1636
    xmath = xmath,
1639
    fs = require("fs"),
1637
    fs = require("fs"),
1640
1638
1641
    con = con,
1639
    con = con,
1642
}
1640
}
1643
1641
1644
do
1642
do
1645
    local ctype_cansave = {}
1643
    local ctype_cansave = {}
1646
1644
1647
    -- Register as "serializeable" the type of cdata object <v>.
1645
    -- Register as "serializeable" the type of cdata object <v>.
1648
    local function reg_serializable_cv(v)
1646
    local function reg_serializable_cv(v)
1649
        assert(type(v)=="cdata")
1647
        assert(type(v)=="cdata")
1650
        assert(type(v._serialize)=="function")
1648
        assert(type(v._serialize)=="function")
1651
        -- NOTE: tonumber() on a ctype cdata object gets its LuaJIT-internal
1649
        -- NOTE: tonumber() on a ctype cdata object gets its LuaJIT-internal
1652
        -- ID, the one that would be shown with tostring(), e.g.
1650
        -- ID, the one that would be shown with tostring(), e.g.
1653
        -- ctype<struct 95>
1651
        -- ctype<struct 95>
1654
        ctype_cansave[tonumber(ffi.typeof(v))] = true
1652
        ctype_cansave[tonumber(ffi.typeof(v))] = true
1655
    end
1653
    end
1656
1654
1657
    function lprivate.cansave_cdata(v)
1655
    function lprivate.cansave_cdata(v)
1658
        return type(v)=="cdata" and ctype_cansave[tonumber(ffi.typeof(v))]
1656
        return type(v)=="cdata" and ctype_cansave[tonumber(ffi.typeof(v))]
1659
    end
1657
    end
1660
1658
1661
    reg_serializable_cv(xmath.vec3())
1659
    reg_serializable_cv(xmath.vec3())
1662
    reg_serializable_cv(ivec3())
1660
    reg_serializable_cv(ivec3())
1663
end
1661
end
1664
1662
1665
-- Protect base modules.
1663
-- Protect base modules.
1666
local function basemod_newidx()
1664
local function basemod_newidx()
1667
    error("modifying base module table forbidden", 2)
1665
    error("modifying base module table forbidden", 2)
1668
end
1666
end
1669
1667
1670
for modname, themodule in pairs(allowed_modules) do
1668
for modname, themodule in pairs(allowed_modules) do
1671
    local mt = {
1669
    local mt = {
1672
        __index = themodule,
1670
        __index = themodule,
1673
        __newindex = basemod_newidx,
1671
        __newindex = basemod_newidx,
1674
    }
1672
    }
1675
1673
1676
    allowed_modules[modname] = setmtonce({}, mt)
1674
    allowed_modules[modname] = setmtonce({}, mt)
1677
end
1675
end
1678
1676
1679
1677
1680
---=== Module stuff ===---
1678
---=== Module stuff ===---
1681
1679
1682
local package_loaded = {}  -- [<modname>] = false/true/table
1680
local package_loaded = {}  -- [<modname>] = false/true/table
1683
local modname_stack = {}  -- [<depth>]=string
1681
local modname_stack = {}  -- [<depth>]=string
1684
local module_gamevars = {}  -- [<modname>] = { <gvname1>, <gvname2>, ... }
1682
local module_gamevars = {}  -- [<modname>] = { <gvname1>, <gvname2>, ... }
1685
local module_gvlocali = {}  -- [<modname>] = { <localidx_beg>, <localidx_end> }
1683
local module_gvlocali = {}  -- [<modname>] = { <localidx_beg>, <localidx_end> }
1686
local module_thread = {}  -- [<modname>] = <module_thread>
1684
local module_thread = {}  -- [<modname>] = <module_thread>
1687
1685
1688
local function getcurmodname(thisfuncname)
1686
local function getcurmodname(thisfuncname)
1689
    if (#modname_stack == 0) then
1687
    if (#modname_stack == 0) then
1690
        error("'"..thisfuncname.."' must be called at the top level of a require'd file", 3)
1688
        error("'"..thisfuncname.."' must be called at the top level of a require'd file", 3)
1691
        -- ... as opposed to "at runtime".
1689
        -- ... as opposed to "at runtime".
1692
    end
1690
    end
1693
1691
1694
    return modname_stack[#modname_stack]
1692
    return modname_stack[#modname_stack]
1695
end
1693
end
1696
1694
1697
1695
1698
local function errorf(level, fmt, ...)
1696
local function errorf(level, fmt, ...)
1699
    local errmsg = string.format(fmt, ...)
1697
    local errmsg = string.format(fmt, ...)
1700
    error(errmsg, level+1)
1698
    error(errmsg, level+1)
1701
end
1699
end
1702
1700
1703
local function readintostr_mod(fn)
1701
local function readintostr_mod(fn)
1704
    -- TODO: g_loadFromGroupOnly?
1702
    -- TODO: g_loadFromGroupOnly?
1705
    local fd = ffiC.kopen4loadfrommod(fn, 0)
1703
    local fd = ffiC.kopen4loadfrommod(fn, 0)
1706
    if (fd < 0) then
1704
    if (fd < 0) then
1707
        return nil
1705
        return nil
1708
    end
1706
    end
1709
1707
1710
    local ret = defs_c.readintostr(fd)
1708
    local ret = defs_c.readintostr(fd)
1711
    ffiC.kclose(fd)
1709
    ffiC.kclose(fd)
1712
    return ret
1710
    return ret
1713
end
1711
end
1714
1712
1715
1713
1716
local debug = require("debug")
1714
local debug = require("debug")
1717
1715
1718
-- Get the number of active locals in the function that calls the function
1716
-- Get the number of active locals in the function that calls the function
1719
-- calling this one.
1717
-- calling this one.
1720
local function getnumlocals(l)
1718
local function getnumlocals(l)
1721
    -- 200 is the max. number of locals at one level
1719
    -- 200 is the max. number of locals at one level
1722
    for i=1,200 do
1720
    for i=1,200 do
1723
        -- level:
1721
        -- level:
1724
        -- 0 is debug.getlocal() itself.
1722
        -- 0 is debug.getlocal() itself.
1725
        -- 1 is this function (getnumlocals).
1723
        -- 1 is this function (getnumlocals).
1726
        -- 2 is the function calling getnumlocals()
1724
        -- 2 is the function calling getnumlocals()
1727
        -- 3 is the function calling that one.
1725
        -- 3 is the function calling that one.
1728
        if (debug.getlocal(3, i) == nil) then
1726
        if (debug.getlocal(3, i) == nil) then
1729
            return i-1
1727
            return i-1
1730
        end
1728
        end
1731
    end
1729
    end
1732
end
1730
end
1733
1731
1734
local function error_on_nil_read(_, varname)
1732
local function error_on_nil_read(_, varname)
1735
    error("attempt to read nil variable '"..varname.."'", 2)
1733
    error("attempt to read nil variable '"..varname.."'", 2)
1736
end
1734
end
1737
1735
1738
local required_module_mt = {
1736
local required_module_mt = {
1739
    __index = error_on_nil_read,
1737
    __index = error_on_nil_read,
1740
1738
1741
    __newindex = function()
1739
    __newindex = function()
1742
        error("modifying module table forbidden", 2)
1740
        error("modifying module table forbidden", 2)
1743
    end,
1741
    end,
1744
1742
1745
    __metatable = true,
1743
    __metatable = true,
1746
}
1744
}
1747
1745
1748
-- Will contain a function to restore gamevars when running from savegame
1746
-- Will contain a function to restore gamevars when running from savegame
1749
-- restoration. See SAVEFUNC_ARGS for its arguments.
1747
-- restoration. See SAVEFUNC_ARGS for its arguments.
1750
local g_restorefunc = nil
1748
local g_restorefunc = nil
1751
1749
1752
-- Local gamevar restoration function run from
1750
-- Local gamevar restoration function run from
1753
-- our_require('end_gamevars') <- [user module].
1751
-- our_require('end_gamevars') <- [user module].
1754
local function restore_local(li, lval)
1752
local function restore_local(li, lval)
1755
    -- level:
1753
    -- level:
1756
    -- 0 is getlocal() itself.
1754
    -- 0 is getlocal() itself.
1757
    -- 1 is this function (restore_local).
1755
    -- 1 is this function (restore_local).
1758
    -- 2 is the function calling restore_local(), the savecode.
1756
    -- 2 is the function calling restore_local(), the savecode.
1759
    -- 3 is the function calling the savecode, our_require.
1757
    -- 3 is the function calling the savecode, our_require.
1760
    -- 4 is the function calling our_require, the module function.
1758
    -- 4 is the function calling our_require, the module function.
1761
    if (ffiC._DEBUG_LUNATIC ~= 0) then
1759
    if (ffiC._DEBUG_LUNATIC ~= 0) then
1762
        printf("Restoring index #%d (%s) with value %s",
1760
        printf("Restoring index #%d (%s) with value %s",
1763
               li, debug.getlocal(4, li), tostring(lval))
1761
               li, debug.getlocal(4, li), tostring(lval))
1764
    end
1762
    end
1765
1763
1766
    assert(debug.setlocal(4, li, lval))
1764
    assert(debug.setlocal(4, li, lval))
1767
end
1765
end
1768
1766
1769
-- The "require" function accessible to Lunatic code.
1767
-- The "require" function accessible to Lunatic code.
1770
-- Base modules in allowed_modules are wrapped so that they cannot be
1768
-- Base modules in allowed_modules are wrapped so that they cannot be
1771
-- modified, user modules are searched in the EDuke32 search
1769
-- modified, user modules are searched in the EDuke32 search
1772
-- path.  Also, our require
1770
-- path.  Also, our require
1773
--  * never messes with the global environment, it only returns the module.
1771
--  * never messes with the global environment, it only returns the module.
1774
--  * allows passing varargs beyond the name to the module.
1772
--  * allows passing varargs beyond the name to the module.
1775
local function our_require(modname, ...)
1773
local function our_require(modname, ...)
1776
    local ERRLEV = 5
1774
    local ERRLEV = 5
1777
1775
1778
    -- Check module name is valid first.
1776
    -- Check module name is valid first.
1779
    -- TODO: restrict valid names?
1777
    -- TODO: restrict valid names?
1780
    if (type(modname) ~= "string" or #modname==0) then
1778
    if (type(modname) ~= "string" or #modname==0) then
1781
        error("module name must be a nonempty string", 2)
1779
        error("module name must be a nonempty string", 2)
1782
    end
1780
    end
1783
1781
1784
    -- For _LUNATIC_DBG
1782
    -- For _LUNATIC_DBG
1785
    if (modname:match("^_LUNATIC") and ffiC._DEBUG_LUNATIC == 0) then
1783
    if (modname:match("^_LUNATIC") and ffiC._DEBUG_LUNATIC == 0) then
1786
        return nil
1784
        return nil
1787
    end
1785
    end
1788
1786
1789
    -- Handle the section between module() and require("end_gamevars").
1787
    -- Handle the section between module() and require("end_gamevars").
1790
    if (modname == "end_gamevars") then
1788
    if (modname == "end_gamevars") then
1791
        local thismodname = getcurmodname("require")
1789
        local thismodname = getcurmodname("require")
1792
1790
1793
        if (module_gamevars[thismodname] ~= nil) then
1791
        if (module_gamevars[thismodname] ~= nil) then
1794
            error("\"require 'end_gamevars'\" must be called at most once per require'd file", 2)
1792
            error("\"require 'end_gamevars'\" must be called at most once per require'd file", 2)
1795
        end
1793
        end
1796
1794
1797
        local gvnames = {}
1795
        local gvnames = {}
1798
1796
1799
        for name in pairs(getfenv(2)) do
1797
        for name in pairs(getfenv(2)) do
1800
            gvnames[#gvnames+1] = name
1798
            gvnames[#gvnames+1] = name
1801
            if (ffiC._DEBUG_LUNATIC ~= 0) then
1799
            if (ffiC._DEBUG_LUNATIC ~= 0) then
1802
                printf("MODULE %s GAMEVAR %s", thismodname, name)
1800
                printf("MODULE %s GAMEVAR %s", thismodname, name)
1803
            end
1801
            end
1804
        end
1802
        end
1805
1803
1806
        module_gamevars[thismodname] = gvnames
1804
        module_gamevars[thismodname] = gvnames
1807
        local gvmodi = module_gvlocali[thismodname]
1805
        local gvmodi = module_gvlocali[thismodname]
1808
        gvmodi[2] = getnumlocals()
1806
        gvmodi[2] = getnumlocals()
1809
1807
1810
        if (ffiC._DEBUG_LUNATIC ~= 0) then
1808
        if (ffiC._DEBUG_LUNATIC ~= 0) then
1811
            local numlocals = gvmodi[2]-gvmodi[1]+1
1809
            local numlocals = gvmodi[2]-gvmodi[1]+1
1812
            if (numlocals > 0) then
1810
            if (numlocals > 0) then
1813
                printf("Module '%s' has %d locals, index %d to %d",
1811
                printf("Module '%s' has %d locals, index %d to %d",
1814
                       thismodname, numlocals, gvmodi[1], gvmodi[2])
1812
                       thismodname, numlocals, gvmodi[1], gvmodi[2])
1815
            end
1813
            end
1816
        end
1814
        end
1817
1815
1818
        -- Potentially restore gamevars.
1816
        -- Potentially restore gamevars.
1819
        if (g_restorefunc) then
1817
        if (g_restorefunc) then
1820
            local modtab = package_loaded[thismodname]
1818
            local modtab = package_loaded[thismodname]
1821
            assert(type(modtab)=="table")
1819
            assert(type(modtab)=="table")
1822
            -- SAVEFUNC_ARGS.
1820
            -- SAVEFUNC_ARGS.
1823
            g_restorefunc(thismodname, modtab, restore_local)
1821
            g_restorefunc(thismodname, modtab, restore_local)
1824
        end
1822
        end
1825
1823
1826
        -- Return whether we're NOT running from a savegame restore in the
1824
        -- Return whether we're NOT running from a savegame restore in the
1827
        -- second outarg. (Lunatic-private!)
1825
        -- second outarg. (Lunatic-private!)
1828
        return nil, (g_restorefunc==nil)
1826
        return nil, (g_restorefunc==nil)
1829
    end
1827
    end
1830
1828
1831
    -- See whether it's a base module name.
1829
    -- See whether it's a base module name.
1832
    if (allowed_modules[modname] ~= nil) then
1830
    if (allowed_modules[modname] ~= nil) then
1833
        return allowed_modules[modname]
1831
        return allowed_modules[modname]
1834
    end
1832
    end
1835
1833
1836
    --- Search user modules...
1834
    --- Search user modules...
1837
1835
1838
    if (modname:find("[/\\]")) then
1836
    if (modname:find("[/\\]")) then
1839
        error("Module name must not contain directory separators", ERRLEV-1)
1837
        error("Module name must not contain directory separators", ERRLEV-1)
1840
    end
1838
    end
1841
    -- Instead, dots are translated to directory separators. For EDuke32's
1839
    -- Instead, dots are translated to directory separators. For EDuke32's
1842
    -- virtual file system, this is always a forward slash. Keep the original
1840
    -- virtual file system, this is always a forward slash. Keep the original
1843
    -- module name for passing to the module function.
1841
    -- module name for passing to the module function.
1844
    local omodname = modname
1842
    local omodname = modname
1845
    modname = modname:gsub("%.", "/")
1843
    modname = modname:gsub("%.", "/")
1846
1844
1847
    local omod = package_loaded[modname]
1845
    local omod = package_loaded[modname]
1848
    if (omod ~= nil) then
1846
    if (omod ~= nil) then
1849
        if (omod==false) then
1847
        if (omod==false) then
1850
            error("Loop while loading modules", ERRLEV-1)
1848
            error("Loop while loading modules", ERRLEV-1)
1851
        end
1849
        end
1852
1850
1853
        -- already loaded
1851
        -- already loaded
1854
        assert(omod==true or type(omod)=="table")
1852
        assert(omod==true or type(omod)=="table")
1855
        return omod
1853
        return omod
1856
    end
1854
    end
1857
1855
1858
    local modfn = modname .. ".lua"
1856
    local modfn = modname .. ".lua"
1859
    local str = readintostr_mod(modfn)
1857
    local str = readintostr_mod(modfn)
1860
    if (str == nil) then
1858
    if (str == nil) then
1861
        errorf(ERRLEV-1, "Couldn't open file \"%s\"", modfn)
1859
        errorf(ERRLEV-1, "Couldn't open file \"%s\"", modfn)
1862
    end
1860
    end
1863
1861
1864
    -- Implant code that yields the module thread just before it would return
1862
    -- Implant code that yields the module thread just before it would return
1865
    -- otherwise.
1863
    -- otherwise.
1866
    str = str.."\nrequire('coroutine').yield()"
1864
    str = str.."\nrequire('coroutine').yield()"
1867
1865
1868
    local modfunc, errmsg = loadstring(str, modfn)
1866
    local modfunc, errmsg = loadstring(str, modfn)
1869
    if (modfunc == nil) then
1867
    if (modfunc == nil) then
1870
        errorf(ERRLEV-1, "Couldn't load \"%s\": %s", modname, errmsg)
1868
        errorf(ERRLEV-1, "Couldn't load \"%s\": %s", modname, errmsg)
1871
    end
1869
    end
1872
1870
1873
    package_loaded[modname] = false  -- 'not yet loaded'
1871
    package_loaded[modname] = false  -- 'not yet loaded'
1874
    table.insert(modname_stack, modname)
1872
    table.insert(modname_stack, modname)
1875
1873
1876
    -- Run the module code in a separate Lua thread!
1874
    -- Run the module code in a separate Lua thread!
1877
    local modthread = coroutine.create(modfunc)
1875
    local modthread = coroutine.create(modfunc)
1878
    local ok, retval = coroutine.resume(modthread, omodname, ...)
1876
    local ok, retval = coroutine.resume(modthread, omodname, ...)
1879
1877
1880
    if (not ok) then
1878
    if (not ok) then
1881
        errorf(ERRLEV-1, "Failed running \"%s\": %s\n%s", modname,
1879
        errorf(ERRLEV-1, "Failed running \"%s\": %s\n%s", modname,
1882
               retval, debug.traceback(modthread))
1880
               retval, debug.traceback(modthread))
1883
    end
1881
    end
1884
1882
1885
    table.remove(modname_stack)
1883
    table.remove(modname_stack)
1886
1884
1887
    local modtab = package_loaded[modname]
1885
    local modtab = package_loaded[modname]
1888
1886
1889
    if (type(modtab) ~= "table") then
1887
    if (type(modtab) ~= "table") then
1890
        -- The module didn't call our 'module'. Check if it returned a table.
1888
        -- The module didn't call our 'module'. Check if it returned a table.
1891
        -- In that case, the coroutine has finished its main function and has
1889
        -- In that case, the coroutine has finished its main function and has
1892
        -- not reached our implanted 'yield'.
1890
        -- not reached our implanted 'yield'.
1893
        if (coroutine.status(modthread)=="dead" and type(retval)=="table") then
1891
        if (coroutine.status(modthread)=="dead" and type(retval)=="table") then
1894
            modtab = retval
1892
            modtab = retval
1895
            package_loaded[modname] = modtab
1893
            package_loaded[modname] = modtab
1896
        else
1894
        else
1897
            package_loaded[modname] = true
1895
            package_loaded[modname] = true
1898
        end
1896
        end
1899
    end
1897
    end
1900
1898
1901
    if (type(modtab) == "table") then
1899
    if (type(modtab) == "table") then
1902
        -- Protect module table in any case (i.e. either if the module used our
1900
        -- Protect module table in any case (i.e. either if the module used our
1903
        -- 'module' or if it returned a table).
1901
        -- 'module' or if it returned a table).
1904
        setmetatable(modtab, required_module_mt)
1902
        setmetatable(modtab, required_module_mt)
1905
    end
1903
    end
1906
1904
1907
    local gvmodi = module_gvlocali[modname]
1905
    local gvmodi = module_gvlocali[modname]
1908
1906
1909
    if (gvmodi and gvmodi[2] and gvmodi[2]>=gvmodi[1]) then
1907
    if (gvmodi and gvmodi[2] and gvmodi[2]>=gvmodi[1]) then
1910
        if (coroutine.status(modthread)=="suspended") then
1908
        if (coroutine.status(modthread)=="suspended") then
1911
            -- Save off the suspended thread so that we may get its locals later on.
1909
            -- Save off the suspended thread so that we may get its locals later on.
1912
            -- It is never resumed, but only ever used for debug.getlocal().
1910
            -- It is never resumed, but only ever used for debug.getlocal().
1913
            module_thread[modname] = modthread
1911
            module_thread[modname] = modthread
1914
1912
1915
            if (ffiC._DEBUG_LUNATIC ~= 0) then
1913
            if (ffiC._DEBUG_LUNATIC ~= 0) then
1916
                printf("Keeping coroutine for module \"%s\"", modname)
1914
                printf("Keeping coroutine for module \"%s\"", modname)
1917
            end
1915
            end
1918
        end
1916
        end
1919
    end
1917
    end
1920
1918
1921
    return modtab
1919
    return modtab
1922
end
1920
end
1923
1921
1924
1922
1925
local module_mt = {
1923
local module_mt = {
1926
    __index = error_on_nil_read,
1924
    __index = error_on_nil_read,
1927
}
1925
}
1928
1926
1929
-- Our 'module' replacement doesn't get the module name from the function args
1927
-- Our 'module' replacement doesn't get the module name from the function args
1930
-- since a malicious user could remove other loaded modules this way.
1928
-- since a malicious user could remove other loaded modules this way.
1931
-- Also, our 'module' takes no varargs ("option functions" in Lua).
1929
-- Also, our 'module' takes no varargs ("option functions" in Lua).
1932
-- TODO: make transactional?
1930
-- TODO: make transactional?
1933
local function our_module()
1931
local function our_module()
1934
    if (#modname_stack == 0) then
1932
    if (#modname_stack == 0) then
1935
        error("'module' must be called at the top level of a require'd file", 2)
1933
        error("'module' must be called at the top level of a require'd file", 2)
1936
        -- ... as opposed to "at runtime".
1934
        -- ... as opposed to "at runtime".
1937
    end
1935
    end
1938
1936
1939
    local modname = getcurmodname("module")
1937
    local modname = getcurmodname("module")
1940
1938
1941
    if (package_loaded[modname]) then
1939
    if (package_loaded[modname]) then
1942
        error("'module' must be called at most once per require'd file", 2)
1940
        error("'module' must be called at most once per require'd file", 2)
1943
    end
1941
    end
1944
1942
1945
    local M = setmetatable({}, module_mt)
1943
    local M = setmetatable({}, module_mt)
1946
    package_loaded[modname] = M
1944
    package_loaded[modname] = M
1947
    -- change the environment of the function which called us:
1945
    -- change the environment of the function which called us:
1948
    setfenv(2, M)
1946
    setfenv(2, M)
1949
1947
1950
    module_gvlocali[modname] = { getnumlocals()+1 }
1948
    module_gvlocali[modname] = { getnumlocals()+1 }
1951
end
1949
end
1952
1950
1953
-- overridden 'error' that always passes a string to the base 'error'
1951
-- overridden 'error' that always passes a string to the base 'error'
1954
local function our_error(errmsg, level)
1952
local function our_error(errmsg, level)
1955
    if (type(errmsg) ~= "string") then
1953
    if (type(errmsg) ~= "string") then
1956
        error("error using 'error': error message must be a string", 2)
1954
        error("error using 'error': error message must be a string", 2)
1957
    end
1955
    end
1958
1956
1959
    if (level) then
1957
    if (level) then
1960
        if (type(level) ~= "number") then
1958
        if (type(level) ~= "number") then
1961
            error("error using 'error': error level must be a number", 2)
1959
            error("error using 'error': error level must be a number", 2)
1962
        end
1960
        end
1963
1961
1964
        error(errmsg, level==0 and 0 or level+1)
1962
        error(errmsg, level==0 and 0 or level+1)
1965
    end
1963
    end
1966
1964
1967
    error(errmsg, 2)
1965
    error(errmsg, 2)
1968
end
1966
end
1969
1967
1970
1968
1971
-- _G tweaks -- pull in only 'safe' stuff
1969
-- _G tweaks -- pull in only 'safe' stuff
1972
local G_ = {}  -- our soon-to-be global environment
1970
local G_ = {}  -- our soon-to-be global environment
1973
1971
1974
G_.assert = assert
1972
G_.assert = assert
1975
G_.error = our_error
1973
G_.error = our_error
1976
G_.ipairs = ipairs
1974
G_.ipairs = ipairs
1977
G_.pairs = pairs
1975
G_.pairs = pairs
1978
G_.pcall = pcall
1976
G_.pcall = pcall
1979
G_.print = print
1977
G_.print = print
1980
G_.module = our_module
1978
G_.module = our_module
1981
G_.next = next
1979
G_.next = next
1982
G_.require = our_require
1980
G_.require = our_require
1983
G_.select = select
1981
G_.select = select
1984
G_.tostring = tostring
1982
G_.tostring = tostring
1985
G_.tonumber = tonumber
1983
G_.tonumber = tonumber
1986
G_.type = type
1984
G_.type = type
1987
G_.unpack = unpack
1985
G_.unpack = unpack
1988
G_.xpcall = xpcall
1986
G_.xpcall = xpcall
1989
G_._VERSION = _VERSION
1987
G_._VERSION = _VERSION
1990
1988
1991
-- Available through our 'require':
1989
-- Available through our 'require':
1992
-- bit, coroutine, math, string, table
1990
-- bit, coroutine, math, string, table
1993
1991
1994
-- Not available:
1992
-- Not available:
1995
-- collectgarbage, debug, dofile, gcinfo (DEPRECATED), getfenv, getmetatable,
1993
-- collectgarbage, debug, dofile, gcinfo (DEPRECATED), getfenv, getmetatable,
1996
-- jit, load, loadfile, loadstring, newproxy (NOT STD?), package, rawequal,
1994
-- jit, load, loadfile, loadstring, newproxy (NOT STD?), package, rawequal,
1997
-- rawget, rawset, setfenv, setmetatable
1995
-- rawget, rawset, setfenv, setmetatable
1998
1996
1999
G_._G = G_
1997
G_._G = G_
2000
1998
2001
-- Chain together two functions taking 3 input args.
1999
-- Chain together two functions taking 3 input args.
2002
local function chain_func3(func1, func2)
2000
local function chain_func3(func1, func2)
2003
    if (func1==nil or func2==nil) then
2001
    if (func1==nil or func2==nil) then
2004
        return assert(func1 or func2)
2002
        return assert(func1 or func2)
2005
    end
2003
    end
2006
2004
2007
    -- Return a function that runs <func1> first and then tail-calls <func2>.
2005
    -- Return a function that runs <func1> first and then tail-calls <func2>.
2008
    return function(aci, pli, dist)
2006
    return function(aci, pli, dist)
2009
        func1(aci, pli, dist)
2007
        func1(aci, pli, dist)
2010
        return func2(aci, pli, dist)
2008
        return func2(aci, pli, dist)
2011
    end
2009
    end
2012
end
2010
end
2013
2011
2014
-- Determines the last numeric index of a table using *pairs*, so that in
2012
-- Determines the last numeric index of a table using *pairs*, so that in
2015
-- arg-lists with "holes" (e.g. {1, 2, nil, function() end}) are handled
2013
-- arg-lists with "holes" (e.g. {1, 2, nil, function() end}) are handled
2016
-- properly.
2014
-- properly.
2017
local function ourmaxn(tab)
2015
local function ourmaxn(tab)
2018
    local maxi = 0
2016
    local maxi = 0
2019
    for i in pairs(tab) do
2017
    for i in pairs(tab) do
2020
        if (type(i)=="number") then
2018
        if (type(i)=="number") then
2021
            maxi = math.max(i, maxi)
2019
            maxi = math.max(i, maxi)
2022
        end
2020
        end
2023
    end
2021
    end
2024
    assert(tab[maxi] ~= nil)
2022
    assert(tab[maxi] ~= nil)
2025
    return maxi
2023
    return maxi
2026
end
2024
end
2027
2025
2028
-- Running for the very first time?
2026
-- Running for the very first time?
2029
local g_firstRun = (ffiC.g_elCONSize == 0)
2027
local g_firstRun = (ffiC.g_elCONSize == 0)
2030
2028
2031
-- Actor functions, saved for actor chaining
2029
-- Actor functions, saved for actor chaining
2032
local actor_funcs = {}
2030
local actor_funcs = {}
2033
-- Event functions, saved for event chaining
2031
-- Event functions, saved for event chaining
2034
local event_funcs = {}
2032
local event_funcs = {}
2035
2033
2036
-- Per-actor sprite animation callbacks
2034
-- Per-actor sprite animation callbacks
2037
local animsprite_funcs = {}
2035
local animsprite_funcs = {}
2038
2036
2039
local gameactor_internal = gameactor_internal  -- included in lunatic.c
2037
local gameactor_internal = gameactor_internal  -- included in lunatic.c
2040
local gameevent_internal = gameevent_internal  -- included in lunatic.c
2038
local gameevent_internal = gameevent_internal  -- included in lunatic.c
2041
2039
2042
local function animate_all_sprites()
2040
local function animate_all_sprites()
2043
    for i=0,ffiC.spritesortcnt-1 do
2041
    for i=0,ffiC.spritesortcnt-1 do
2044
        local tspr = ffiC.tsprite[i]
2042
        local tspr = ffiC.tsprite[i]
2045
2043
2046
        if (tspr.owner < ffiC.MAXSPRITES+0ULL) then
2044
        if (tspr.owner < ffiC.MAXSPRITES+0ULL) then
2047
            local spr = tspr:getspr()
2045
            local spr = tspr:getspr()
2048
            local animfunc = animsprite_funcs[spr.picnum]
2046
            local animfunc = animsprite_funcs[spr.picnum]
2049
2047
2050
            if (animfunc) then
2048
            if (animfunc) then
2051
                animfunc(tspr)
2049
                animfunc(tspr)
2052
            end
2050
            end
2053
        end
2051
        end
2054
    end
2052
    end
2055
end
2053
end
2056
2054
2057
2055
2058
local function check_arg_number(name, argpos, val)
2056
local function check_arg_number(name, argpos, val)
2059
    if (type(val) ~= "number") then
2057
    if (type(val) ~= "number") then
2060
        errorf(3, "invalid '%s' argument (#%d) to gameactor: must be a number", name, argpos)
2058
        errorf(3, "invalid '%s' argument (#%d) to gameactor: must be a number", name, argpos)
2061
    end
2059
    end
2062
end
2060
end
2063
2061
2064
-- gameactor{tilenum [, flags [, strength [, action [, move [, movflags]]]]], func}
2062
-- gameactor{tilenum [, flags [, strength [, action [, move [, movflags]]]]], func}
2065
-- Every arg may be positional OR key=val (with the name indicated above as key),
2063
-- Every arg may be positional OR key=val (with the name indicated above as key),
2066
-- but not both.
2064
-- but not both.
2067
local function our_gameactor(args)
2065
local function our_gameactor(args)
2068
    bcheck.top_level("gameactor")
2066
    bcheck.top_level("gameactor")
2069
2067
2070
    if (type(args)~="table") then
2068
    if (type(args)~="table") then
2071
        error("invalid gameactor call: must be passed a table")
2069
        error("invalid gameactor call: must be passed a table")
2072
    end
2070
    end
2073
2071
2074
    local tilenum = args[1]
2072
    local tilenum = args[1]
2075
    if (type(tilenum) ~= "number") then
2073
    if (type(tilenum) ~= "number") then
2076
        error("invalid argument #1 to gameactor: must be a number", 2)
2074
        error("invalid argument #1 to gameactor: must be a number", 2)
2077
    end
2075
    end
2078
    if (not (tilenum >= 0 and tilenum < ffiC.MAXTILES)) then
2076
    if (not (tilenum >= 0 and tilenum < ffiC.MAXTILES)) then
2079
        error("invalid argument #1 to gameactor: must be a tile number [0..gv.MAXTILES-1]", 2)
2077
        error("invalid argument #1 to gameactor: must be a tile number [0..gv.MAXTILES-1]", 2)
2080
    end
2078
    end
2081
2079
2082
    local lastargi = ourmaxn(args)
2080
    local lastargi = ourmaxn(args)
2083
    local func = args[lastargi]
2081
    local func = args[lastargi]
2084
    if (type(func) ~= "function") then
2082
    if (type(func) ~= "function") then
2085
        func = args.func
2083
        func = args.func
2086
        lastargi = 1/0
2084
        lastargi = 1/0
2087
    end
2085
    end
2088
    if (type(func) ~= "function") then
2086
    if (type(func) ~= "function") then
2089
        error("invalid gameactor call: must provide a function with last numeric arg or .func", 2)
2087
        error("invalid gameactor call: must provide a function with last numeric arg or .func", 2)
2090
    end
2088
    end
2091
2089
2092
    local flags = (lastargi > 2 and args[2]) or args.flags or 0
2090
    local flags = (lastargi > 2 and args[2]) or args.flags or 0
2093
    check_arg_number("flags", 2, flags)
2091
    check_arg_number("flags", 2, flags)
2094
2092
2095
    local AF = actor.FLAGS
2093
    local AF = actor.FLAGS
2096
    local chainflags = band(flags, AF._CHAIN_MASK_ACTOR)
2094
    local chainflags = band(flags, AF._CHAIN_MASK_ACTOR)
2097
    flags = band(flags, BNOT.CHAIN_MASK_ACTOR)
2095
    flags = band(flags, BNOT.CHAIN_MASK_ACTOR)
2098
2096
2099
    if (chainflags == 0) then
2097
    if (chainflags == 0) then
2100
        -- Default chaining behavior: don't, replace the old actor instead.
2098
        -- Default chaining behavior: don't, replace the old actor instead.
2101
        chainflags = AF.replace
2099
        chainflags = AF.replace
2102
    elseif (band(chainflags, chainflags-1) ~= 0) then
2100
    elseif (band(chainflags, chainflags-1) ~= 0) then
2103
        error("invalid chaining control flags to gameactor", 2)
2101
        error("invalid chaining control flags to gameactor", 2)
2104
    end
2102
    end
2105
2103
2106
    local replacep = (chainflags==AF.replace)
2104
    local replacep = (chainflags==AF.replace)
2107
    if (not replacep and not actor_funcs[tilenum]) then
2105
    if (not replacep and not actor_funcs[tilenum]) then
2108
        error("attempt to chain code to nonexistent actor tile "..tilenum, 2)
2106
        error("attempt to chain code to nonexistent actor tile "..tilenum, 2)
2109
    end
2107
    end
2110
2108
2111
    local flags_rbits = band(flags, BNOT.USER_MASK)
2109
    local flags_rbits = band(flags, BNOT.USER_MASK)
2112
    if (flags_rbits ~= 0) then
2110
    if (flags_rbits ~= 0) then
2113
        error("invalid 'flags' argument (#2) to gameactor: must not set reserved bits (0x"
2111
        error("invalid 'flags' argument (#2) to gameactor: must not set reserved bits (0x"
2114
              ..(bit.tohex(flags_rbits))..")", 2)
2112
              ..(bit.tohex(flags_rbits))..")", 2)
2115
    end
2113
    end
2116
2114
2117
    local strength = ((lastargi > 3 and args[3]) or args.strength) or (replacep and 0 or nil)
2115
    local strength = ((lastargi > 3 and args[3]) or args.strength) or (replacep and 0 or nil)
2118
    if (replacep or strength~=nil) then
2116
    if (replacep or strength~=nil) then
2119
        check_arg_number("strength", 3, strength)
2117
        check_arg_number("strength", 3, strength)
2120
    end
2118
    end
2121
2119
2122
    local act = ((lastargi > 4 and args[4]) or args.action) or (replacep and literal_act[0] or nil)
2120
    local act = ((lastargi > 4 and args[4]) or args.action) or (replacep and literal_act[0] or nil)
2123
    if (replacep or act ~= nil) then
2121
    if (replacep or act ~= nil) then
2124
        if (type(act)=="number" and (act==0 or act==1)) then
2122
        if (type(act)=="number" and (act==0 or act==1)) then
2125
            act = literal_act[act]
2123
            act = literal_act[act]
2126
        elseif (not ffi.istype(con_action_ct, act)) then
2124
        elseif (not ffi.istype(con_action_ct, act)) then
2127
            error("invalid 'action' argument (#4) to gameactor: must be an action", 2)
2125
            error("invalid 'action' argument (#4) to gameactor: must be an action", 2)
2128
        end
2126
        end
2129
    end
2127
    end
2130
2128
2131
    local mov = ((lastargi > 5 and args[5]) or args.move) or (replacep and literal_mov[0] or nil)
2129
    local mov = ((lastargi > 5 and args[5]) or args.move) or (replacep and literal_mov[0] or nil)
2132
    if (replacep or mov ~= nil) then
2130
    if (replacep or mov ~= nil) then
2133
        if (type(mov)=="number" and (mov==0 or mov==1)) then
2131
        if (type(mov)=="number" and (mov==0 or mov==1)) then
2134
            mov = literal_mov[mov]
2132
            mov = literal_mov[mov]
2135
        elseif (not ffi.istype(con_move_ct, mov)) then
2133
        elseif (not ffi.istype(con_move_ct, mov)) then
2136
            error("invalid 'move' argument (#5) to gameactor: must be a move", 2)
2134
            error("invalid 'move' argument (#5) to gameactor: must be a move", 2)
2137
        end
2135
        end
2138
    end
2136
    end
2139
2137
2140
    local movflags = ((lastargi > 6 and args[6]) or args.movflags) or (replacep and 0 or nil)
2138
    local movflags = ((lastargi > 6 and args[6]) or args.movflags) or (replacep and 0 or nil)
2141
    if (replacep or movflags ~= nil) then
2139
    if (replacep or movflags ~= nil) then
2142
        check_arg_number("movflags", 6, movflags)
2140
        check_arg_number("movflags", 6, movflags)
2143
    end
2141
    end
2144
2142
2145
    -- Register a potentially passed drawn sprite animation callback function.
2143
    -- Register a potentially passed drawn sprite animation callback function.
2146
    -- TODO: allow registering without main actor execution callback.
2144
    -- TODO: allow registering without main actor execution callback.
2147
    local animfunc = args.animate
2145
    local animfunc = args.animate
2148
    if (animfunc ~= nil) then
2146
    if (animfunc ~= nil) then
2149
        if (type(animfunc) ~= "function") then
2147
        if (type(animfunc) ~= "function") then
2150
            error("invalid 'animate' argument to gameactor: must be a function", 2)
2148
            error("invalid 'animate' argument to gameactor: must be a function", 2)
2151
        end
2149
        end
2152
2150
2153
        animsprite_funcs[tilenum] = replacep and func
2151
        animsprite_funcs[tilenum] = replacep and func
2154
            or (chainflags==AF.chain_beg) and chain_func3(animfunc, animsprite_funcs[tilenum])
2152
            or (chainflags==AF.chain_beg) and chain_func3(animfunc, animsprite_funcs[tilenum])
2155
            or (chainflags==AF.chain_end) and chain_func3(animsprite_funcs[tilenum], animfunc)
2153
            or (chainflags==AF.chain_end) and chain_func3(animsprite_funcs[tilenum], animfunc)
2156
            or assert(false)
2154
            or assert(false)
2157
2155
2158
        -- Register our EVENT_ANIMATEALLSPRITES only now so that it is not
2156
        -- Register our EVENT_ANIMATEALLSPRITES only now so that it is not
2159
        -- called if there are no 'animate' definitions.
2157
        -- called if there are no 'animate' definitions.
2160
        gameevent_internal(97, animate_all_sprites)  -- EVENT_ANIMATEALLSPRITES
2158
        gameevent_internal(97, animate_all_sprites)  -- EVENT_ANIMATEALLSPRITES
2161
    end
2159
    end
2162
2160
2163
    -- All good, bitwise-OR the tile bits and register the actor!
2161
    -- All good, bitwise-OR the tile bits and register the actor!
2164
    ffiC.g_tile[tilenum]._flags = bit.bor(ffiC.g_tile[tilenum]._flags, flags)
2162
    ffiC.g_tile[tilenum]._flags = bit.bor(ffiC.g_tile[tilenum]._flags, flags)
2165
2163
2166
    local newfunc = replacep and func
2164
    local newfunc = replacep and func
2167
        or (chainflags==AF.chain_beg) and chain_func3(func, actor_funcs[tilenum])
2165
        or (chainflags==AF.chain_beg) and chain_func3(func, actor_funcs[tilenum])
2168
        or (chainflags==AF.chain_end) and chain_func3(actor_funcs[tilenum], func)
2166
        or (chainflags==AF.chain_end) and chain_func3(actor_funcs[tilenum], func)
2169
        or assert(false)
2167
        or assert(false)
2170
2168
2171
    gameactor_internal(tilenum, strength, act, mov, movflags, newfunc)
2169
    gameactor_internal(tilenum, strength, act, mov, movflags, newfunc)
2172
    actor_funcs[tilenum] = newfunc
2170
    actor_funcs[tilenum] = newfunc
2173
end
2171
end
2174
2172
2175
2173
2176
-- gameevent{<event idx or string> [, flags], <event function>}
2174
-- gameevent{<event idx or string> [, flags], <event function>}
2177
local function our_gameevent(args)
2175
local function our_gameevent(args)
2178
    bcheck.top_level("gameevent")
2176
    bcheck.top_level("gameevent")
2179
2177
2180
    if (type(args)~="table") then
2178
    if (type(args)~="table") then
2181
        error("invalid gameevent call: must be passed a table")
2179
        error("invalid gameevent call: must be passed a table")
2182
    end
2180
    end
2183
2181
2184
    local event = args[1]
2182
    local event = args[1]
2185
2183
2186
    if (type(event) == "string") then
2184
    if (type(event) == "string") then
2187
        if (event:sub(1,6) ~= "EVENT_") then
2185
        if (event:sub(1,6) ~= "EVENT_") then
2188
            event = "EVENT_"..event
2186
            event = "EVENT_"..event
2189
        end
2187
        end
2190
        local eventidx = con_lang.EVENT[event]
2188
        local eventidx = con_lang.EVENT[event]
2191
        if (eventidx == nil) then
2189
        if (eventidx == nil) then
2192
            errorf(2, "gameevent: invalid event label %q", event)
2190
            errorf(2, "gameevent: invalid event label %q", event)
2193
        end
2191
        end
2194
        event = eventidx
2192
        event = eventidx
2195
    end
2193
    end
2196
    if (type(event) ~= "number") then
2194
    if (type(event) ~= "number") then
2197
        error("invalid argument #1 to gameevent: must be a number or event label", 2)
2195
        error("invalid argument #1 to gameevent: must be a number or event label", 2)
2198
    end
2196
    end
2199
    if (not (event >= 0 and event < con_lang.MAXEVENTS)) then
2197
    if (not (event >= 0 and event < con_lang.MAXEVENTS)) then
2200
        error("invalid argument #1 to gameevent: must be an event number (0 .. MAXEVENTS-1)", 2)
2198
        error("invalid argument #1 to gameevent: must be an event number (0 .. MAXEVENTS-1)", 2)
2201
    end
2199
    end
2202
2200
2203
    local AF = actor.FLAGS
2201
    local AF = actor.FLAGS
2204
2202
2205
    -- Kind of CODEDUP from our_gameactor.
2203
    -- Kind of CODEDUP from our_gameactor.
2206
    local lastargi = ourmaxn(args)
2204
    local lastargi = ourmaxn(args)
2207
    local func = args[lastargi]
2205
    local func = args[lastargi]
2208
    if (type(func) ~= "function") then
2206
    if (type(func) ~= "function") then
2209
        func = args.func
2207
        func = args.func
2210
        lastargi = 1/0
2208
        lastargi = 1/0
2211
    end
2209
    end
2212
    if (type(func) ~= "function") then
2210
    if (type(func) ~= "function") then
2213
        error("invalid gameevent call: must provide a function with last numeric arg or .func", 2)
2211
        error("invalid gameevent call: must provide a function with last numeric arg or .func", 2)
2214
    end
2212
    end
2215
2213
2216
    -- Event chaining: in Lunatic, chaining at the *end* is the default.
2214
    -- Event chaining: in Lunatic, chaining at the *end* is the default.
2217
    local flags = (lastargi > 2 and args[2]) or args.flags or AF.chain_end
2215
    local flags = (lastargi > 2 and args[2]) or args.flags or AF.chain_end
2218
    if (type(flags) ~= "number") then
2216
    if (type(flags) ~= "number") then
2219
        error("invalid 'flags' argument (#2) to gameevent: must be a number", 2)
2217
        error("invalid 'flags' argument (#2) to gameevent: must be a number", 2)
2220
    end
2218
    end
2221
2219
2222
    if (band(flags, BNOT.CHAIN_MASK_EVENT) ~= 0) then
2220
    if (band(flags, BNOT.CHAIN_MASK_EVENT) ~= 0) then
2223
        error("invalid