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