1
2-- This file contains LuaJIT definitions of stuff that's common to the game and
3-- editor.  The 'decl' function is expected to be defined in the global
4-- environment.
5-- See the file "BUILDLIC.TXT" distributed with EDuke32 for license info.
6
7-- Will be 'true' if running from engine Lua state:
8local _LUNATIC_AUX = _LUNATIC_AUX
9
10local ffi = require("ffi")
11local ffiC = ffi.C
12local bit = require("bit")
13
14local bor = bit.bor
15local pcall = pcall
16
17ffi.cdef "const char **g_argv;"
18
19-- Lunatic debugging options (-Lopts=<opt1>,<opt2>,... from the command line):
20--     diag: print diagnostic information
21--    nojit: disable JIT compilation
22--   traces: load LuaJIT's 'v' module, printing trace info
23--           (env var: LUAJIT_VERBOSEFILE)
24--     dump: load LuaJIT's 'dump' module, printing generated IR/machine code
25--           (env var: LUAJIT_DUMPFILE)
26--  profile: load LuaJIT's 'jit.p' module for profiling (LuaJIT 2.1 only)
27--           (env var: LUAJIT_PROFILEFILE)
28--   strict: catch various conditions that may indicate a logical error
29-- TODO for strict: actor[], spriteext[], per-actor gamevars
30local debug_flags = {}
31local IS_DEBUG_FLAG = {
32    diag=true, nojit=true, traces=true, dump=true,
33    strict=true, profile=true,
34}
35
36-- Handle command-line argument. (Look for -Lopts=...)
37local function handle_cmdline_arg(str)
38    local opts = str:match("^-Lopts=(.*)")
39
40    if (opts ~= nil) then
41        for opt in opts:gmatch("[^,]+") do
42            if (IS_DEBUG_FLAG[opt]) then
43                debug_flags[opt] = true
44            end
45        end
46    end
47end
48
49local i=0
50while (ffiC.g_argv[i] ~= nil) do
51    handle_cmdline_arg(ffi.string(ffiC.g_argv[i]))
52    i = i+1
53end
54
55-- Print diagnostic information?
56ffi.cdef("enum { _DEBUG_LUNATIC="..(debug_flags.diag and 1 or 0).." }")
57-- Be strict?
58ffi.cdef("enum { _LUNATIC_STRICT="..(debug_flags.strict and 1 or 0).." }")
59
60if (debug_flags.nojit) then
61    require("jit").off()
62end
63
64if (not _LUNATIC_AUX) then
65    if (debug_flags.dump) then
66        require("dump").on("+rs")
67    elseif (debug_flags.traces) then
68        require("v").on()
69    end
70
71    if (debug_flags.profile) then
72        if (pcall(function() require("jit.p").start() end) == false) then
73            print("Warning: failed enabing profiler. Running LuaJIT 2.0 build?")
74        end
75    end
76end
77
78local math = require("math")
79local string = require("string")
80local table = require("table")
81
82local assert = assert
83local error = error
84local pairs = pairs
85local require = require
86local setmetatable = setmetatable
87local tostring = tostring
88local type = type
89
90local decl = assert(decl)
91local getfenv = getfenv
92
93decl "void OSD_Printf(const char *fmt, ...);"
94print = function(str)
95    -- our "print" doesn't use the global "tostring", but the initial one
96    str = tostring(str)
97    if (type(str) ~= "string") then
98        error("invalid argument to print: must be convertible to a string")
99    end
100    ffiC.OSD_Printf("%s\n", str)
101end
102
103local print=print
104
105
106module(...)
107
108
109local band = bit.band
110local bor = bit.bor
111local bnot = bit.bnot
112local lshift = bit.lshift
113local rshift = bit.rshift
114local xor = bit.bxor
115
116
117--== bitint type factory ==--
118
119-- Metatable for an integer type that is treated as bitfield. The integer
120-- itself must be named '_v'.
121local bitint_mt = {
122    __index = {
123        set = function(self, bits)
124            self._v = bor(self._v, bits)
125        end,
126
127        clear = function(self, bits)
128            self._v = band(self._v, bnot(bits))
129        end,
130
131        flip = function(self, bits)
132            self._v = xor(self._v, bits)
133        end,
134
135        test = function(self, bits)
136            return (band(self._v, bits) ~= 0)
137        end,
138
139        mask = function(self, bits)
140            return band(self._v, bits)
141        end,
142    },
143
144    __metatable = true,
145}
146
147local bitint_to_base_type = {}
148
149function bitint_new_struct_type(basetypename, newtypename)
150    assert(bitint_to_base_type[newtypename] == nil)
151    assert(type(basetypename)=="string")
152    assert(type(newtypename)=="string")
153
154    local bitint_struct_t = ffi.typeof("struct { $ _v; }", ffi.typeof(basetypename))
155    ffi.metatype(bitint_struct_t, bitint_mt)
156    ffi.cdef("typedef $ $;", bitint_struct_t, newtypename)
157
158    bitint_to_base_type[newtypename] = basetypename
159end
160
161function bitint_member(bitint_struct_typename, membname)
162    return string.format("union { %s %s; %s %sbits; };",
163                         bitint_to_base_type[bitint_struct_typename], membname,
164                         bitint_struct_typename, membname)
165end
166
167bitint_new_struct_type("uint8_t", "UBit8")
168bitint_new_struct_type("uint16_t", "UBit16")
169
170-- Converts a template struct definition to an internal, unrestricted one.
171-- NOTE: "[^ ]*" for const decorations in _defs_game.lua.
172function strip_const(structstr)
173    return (string.gsub(structstr, "const[^ ]* ", ""));
174end
175
176local function maybe_strip_const(str)
177    return _LUNATIC_AUX and strip_const(str) or str
178end
179
180
181--== Core engine structs ==--
182
183local CF_MEMBERS = [[
184    const int16_t ~picnum;
185    int16_t ~heinum;
186    const int16_t ~bunch;
187]]..bitint_member("UBit16", "~stat")..[[
188    int32_t ~z;
189    int8_t ~shade;
190    uint8_t ~pal, ~xpanning, ~ypanning;
191]]
192
193ffi.cdef("typedef struct { "..CF_MEMBERS:gsub("~","").." } ceiling_or_floor_t;")
194
195local hplane_ptr_ct = ffi.typeof("struct { "..strip_const(CF_MEMBERS:gsub("~","")).." } *")
196
197local SECTOR_STRUCT = [[
198struct {
199    const int16_t wallptr, wallnum;
200]]..
201string.format([[
202    union {
203      struct { ceiling_or_floor_t ceiling, floor; };
204      struct { %s %s };
205    };
206]], CF_MEMBERS:gsub("~","ceiling"), CF_MEMBERS:gsub("~","floor"))
207..[[
208    uint8_t visibility, fogpal;
209    int16_t lotag, hitag;  // NOTE: signed for Lunatic
210    int16_t extra;
211}]]
212
213local SPRITE_STRUCT = [[
214struct {
215    // TODO: transparent union with vec3_t pos?
216    int32_t x, y, z;
217]]..bitint_member("UBit16", "cstat")..[[
218    const int16_t picnum;
219    int8_t shade;
220    uint8_t pal, clipdist, blend;
221    uint8_t xrepeat, yrepeat;
222    int8_t xoffset, yoffset;
223    const int16_t sectnum, statnum;
224    int16_t ang;
225
226    const int16_t owner;
227    int16_t xvel;
228    // NOTE: yvel is often used as player index in game code.
229    const int16_t yvel;
230    int16_t zvel;
231
232    int16_t lotag, hitag, extra;
233}]]
234
235local WALL_STRUCT = [[
236struct {
237    int32_t x, y;
238    const int16_t point2, nextwall, nextsector;
239    const int16_t upwall, dnwall;
240]]..bitint_member("UBit16", "cstat")..[[
241    const int16_t picnum, overpicnum;
242    int8_t shade;
243    uint8_t pal, xrepeat, yrepeat, xpanning, ypanning;
244    int16_t lotag, hitag, extra;
245    uint8_t blend, _filler;
246}]]
247
248-- NOTE for FFI definitions: we're compiling EDuke32 with -funsigned-char, so
249-- we need to take care to declare chars as unsigned whenever it matters, for
250-- example if it represents a palette index.  (I think it's harmless for stuff
251-- like passing a function argument, but it should be done there for clarity.)
252
253-- TODO: provide getters for unsigned {hi,lo}tag?
254ffi.cdef([[
255typedef $ sectortype;
256typedef $ walltype;
257// NOTE: spritetype and uspritetype are different types with the same data members.
258typedef $ spritetype;
259typedef struct { spritetype; } uspritetype;
260
261typedef struct {
262    int32_t x, y;
263} vec2_t;
264
265typedef struct {
266    int32_t x, y, z;
267} vec3_t;
268
269typedef struct {
270    const uint32_t mdanimtims;
271    const int16_t mdanimcur;
272    int16_t angoff, pitch, roll;
273    vec3_t mdoff;
274]]..bitint_member("UBit8", "flags")..[[
275    uint8_t xpanning, ypanning;
276    const uint8_t filler;
277    float alpha;
278    // NOTE: const aggregate fixed with LuaJIT git fe9934feea0a8d580de1
279    // ("FFI: Fix handling of qualified transparent structs/unions.")
280    const union {
281        intptr_t _tspr;
282        struct { int32_t _dummy0, _dummy1; };
283    };
284} spriteext_t;
285
286typedef struct {
287    vec3_t pos;
288    int16_t sprite, wall, sector;
289} hitdata_t;
290]],
291ffi.typeof(maybe_strip_const(SECTOR_STRUCT)),
292ffi.typeof(maybe_strip_const(WALL_STRUCT)),
293ffi.typeof(maybe_strip_const(SPRITE_STRUCT)))
294
295if (not _LUNATIC_AUX) then
296    -- Define the "palette_t" type, which for us has .{r,g,b} fields and a
297    -- bound-checking array of length 3 overlaid.
298    require("bcarray").new("uint8_t", 3, "RGB array", "palette_t",
299                           { "r", "g", "b", "f" })
300    assert(ffi.alignof("palette_t")==1)
301end
302
303local vec3_ct = ffi.typeof("vec3_t")  -- will be metatype'd in xmath.lua:
304
305if (not _LUNATIC_AUX) then
306    require("xmath")
307end
308
309-- TODO: 'isceiling' and 'isfloor' methods or similar?
310local hitdata_ct = ffi.typeof("hitdata_t")
311
312decl[[
313const int32_t engine_main_arrays_are_static;
314const int32_t engine_v8;
315]]
316
317
318--== Engine data and functions ==--
319
320
321-- NOTE TO SELF: This is not C, never EVER write
322--    if (x)
323-- when checking a C variable x for 'thuthiness'
324if (ffiC.engine_main_arrays_are_static ~= 0) then
325    decl[[
326    sectortype sector[];
327    walltype wall[];
328    spritetype sprite[];
329    uspritetype tsprite[];
330    spriteext_t spriteext[];
331    ]]
332else
333    decl[[
334    sectortype *sector;
335    walltype *wall;
336    spritetype *sprite;
337    uspritetype *tsprite;
338    spriteext_t *spriteext;
339    ]]
340end
341
342if (ffiC.engine_v8 == 0) then
343    -- V7
344    ffi.cdef[[
345enum
346{
347    MAXSECTORS = 1024,
348    MAXWALLS = 8192,
349    MAXSPRITES = 4096,
350}
351]]
352else
353    -- V8
354    ffi.cdef[[
355enum
356{
357    MAXSECTORS = 4096,
358    MAXWALLS = 16384,
359    MAXSPRITES = 16384,
360}
361]]
362end
363
364ffi.cdef[[
365enum {
366    MAXSTATUS = 1024,
367    MAXTILES = 30720,
368    MAXSPRITESONSCREEN = 4096,
369
370    MAXBUNCHES = 512,
371    CEILING = 0,  // must be 0
372    FLOOR = 1,  // must be 1
373    BOTH_CF = 2,
374
375    CLIPMASK0 = (1<<16)+1,  // blocking
376    CLIPMASK1 = (256<<16)+64,  // hittable
377};
378]]
379
380ffi.cdef(maybe_strip_const("const int16_t numsectors, numwalls;"))
381
382ffi.cdef[[
383const int32_t Numsprites;
384const int32_t numyaxbunches;  // XXX
385const int32_t totalclock;
386int32_t randomseed;  // DEPRECATED
387const int32_t xdim, ydim;
388const vec2_t windowxy1, windowxy2;
389]]
390
391decl[[
392int32_t kopen4load(const char *filename, char searchfirst);
393int32_t kfilelength(int32_t handle);
394void kclose(int32_t handle);
395int32_t kread(int32_t handle, void *buffer, int32_t leng);
396int32_t klseek(int32_t handle, int32_t offset, int32_t whence);
397
398int32_t sectorofwall_noquick(int16_t theline);
399]]
400
401-- Reads the whole file given by the k* file descriptor into a Lua string.
402-- Does not close the file descriptor.
403function readintostr(fd, kopen4load_func)
404    -- XXX: this is pretty much the same as the code in L_RunOnce()
405
406    local sz = ffiC.kfilelength(fd)
407    if (sz == 0) then
408        return ""
409    end
410
411    if (sz < 0) then
412        error("INTERNAL ERROR: kfilelength() returned negative length")
413    end
414
415    local str = ffi.new("char [?]", sz)
416    local readlen = ffiC.kread(fd, str, sz)
417
418    if (readlen ~= sz) then
419        error("INTERNAL ERROR: couldn't read file wholly")
420    end
421
422    return ffi.string(str, sz)
423end
424
425if (_LUNATIC_AUX) then
426    -- XXX: The global doesn't show up in 'engine_maptext'.
427    -- I guess I still haven't fully grokked globals in Lua.
428    string.readintostr = readintostr
429    require "engine_maptext"
430    return
431end
432
433ffi.cdef "const int32_t rendmode;"
434
435decl[[
436int32_t yxaspect;
437int32_t viewingrange;
438int32_t spritesortcnt;
439int32_t guniqhudid;
440const int16_t headspritesect[MAXSECTORS+1], headspritestat[MAXSTATUS+1];
441const int16_t prevspritesect[MAXSPRITES], prevspritestat[MAXSPRITES];
442const int16_t nextspritesect[MAXSPRITES], nextspritestat[MAXSPRITES];
443const vec2_t tilesiz[MAXTILES];
444typedef struct {
445    uint8_t num;  // animate number
446    int8_t xofs, yofs;
447    uint8_t sf;  // anim. speed and flags
448} picanm_t;
449const picanm_t picanm[MAXTILES];
450
451uint8_t show2dsector[(MAXSECTORS+7)>>3];
452
453const int16_t headsectbunch[2][MAXBUNCHES], nextsectbunch[2][MAXSECTORS];
454
455int16_t yax_getbunch(int16_t i, int16_t cf);
456
457int32_t   getceilzofslopeptr(const sectortype *sec, int32_t dax, int32_t day);
458int32_t   getflorzofslopeptr(const sectortype *sec, int32_t dax, int32_t day);
459void   getzsofslopeptr(const sectortype *sec, int32_t dax, int32_t day,
460                       int32_t *ceilz, int32_t *florz);
461int32_t spriteheightofsptr(const spritetype *spr, int32_t *height, int32_t alsotileyofs);
462
463int32_t changespritesect(int16_t spritenum, int16_t newsectnum);
464int32_t changespritestat(int16_t spritenum, int16_t newstatnum);
465
466int32_t hitscan(const vec3_t *sv, int16_t sectnum, int32_t vx, int32_t vy, int32_t vz,
467                hitdata_t *hitinfo, uint32_t cliptype);
468int32_t cansee(int32_t x1, int32_t y1, int32_t z1, int16_t sect1,
469               int32_t x2, int32_t y2, int32_t z2, int16_t sect2);
470void neartag(int32_t xs, int32_t ys, int32_t zs, int16_t sectnum, int16_t ange, int16_t *neartagsector, int16_t *neartagwall,
471             int16_t *neartagsprite, int32_t *neartaghitdist, int32_t neartagrange, uint8_t tagsearch,
472             int32_t (*blacklist_sprite_func)(int32_t));
473void dragpoint(int16_t pointhighlight, int32_t dax, int32_t day, uint8_t flags);
474void getzrange(const vec3_t *pos, int16_t sectnum,
475               int32_t *ceilz, int32_t *ceilhit, int32_t *florz, int32_t *florhit,
476               int32_t walldist, uint32_t cliptype);
477int32_t clipmovex(vec3_t *pos, int16_t *sectnum, int32_t xvect, int32_t yvect,
478                  int32_t walldist, int32_t ceildist, int32_t flordist, uint32_t cliptype,
479                  uint8_t noslidep);
480
481int32_t ldist(const spritetype *s1, const spritetype *s2);
482int32_t dist(const spritetype *s1, const spritetype *s2);
483
484int32_t inside(int32_t x, int32_t y, int16_t sectnum);
485void updatesector(int32_t x, int32_t y, int16_t *sectnum);
486void updatesectorbreadth(int32_t x, int32_t y, int16_t *sectnum);
487void updatesectorz(int32_t x, int32_t y, int32_t z, int16_t *sectnum);
488
489void rotatesprite_(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum,
490                   int8_t dashade, unsigned char dapalnum, int32_t dastat, uint8_t alpha, uint8_t dablend,
491                   int32_t cx1, int32_t cy1, int32_t cx2, int32_t cy2);
492
493void setaspect(int32_t daxrange, int32_t daaspect);
494]]
495
496-- misc. functions
497ffi.cdef[[
498uint32_t getticks(void);
499double gethiticks(void);
500
501int32_t krand(void);
502int32_t ksqrt(uint32_t num);
503int32_t getangle(int32_t xvect, int32_t yvect);
504int32_t Mulscale(int32_t a, int32_t b, int32_t sh);
505]]
506
507local bcheck = require("bcheck")
508local check_sector_idx = bcheck.sector_idx
509local check_wall_idx = bcheck.wall_idx
510local check_sprite_idx = bcheck.sprite_idx
511local check_tile_idx = bcheck.tile_idx
512
513local wallsofsec  -- fwd-decl
514
515local sectortype_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(SECTOR_STRUCT)))
516
517local function get_sector_idx(sec)
518    local i = ffi.cast(sectortype_ptr_ct, sec)-ffi.cast(sectortype_ptr_ct, ffiC.sector)
519--    assert(i >= 0 and i < ffiC.numsectors)
520    return i
521end
522
523local zret = ffi.new("int32_t [4]")
524local zret_t = ffi.typeof[[const struct {
525    struct {
526        bool spritep;
527        int32_t num;  // number of sector or sprite
528        int32_t z;
529    } c, f;
530}]]
531
532local sectortype_mt = {
533    __index = {
534        --- Setters
535        set_ceilingpicnum = function(s, picnum)
536            check_tile_idx(picnum)
537            ffi.cast(sectortype_ptr_ct, s).ceilingpicnum = picnum
538        end,
539
540        set_floorpicnum = function(s, picnum)
541            check_tile_idx(picnum)
542            ffi.cast(sectortype_ptr_ct, s).floorpicnum = picnum
543        end,
544
545        --- Other methods
546        ceilingzat = function(s, pos)
547            return ffiC.getceilzofslopeptr(s, pos.x, pos.y)
548        end,
549
550        floorzat = function(s, pos)
551            return ffiC.getflorzofslopeptr(s, pos.x, pos.y)
552        end,
553
554        -- getzrange() interface
555        zrangeat = function(s, pos, walldist, cliptype)
556            local sectnum = get_sector_idx(s)
557            local ipos = vec3_ct(pos.x, pos.y, pos.z)
558            walldist = walldist or 128
559            cliptype = cliptype or ffiC.CLIPMASK0
560
561            ffiC.getzrange(ipos, sectnum, zret+0, zret+1, zret+2, zret+3,
562                           walldist, cliptype)
563            local ceilz, ceilhit, florz, florhit = zret[0], zret[1], zret[2], zret[3]
564
565            return zret_t({ ceilhit>=49152, bit.band(ceilhit,16383), ceilz },
566                          { florhit>=49152, bit.band(florhit,16383), florz })
567        end,
568
569        -- inside() port, OUTOFSYNC with engine.c
570        contains = function(s, pos)
571            local x, y = pos.x, pos.y
572            local cnt = 0
573
574            for w in wallsofsec(s) do
575                local wal2 = ffiC.wall[ffiC.wall[w].point2]
576                local y1, y2 = ffiC.wall[w].y-y, wal2.y-y
577
578                if (xor(y1, y2) < 0) then
579                    local x1, x2 = ffiC.wall[w].x-x, wal2.x-x
580
581                    if (xor(x1, x2)>=0) then
582                        cnt = xor(cnt, x1)
583                    else
584                        cnt = xor(cnt, xor(x1*y2-x2*y1, y2))
585                    end
586                end
587            end
588
589            return (cnt < 0)
590        end,
591    }
592}
593ffi.metatype("sectortype", sectortype_mt)
594
595local hplane_mt = {
596    __index = {
597        set_picnum = function(hp, picnum)
598            check_tile_idx(picnum)
599            ffi.cast(hplane_ptr_ct, hp).picnum = picnum
600        end
601    },
602}
603ffi.metatype("ceiling_or_floor_t", hplane_mt)
604
605local walltype_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(WALL_STRUCT)))
606
607local walltype_mt = {
608    __index = {
609        --- Setters
610        set_picnum = function(w, picnum)
611            check_tile_idx(picnum)
612            ffi.cast(walltype_ptr_ct, w).picnum = picnum
613        end,
614
615        set_overpicnum = function(w, picnum)
616            check_tile_idx(picnum)
617            ffi.cast(walltype_ptr_ct, w).overpicnum = picnum
618        end,
619
620        _set_nextwall = function(w, nextwall)
621            -- NOTE: Allow setting a wall to white too, but no checking of the
622            -- consistency invariant ".nextwall>=0 iff .nextsector>=0".
623            if (not (nextwall < 0)) then
624                check_wall_idx(nextwall)
625            end
626            ffi.cast(walltype_ptr_ct, w).nextwall = nextwall
627        end,
628
629        _set_nextsector = function(w, nextsector)
630            if (not (nextsector < 0)) then
631                check_sector_idx(nextsector)
632            end
633            ffi.cast(walltype_ptr_ct, w).nextsector = nextsector
634        end,
635
636        --- Predicates
637        isblocking = function(w)
638            return (band(w.cstat, 1)~=0)
639        end,
640
641        ismasked = function(w)
642            return (band(w.cstat, 16)~=0)
643        end,
644
645        isoneway = function(w)
646            return (band(w.cstat, 32)~=0)
647        end,
648
649        ishittable = function(w)
650            return (band(w.cstat, 64)~=0)
651        end,
652
653        -- Indexing a wall with 'z' gets 0, so that you can e.g. use a wall as
654        -- RHS to vec3_t addition.
655        z = 0,
656    }
657}
658ffi.metatype("walltype", walltype_mt)
659
660local spriteext_mt = {
661    __index = {
662        -- Enable EVENT_ANIMATESPRITES for this sprite.
663        -- XXX: unused?
664        make_animated = function(sx)
665            sx.flags = bor(sx.flags, 16)
666        end,
667    },
668}
669ffi.metatype("spriteext_t", spriteext_mt)
670
671local spritetype_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(SPRITE_STRUCT)))
672-- NOTE: this is the *protected* uspritetype pointer.
673local tspritetype_ptr_ct = ffi.typeof("$ *", ffi.typeof("uspritetype"))
674
675local intarg = ffi.new("int32_t[1]")
676
677-- XXX: We ought to be using the dynamic value, but users remapping that tile
678-- are likely somewhat confused anyway. Also, it would be more proper if this
679-- lived on game-side Lunatic.
680local APLAYER = 1405
681
682local spritetype_mt = {
683    __pow = function(s, zofs)
684        return vec3_ct(s.x, s.y, s.z-zofs)
685    end,
686
687    __index = {
688        --- Setters
689        set_picnum = function(s, tilenum)
690            if (s.picnum == APLAYER or tilenum == APLAYER) then
691                error("setting picnum to or on an APLAYER sprite forbidden")
692            end
693            check_tile_idx(tilenum)
694            ffi.cast(spritetype_ptr_ct, s).picnum = tilenum
695        end,
696
697        set_yvel = function(s, yvel)
698            -- XXX: A malicious user might still find some backdoor way to
699            --  inject a bad yvel value, but this together with the above
700            --  set_picnum() check should at least prevent naive attempts.
701            if (s.picnum == APLAYER) then
702                error("setting yvel on an APLAYER sprite forbidden", 2)
703            end
704            ffi.cast(spritetype_ptr_ct, s).yvel = yvel
705        end,
706
707        _set_owner = function(s, owner)
708            -- XXX: AMC TC sets owner to -1 in the cutscene.
709            check_sprite_idx(owner)
710            ffi.cast(spritetype_ptr_ct, s).owner = owner
711        end,
712
713        --- Custom methods ---
714
715        getheightofs = function(s)
716            -- XXX: better reimplement in Lua?
717            local zofs = ffiC.spriteheightofsptr(s, intarg, 0)
718            return intarg[0], zofs
719        end,
720    },
721}
722
723local function deep_copy(tab)
724    local ntab = {}
725    for key, val in pairs(tab) do
726        if (type(val)=="table") then
727            ntab[key] = deep_copy(val)
728        else
729            assert(type(val)=="function")
730            ntab[key] = val
731        end
732    end
733    return ntab
734end
735
736local tspritetype_mt = deep_copy(spritetype_mt)
737
738-- Get the sprite index of a sprite reference.
739-- This is relatively slow if the code doesn't get compiled, see related
740-- discussion here:
741-- http://www.freelists.org/post/luajit/FFI-versus-Lua-C-API-in-purely-interpreted-mode
742local function get_sprite_idx(spr)
743    local i = ffi.cast(spritetype_ptr_ct, spr)-ffi.cast(spritetype_ptr_ct, ffiC.sprite)
744--    assert(i >= 0 and i < ffiC.MAXSPRITES)
745    return i
746end
747
748---=== Methods that are specific to sprites ===---
749
750local l_updatesector  -- fwd-decl
751local l_changesect  -- fwd-decl
752
753-- The 'setpos' method is available for sprite and tsprite objects.
754-- spr:setpos(pos [, newsect]),
755-- where <newsect> is taken to mean "set the sprite's sector number to <newsect>";
756-- don't run update/search routines or anything like this.
757function spritetype_mt.__index.setpos(spr, pos, newsect)
758    spr.x, spr.y, spr.z = pos.x, pos.y, pos.z
759    if (newsect ~= nil) then
760        spr:changesect(newsect)
761    end
762    return spr
763end
764
765-- spr:changesect(newsect)
766-- changes the sector number of <spr> in an 'unforgiving' fashion (<newsect> of
767-- -1 is error)
768function spritetype_mt.__index.changesect(spr, newsect)
769    if (newsect ~= spr.sectnum) then
770        l_changesect(get_sprite_idx(spr), newsect)
771    end
772end
773
774-- spr:updatesect(flags)
775-- updates <spr>'s sectnum; if no matching sector is found, no-op
776-- returns the updated sector number
777function spritetype_mt.__index.updatesect(spr, flags)
778    local newsect = l_updatesector(spr, spr.sectnum, flags)
779    if (newsect ~= -1) then
780        spr:changesect(newsect)
781    end
782    return newsect
783end
784
785
786---=== Methods that are specific to tsprites ===---
787
788-- This ought to be called 'set_sectnum', but 'changesect' is for consistency
789-- with the sprite method.
790function tspritetype_mt.__index.changesect(tspr, sectnum)
791    if (tspr.sectnum ~= sectnum) then
792        check_sector_idx(sectnum)
793        ffi.cast(spritetype_ptr_ct, tspr).sectnum = sectnum
794    end
795end
796
797function tspritetype_mt.__index.setpos(tspr, pos, newsect)
798    tspr.x, tspr.y, tspr.z = pos.x, pos.y, pos.z
799    if (newsect ~= nil) then
800        tspr:changesect(newsect)
801    end
802    return tspr
803end
804
805function tspritetype_mt.__index.updatesect(tspr, flags)
806    local newsect = l_updatesector(tspr, tspr.sectnum, flags)
807    if (newsect ~= -1 and newsect ~= tspr.sectnum) then
808        tspr:changesect(newsect)
809    end
810    return newsect
811end
812
813function tspritetype_mt.__index.dup(tspr)
814    if (ffiC.spritesortcnt >= ffiC.MAXSPRITESONSCREEN+0ULL) then
815        return nil
816    end
817
818    local newtspr = ffiC.tsprite[ffiC.spritesortcnt]
819    ffi.copy(newtspr, tspr, ffi.sizeof(tspr))
820    ffiC.spritesortcnt = ffiC.spritesortcnt+1
821
822    return newtspr
823end
824
825function tspritetype_mt.__index.getspr(tspr)
826    check_sprite_idx(tspr.owner)
827    return ffiC.sprite[tspr.owner]
828end
829
830---======---
831
832-- The user of this module can insert additional "spritetype" index
833-- methods and register them with "ffi.metatype".
834function finish_spritetype(mt_index)
835    for name, func in pairs(mt_index) do
836        spritetype_mt.__index[name] = func
837        tspritetype_mt.__index[name] = func
838    end
839    ffi.metatype("spritetype", spritetype_mt)
840    ffi.metatype("uspritetype", tspritetype_mt)
841end
842
843
844---=== Restricted access to C variables from Lunatic ===---
845
846-- set metatable and forbid setting it further
847function setmtonce(tab, mt)
848    mt.__metatable = true
849    return setmetatable(tab, mt)
850end
851
852---- indirect C array access ----
853
854-- Create a safe indirection for an ffi.C array.
855function creategtab(ctab, maxidx, name)
856    local tab = {}
857    local tmpmt = {
858        __index = function(tab, key)
859            if (key>=0 and key < maxidx) then
860                return ctab[key]
861            end
862            error('out-of-bounds '..name..' read access', 2)
863        end,
864        __newindex = function()
865            error('cannot write directly to '..name, 2)
866        end,
867    }
868
869    return setmtonce(tab, tmpmt)
870end
871
872-- Create a a safe indirection for an ffi.C struct array, accessing a given
873-- member.
874function creategtab_membidx(ctab, membname, maxidx, name)
875    local tab = {}
876    local tmpmt = {
877        __index = function(tab, key)
878            if (key>=0 and key < maxidx) then
879                return ctab[key][membname]
880            end
881            error('out-of-bounds '..name..' read access', 2)
882        end,
883        __newindex = function()
884            error('cannot write directly to '..name, 2)
885        end,
886    }
887
888    return setmtonce(tab, tmpmt)
889end
890
891-- Create a a safe indirection for an ffi.C struct array, accessing a given
892-- pointer member, which either points to one element, or is NULL.
893function creategtab_membidx_ptr(ctab, membname, maxidx, name)
894    local tab = {}
895    local tmpmt = {
896        __index = function(tab, key)
897            if (key>=0 and key < maxidx) then
898                local ptr = ctab[key][membname]
899                if (ptr ~= nil) then
900                    return ctab[key][membname][0]
901                end
902                return nil
903--                error(name .. '[' .. key .. '] is null', 2)
904            end
905            error('out-of-bounds '..name..'[] read access', 2)
906        end,
907        __newindex = function()
908            error('cannot write directly to '..name..'[]', 2)
909        end,
910    }
911
912    return setmtonce(tab, tmpmt)
913end
914
915-- Construct const struct from table
916function conststruct(tab)
917    local strtab = { "struct {" }
918
919    if (tab[1] ~= nil) then
920        -- e.g. { "KNEE", "PISTOL", ... } provided
921        for i=1,#tab do
922            strtab[#strtab+1] = "static const int "..tab[i].."="..(i-1)..";"
923        end
924    else
925        -- e.g. { KNEE=0, PISTOL=1, ... } provided
926        for member, val in pairs(tab) do
927            strtab[#strtab+1] = "static const int "..member.."="..val..";"
928        end
929    end
930    strtab[#strtab+1] = "}"
931
932    return ffi.new(table.concat(strtab))
933end
934
935do
936    local smt_mt = {
937        __index = function()
938            error("invalid access to static data", 2)
939        end
940    }
941
942    function static_members_tab()
943        return setmtonce({}, smt_mt)
944    end
945end
946
947-- Static, non-instance members. Used to hold constants, for example
948-- sprite.CSTAT.TRANS1
949local static_members = {
950    sector = static_members_tab(),
951    wall = static_members_tab(),
952    sprite = static_members_tab(),
953}
954
955static_members.sector.STAT = conststruct
956{
957    PARALLAX = 1,
958    SLOPE = 2,
959    SWAPXY = 4,
960    SMOOSH = 8,
961    FLIPX = 16,
962    FLIPY = 32,
963    RELATIVE = 64,
964    MASK = 128,
965    -- NOTE the reversed order
966    TRANS2 = 128,
967    TRANS1 = 256,
968    BLOCK = 512,
969    HITSCAN = 2048,
970
971    FLIP_BITMASK = 16+32,
972    ORIENT_BITMASK = 4+16+32,
973    TRANS_BITMASK = 128+256,
974}
975
976static_members.sector.NEARTAG_FLAGS = conststruct
977{
978    LOTAG = 1,
979    HITAG = 2,
980    NOSPRITES = 4,
981}
982
983static_members.sector.UPDATE_FLAGS = conststruct
984{
985    BREADTH = 1,
986    Z = 2,
987}
988
989static_members.wall.CSTAT = conststruct
990{
991    BLOCK = 1,
992    BOTTOMSWAP = 2,
993    ALIGNBOTTOM = 4,
994    FLIPX = 8,
995    MASK = 16,
996    ONEWAY = 32,
997    HITSCAN = 64,
998    TRANS1 = 128,
999    FLIPY = 256,
1000    TRANS2 = 512,
1001
1002    FLIP_BITMASK = 8+256,
1003    TRANS_BITMASK = 128+512,
1004}
1005
1006static_members.sprite.CSTAT = conststruct
1007{
1008    BLOCK = 1,
1009    TRANS1 = 2,
1010    XFLIP = 4,
1011    YFLIP = 8,
1012    ALIGNWALL = 16,
1013    ALIGNFLOOR = 32,
1014    ONESIDE = 64,
1015    CENTER = 128,
1016    HITSCAN = 256,
1017    TRANS2 = 512,
1018
1019    ALIGN_BITMASK = 16+32,
1020    TRANS_BITMASK = 2+512,
1021
1022    INVISIBLE = 32768,
1023}
1024
1025local bitar = require("bitar")
1026-- XXX: bitar uses int32_t arrays, while show2dsector[] is a uint8_t
1027-- array. Potential unaligned access. Also, only works on little-endian
1028-- machines. This sucks.
1029static_members.sector.showbitmap = bitar.new(ffiC.MAXSECTORS-1, ffi.cast("int32_t *", ffiC.show2dsector))
1030
1031static_members.sector.DAMAGEHPLANE = conststruct
1032{
1033    SUPPRESS = -1,
1034    DEFAULT = 0,
1035    GLASSBREAK = 2^20,
1036}
1037
1038function static_members.sector.damagehplane_whatsect(RETURN)
1039    local what = (band(RETURN, 65536)~=0) and "ceiling" or "floor"
1040    local sectnum = band(RETURN, ffiC.MAXSECTORS-1)
1041    return what, sectnum
1042end
1043
1044local function iter_allsprites(_, curi)
1045    for i=curi+1,ffiC.MAXSPRITES-1 do
1046        if (ffiC.sprite[i].statnum ~= ffiC.MAXSTATUS) then
1047            return i
1048        end
1049    end
1050end
1051
1052function static_members.sprite.all()
1053    return iter_allsprites, nil, -1
1054end
1055
1056local sms = static_members.sprite
1057sms._headspritesect = creategtab(ffiC.headspritesect, ffiC.MAXSECTORS, 'headspritesect[]')
1058-- NOTE: don't allow freelist access
1059sms._headspritestat = creategtab(ffiC.headspritestat, ffiC.MAXSTATUS, 'headspritestat[]')
1060sms._nextspritesect = creategtab(ffiC.nextspritesect, ffiC.MAXSPRITES, 'nextspritesect[]')
1061sms._nextspritestat = creategtab(ffiC.nextspritestat, ffiC.MAXSPRITES, 'nextspritestat[]')
1062sms._prevspritesect = creategtab(ffiC.prevspritesect, ffiC.MAXSPRITES, 'prevspritesect[]')
1063sms._prevspritestat = creategtab(ffiC.prevspritestat, ffiC.MAXSPRITES, 'prevspritestat[]')
1064
1065function static_members.wall.dragto(wallnum, pos)
1066    check_wall_idx(wallnum)
1067
1068    -- TODO: some kind of validation of the position?
1069    ffiC.dragpoint(wallnum, pos.x, pos.y, 0)
1070end
1071
1072-- Functions changing the sector/status number of a sprite, without asking.
1073
1074-- Changes sector number of sprite with index <spritenum> to <sectnum>,
1075-- unconditionally and "unforgiving" (oob <sectnum> gives error).
1076-- <noerr> is for CON compatibility and prevents error on *sprite not in the
1077-- game world* if true.
1078function static_members.sprite.changesect(spritenum, sectnum, noerr)
1079    check_sprite_idx(spritenum)
1080    check_sector_idx(sectnum)
1081    if (ffiC.changespritesect(spritenum, sectnum)==-1 and not noerr) then
1082        error("cannot change sector number of sprite not in the game world", 2)
1083    end
1084end
1085
1086l_changesect = static_members.sprite.changesect
1087
1088function static_members.sprite.changestat(spritenum, statnum, noerr)
1089    -- TODO: see gameexec.c's CON_CHANGESPRITESTAT.
1090    check_sprite_idx(spritenum)
1091    if (not (statnum >= 0 and statnum < ffiC.MAXSTATUS)) then
1092        error("invalid status number "..statnum, 2)
1093    end
1094    if (ffiC.changespritestat(spritenum, statnum)==-1 and not noerr) then
1095        error("cannot change status number of sprite not in the game world", 2)
1096    end
1097end
1098
1099-- Update a sprite's sector number from its current position and sector number.
1100function static_members.sprite.updatesect(spritenum, flags)
1101    check_sprite_idx(spritenum)
1102    local spr = ffiC.sprite[spritenum]
1103
1104    local newsect = l_updatesector(spr, spr.sectnum, flags)
1105    if (newsect ~= -1 and newsect ~= spr.sectnum) then
1106        l_changesect(spritenum, newsect)
1107    end
1108    return newsect
1109end
1110
1111local strictp = debug_flags.strict
1112
1113function GenStructMetatable(Structname, Boundname, StaticMembersTab)
1114    StaticMembersTab = StaticMembersTab or static_members[Structname]
1115
1116    -- If we're running with the 'strict' option, disallow accesses to void
1117    -- sprites.
1118    local index_func = (strictp and Structname=="sprite") and
1119
1120        -- Mostly CODEDUP of lower function, ...
1121        function(tab, key)
1122            if (type(key)=="number") then
1123                if (key >= 0 and key < ffiC[Boundname]) then
1124                    -- ... except this.
1125                    -- (Inlining into the other function did slow things down.)
1126                    if (ffiC.sprite[key].statnum == ffiC.MAXSTATUS) then
1127                        error("attempt to access void sprite with index "..key, 2)
1128                    end
1129                    return ffiC[Structname][key]
1130                end
1131                error("out-of-bounds "..Structname.."[] read access with index "..key, 2)
1132            elseif (type(key)=="string") then
1133                return StaticMembersTab[key]
1134            end
1135        end
1136
1137        or
1138
1139        function(tab, key)
1140            if (type(key)=="number") then
1141                if (key >= 0 and key < ffiC[Boundname]) then
1142                    return ffiC[Structname][key]
1143                end
1144                error("out-of-bounds "..Structname.."[] read access with index "..key, 2)
1145            elseif (type(key)=="string") then
1146                return StaticMembersTab[key]
1147            end
1148        end
1149
1150    return {
1151        __index = index_func,
1152        __newindex = function() error("cannot write directly to "..Structname.."[]", 2) end,
1153    }
1154end
1155
1156local sector_mt = GenStructMetatable("sector", "numsectors")
1157local wall_mt = GenStructMetatable("wall", "numwalls")
1158local sprite_mt = GenStructMetatable("sprite", "MAXSPRITES")
1159
1160local atsprite_mt = {
1161    __index = function(tab, idx)
1162        check_sprite_idx(idx)
1163
1164        local tspr = ffi.cast(tspritetype_ptr_ct, ffiC.spriteext[idx]._tspr)
1165        if (tspr == nil) then
1166            error("tsprite of actor "..idx.." unavailable", 2)
1167        end
1168
1169        -- Return a reference to a tsprite[] element.
1170        return tspr[0]
1171    end,
1172
1173    __newindex = function() error('cannot write directly to atsprite[]', 2) end,
1174}
1175
1176
1177local vars_to_ignore = {}
1178for varname,_ in pairs(getfenv(1)) do
1179    if (ffiC._DEBUG_LUNATIC ~= 0) then
1180        print("IGNORE "..varname)
1181    end
1182    vars_to_ignore[varname] = true
1183end
1184
1185--== ALL GLOBALS FROM HERE ON ARE EXPORTED UPWARDS (see create_globals() below) ==--
1186
1187sector = setmtonce({}, sector_mt)
1188local sector = sector
1189wall = setmtonce({}, wall_mt)
1190sprite = setmtonce({}, sprite_mt)
1191spriteext = creategtab(ffiC.spriteext, ffiC.MAXSPRITES, 'spriteext[]')
1192_atsprite = setmtonce({}, atsprite_mt)
1193
1194local function iter_wallsofsec(endwall, w)
1195    w = w+1
1196    if (w < endwall) then
1197        return w
1198    end
1199end
1200
1201wallsofsec = function(sec)  -- local
1202    return iter_wallsofsec, sec.wallptr+sec.wallnum, sec.wallptr-1
1203end
1204
1205function wallsofsect(sect)
1206    check_sector_idx(sect)
1207    return iter_wallsofsec, sector[sect].wallptr+sector[sect].wallnum, sector[sect].wallptr-1
1208end
1209
1210--== Per-sector/per-statnum sprite iterators ==--
1211local function iter_spritesofsect(sect, i)
1212    if (i < 0) then
1213        i = ffiC.headspritesect[sect]
1214    else
1215        i = ffiC.nextspritesect[i]
1216    end
1217
1218    if (i >= 0) then return i end
1219end
1220
1221-- sprites of sectnum iterator that allows deleting the iterated sprite
1222local function iter_spritesofsect_safe(tab, i)
1223    if (i < 0) then
1224        i = ffiC.headspritesect[-i]
1225    else
1226        i = tab[1]
1227    end
1228
1229    if (i >= 0) then
1230        tab[1] = ffiC.nextspritesect[i]
1231        return i
1232    end
1233end
1234
1235function spritesofsect(sect, maydelete)
1236    check_sector_idx(sect)
1237
1238    if (maydelete) then
1239        return iter_spritesofsect_safe, { -1 }, -sect
1240    else
1241        return iter_spritesofsect, sect, -1
1242    end
1243end
1244
1245local function iter_spritesofstat(stat, i)
1246    if (i < 0) then
1247        i = ffiC.headspritestat[stat]
1248    else
1249        i = ffiC.nextspritestat[i]
1250    end
1251
1252    if (i >= 0) then return i end
1253end
1254
1255-- sprites of statnum iterator that allows deleting the iterated sprite
1256local function iter_spritesofstat_safe(tab, i)
1257    if (i < 0) then
1258        i = ffiC.headspritestat[-i]
1259    else
1260        i = tab[1]
1261    end
1262
1263    if (i >= 0) then
1264        tab[1] = ffiC.nextspritestat[i]
1265        return i
1266    end
1267end
1268
1269function spritesofstat(stat, maydelete)
1270    if (not (stat >= 0 and stat < ffiC.MAXSTATUS)) then
1271        error("passed invalid statnum to spritesofstat iterator", 2)
1272    end
1273
1274    if (maydelete) then
1275        return iter_spritesofstat_safe, { -1 }, -stat
1276    else
1277        return iter_spritesofstat, stat, -1
1278    end
1279end
1280
1281--== TROR iterators ==--
1282local function iter_sectorsofbunch(cf, i)
1283    if (i < 0) then
1284        i = ffiC.headsectbunch[cf][-i-1];
1285    else
1286        i = ffiC.nextsectbunch[cf][i];
1287    end
1288
1289    if (i >= 0) then return i end
1290end
1291
1292local function iter_sectorsofbunch_both(cftab, i)
1293    local cf = cftab[1]
1294
1295    if (i < 0) then
1296        i = ffiC.headsectbunch[cf][-i-1];
1297    else
1298        i = ffiC.nextsectbunch[cf][i];
1299    end
1300
1301    if (i < 0 and cf==0) then
1302        cftab[1] = 1
1303        i = ffiC.headsectbunch[1][cftab[2]]
1304        assert(i >= 0, "TROR bunch lists corrupt")
1305    end
1306
1307    if (i >= 0) then
1308        return i, cftab[1]==0 and "ceiling" or "floor"
1309    end
1310end
1311
1312function sectorsofbunch(bunchnum, cf)
1313    if (not (bunchnum >= 0 and bunchnum < ffiC.numyaxbunches)) then
1314        error("passed invalid bunchnum to sectorsofbunch iterator", 2)
1315    end
1316
1317    if (cf == ffiC.BOTH_CF) then
1318        return iter_sectorsofbunch_both, { 0, bunchnum }, -bunchnum-1
1319    else
1320        if (not (cf == 0 or cf == 1)) then
1321            error("passed invalid 'cf' to sectorsofbunch iterator, must be 0 or 1", 2)
1322        end
1323
1324        return iter_sectorsofbunch, cf, -bunchnum-1
1325    end
1326end
1327
1328
1329---=== Engine functions, wrapped for Lua convenience ===---
1330
1331-- returns a hitdata_ct
1332-- TODO: make cliptype optional? What should be the default?
1333function hitscan(pos, sectnum, ray, cliptype)
1334    check_sector_idx(sectnum)
1335    local vec = vec3_ct(pos.x, pos.y, pos.z)
1336    local hitdata = hitdata_ct()
1337
1338    ffiC.hitscan(vec, sectnum, ray.x, ray.y, ray.z, hitdata, cliptype)
1339    return hitdata
1340end
1341
1342function cansee(pos1,sect1, pos2,sect2)
1343    check_sector_idx(sect1)
1344    check_sector_idx(sect2)
1345
1346    local ret = ffiC.cansee(pos1.x,pos1.y,pos1.z, sect1,
1347                            pos2.x,pos2.y,pos2.z, sect2)
1348    return (ret~=0)
1349end
1350
1351local neartag_ret_ct = ffi.typeof[[
1352const struct {
1353    int32_t sector, wall, sprite;
1354    int32_t dist;
1355}
1356]]
1357
1358local function newar() return ffi.new("int16_t [1]") end
1359-- NOTE: <tagsearch> flags are in sector.NEARTAG_FLAGS
1360function neartag(pos, sectnum, ang, range, tagsearch)
1361    check_sector_idx(sectnum)
1362    local a, b, c, d = newar(), newar(), newar(), ffi.new("int32_t [1]")
1363    ffiC.neartag(pos.x, pos.y, pos.z, sectnum, ang, a, b, c, d, range, tagsearch, nil)
1364    return neartag_ret_ct(a[0], b[0], c[0], d[0])
1365end
1366
1367function inside(pos, sectnum)
1368    check_sector_idx(sectnum)
1369    return (ffiC.inside(pos.x, pos.y, sectnum)==1)
1370end
1371
1372local us_retsect = ffi.new("int16_t [1]")
1373local USF = sector.UPDATE_FLAGS
1374
1375function updatesector(pos, sectnum, flags)
1376    if (sectnum ~= -1) then
1377        check_sector_idx(sectnum)
1378    end
1379
1380    us_retsect[0] = sectnum
1381
1382    if (flags==nil or flags==0) then
1383        ffiC.updatesector(pos.x, pos.y, us_retsect)
1384    elseif (flags==USF.BREADTH) then
1385        ffiC.updatesectorbreadth(pos.x, pos.y, us_retsect)
1386    elseif (flags==USF.Z) then
1387        -- Same as updatesectorz, but useful if we are called from
1388        -- e.g. sprite.updatesect().
1389        ffiC.updatesectorz(pos.x, pos.y, pos.z, us_retsect)
1390    else
1391        error("invalid argument #3 (flags)", 2)
1392    end
1393
1394    return us_retsect[0]
1395end
1396
1397l_updatesector = updatesector
1398
1399function updatesectorz(pos, sectnum, flags)
1400    if (sectnum ~= -1) then
1401        check_sector_idx(sectnum)
1402    end
1403    if (flags ~= nil) then
1404        error("invalid argument #3 (flags)", 2)
1405    end
1406
1407    us_retsect[0] = sectnum
1408    ffiC.updatesectorz(pos.x, pos.y, pos.z, us_retsect)
1409    return us_retsect[0]
1410end
1411
1412function printf(fmt, ...)
1413    print(string.format(fmt, ...))
1414end
1415
1416
1417-- This is supposed to be run from the file that 'require's this module to take
1418-- over the non-local variables from here into its global environment.
1419function create_globals(_G_their)
1420    local _G_our = getfenv(1)
1421    vars_to_ignore["create_globals"] = true
1422
1423    for varname,obj in pairs(_G_our) do
1424        if (not vars_to_ignore[varname]) then
1425            if (ffiC._DEBUG_LUNATIC ~= 0) then
1426                print("EXPORT "..varname)
1427            end
1428            _G_their[varname] = obj
1429        end
1430    end
1431end
1432