-- definitions of BUILD and game types for the Lunatic Interpreter -- INTERNAL! local ffi = require("ffi") ---- sector, wall, sprite, ... ---- ffi.cdef[[ #pragma pack(push,1) typedef struct { const int16_t wallptr, wallnum; int32_t ceilingz, floorz; int16_t ceilingstat, floorstat; int16_t ceilingpicnum, ceilingheinum; int8_t ceilingshade; uint8_t ceilingpal, ceilingxpanning, ceilingypanning; int16_t floorpicnum, floorheinum; int8_t floorshade; uint8_t floorpal, floorxpanning, floorypanning; uint8_t visibility, filler; int16_t lotag, hitag, extra; } sectortype; typedef struct { int32_t x, y; const int16_t point2, nextwall, nextsector; int16_t cstat; int16_t picnum, overpicnum; int8_t shade; uint8_t pal, xrepeat, yrepeat, xpanning, ypanning; int16_t lotag, hitag, extra; } walltype; typedef struct { int32_t x, y, z; int16_t cstat, picnum; int8_t shade; uint8_t pal, clipdist, filler; uint8_t xrepeat, yrepeat; int8_t xoffset, yoffset; const int16_t sectnum, statnum; int16_t ang, owner, xvel, yvel, zvel; int16_t lotag, hitag, extra; } spritetype; typedef struct { const uint32_t mdanimtims; const int16_t mdanimcur; int16_t angoff, pitch, roll; int32_t xoff, yoff, zoff; uint8_t flags; uint8_t xpanning, ypanning; const uint8_t filler; float alpha; const int32_t _do_not_use1; const int32_t _do_not_use2; } spriteext_t; typedef struct { int32_t x, y, z; } vec3_t; #pragma pack(pop) enum { MAXSECTORS = 4096, MAXWALLS = 16384, MAXSPRITES = 16384, MAXSTATUS = 1024, MAXBUNCHES = 256, CEILING = 0, FLOOR = 1, }; ]] assert(ffi.sizeof('sectortype')==40) assert(ffi.sizeof('walltype')==32) assert(ffi.sizeof('spritetype')==44) ---- engine data and functions ---- ffi.cdef[[int32_t engine_main_arrays_are_static;]] -- NOTE TO SELF: This is not C, never EVER write -- if (x) -- when checking a C variable x for 'thuthiness' if (ffi.C.engine_main_arrays_are_static ~= 0) then -- print('main arrays are static'); ffi.cdef[[ sectortype sector[]; walltype wall[]; spritetype sprite[]; spriteext_t spriteext[]; ]] else -- print('main arrays are pointers'); ffi.cdef[[ sectortype *sector; walltype *wall; spritetype *sprite; spriteext_t *spriteext; ]] end ffi.cdef[[ const int16_t numsectors, numwalls; const int32_t numyaxbunches; const int16_t headspritesect[MAXSECTORS+1], headspritestat[MAXSTATUS+1]; const int16_t prevspritesect[MAXSPRITES], prevspritestat[MAXSPRITES]; const int16_t nextspritesect[MAXSPRITES], nextspritestat[MAXSPRITES]; const int16_t headsectbunch[2][MAXBUNCHES], nextsectbunch[2][MAXSECTORS]; int16_t yax_getbunch(int16_t i, int16_t cf); ]] ---- game structs ---- ffi.cdef[[ #pragma pack(push,1) // ACTOR_T, might still need to make some fields read-only typedef struct { const int32_t t_data[10]; // 40b sometimes used to hold offsets to con code int16_t picnum,ang,extra,owner; //8b int16_t movflag,tempang,timetosleep; //6b int32_t flags, bposx,bposy,bposz; //16b int32_t floorz,ceilingz,lastvx,lastvy; //16b int32_t lasttransport; //4b const int16_t lightId, lightcount, lightmaxrange, cgg; //8b int16_t actorstayput, dispicnum, shootzvel; // 6b const int8_t _do_not_use[24]; } actor_t; #pragma pack(pop) enum { MAXMOUSEBUTTONS = 10, MAXMOUSEAXES = 2, MAXJOYBUTTONS = (32+4), MAXJOYAXES = 8, NUMGAMEFUNCTIONS = 56, }; typedef struct { vec3_t camera; int32_t const_visibility,uw_framerate; int32_t camera_time,folfvel,folavel,folx,foly,fola; int32_t reccnt,crosshairscale; int32_t runkey_mode,statusbarscale,mouseaiming,weaponswitch,drawweapon; // JBF 20031125 int32_t democams,color,msgdisptime,statusbarmode; int32_t m_noexits,noexits,autovote,automsg,idplayers; int32_t team, viewbob, weaponsway, althud, weaponscale, textscale; int32_t entered_name,screen_tilting,shadows,fta_on,executions,auto_run; int32_t coords,tickrate,levelstats,m_coop,coop,screen_size,lockout,crosshair; int32_t playerai,angleinterpolation,obituaries; int32_t respawn_monsters,respawn_items,respawn_inventory,recstat,monsters_off,brightness; int32_t m_respawn_items,m_respawn_monsters,m_respawn_inventory,m_recstat,m_monsters_off,detail; int32_t m_ffire,ffire,m_player_skill,m_level_number,m_volume_number,multimode; int32_t player_skill,level_number,volume_number,m_marker,marker,mouseflip; int32_t configversion; int16_t cameraang, camerasect, camerahoriz; int16_t pause_on,from_bonus; int16_t camerasprite,last_camsprite; int16_t last_level,secretlevel, bgstretch; struct { int32_t UseJoystick; int32_t UseMouse; int32_t AutoAim; int32_t ShowOpponentWeapons; int32_t MouseDeadZone,MouseBias; int32_t SmoothInput; // JBF 20031211: Store the input settings because // (currently) jmact can't regurgitate them int32_t MouseFunctions[MAXMOUSEBUTTONS][2]; int32_t MouseDigitalFunctions[MAXMOUSEAXES][2]; int32_t MouseAnalogueAxes[MAXMOUSEAXES]; int32_t MouseAnalogueScale[MAXMOUSEAXES]; int32_t JoystickFunctions[MAXJOYBUTTONS][2]; int32_t JoystickDigitalFunctions[MAXJOYAXES][2]; int32_t JoystickAnalogueAxes[MAXJOYAXES]; int32_t JoystickAnalogueScale[MAXJOYAXES]; int32_t JoystickAnalogueDead[MAXJOYAXES]; int32_t JoystickAnalogueSaturate[MAXJOYAXES]; uint8_t KeyboardKeys[NUMGAMEFUNCTIONS][2]; // // Sound variables // int32_t FXDevice; int32_t MusicDevice; int32_t FXVolume; int32_t MusicVolume; int32_t SoundToggle; int32_t MusicToggle; int32_t VoiceToggle; int32_t AmbienceToggle; int32_t NumVoices; int32_t NumChannels; int32_t NumBits; int32_t MixRate; int32_t ReverseStereo; // // Screen variables // int32_t ScreenMode; int32_t ScreenWidth; int32_t ScreenHeight; int32_t ScreenBPP; int32_t ForceSetup; int32_t NoAutoLoad; int32_t scripthandle; int32_t setupread; int32_t CheckForUpdates; int32_t LastUpdateCheck; int32_t useprecache; } config; char overhead_on,last_overhead,showweapons; char god,warp_on,cashman,eog,showallmap; char show_help,scrollmode,clipping; char ridecule[10][40]; char savegame[10][22]; char pwlockout[128],rtsname[128]; char display_bonus_screen; char show_level_text; } user_defs; ]] ffi.cdef[[ actor_t actor[MAXSPRITES]; user_defs ud; ]] ---- _G tweaks -- pull in only 'safe' stuff ---- local G_ = {} -- our soon-to-be global environment local oG = _G G_.coroutine = coroutine G_.assert = assert G_.tostring = tostring G_.tonumber = tonumber --rawget G_.xpcall = xpcall G_.ipairs = ipairs G_.print = print G_.pcall = pcall --gcinfo --DEPRECATED --module --setfenv --require --rawset --jit G_.bit = bit --package G_.error = error --debug --loadfile --rawequal --load G_.unpack = unpack G_.pairs = pairs G_.table = table G_._VERSION = _VERSION --newproxy --NOT STD? --collectgarbage --dofile G_.next = next G_.math = math --loadstring --_G G_.select = select G_.string = string G_.type = type --getmetatable --getfenv --setmetatable G_._G = G_ -- REMOVE this for release DBG_ = {} DBG_.loadstring = oG.loadstring ---- Set up restricted access to ffi.C from lunatic. ---- local ffiC = ffi.C -- error(..., 2) is to blame the caller and get its line numbers local det = {} -- dummy empty table local tmpmt = { __index = function() error('dummy variable: read access forbidden', 2) end, __newindex = function() error('dummy variable: write access forbidden', 2) end, __metatable = true -- forbid setting the metatable } oG.setmetatable(det, tmpmt) -- GLOBAL gv: provides access to C global *scalars* gv = { -- all non-scalars need to be explicitly listed -- and access to them is redirected to the dummy -- empty table... this is somewhat ugly sector = det, wall = det, sprite = det, spriteext = det, headspritesect = det, headspritestat = det, prevspritesect = det, prevspritestat = det, nextspritesect = det, nextspritestat = det, actor = det, ud = det, } local tmpmt = { __index = ffiC, __newindex = function() error("cannot create new or write into existing fields of 'gv'", 2) end, __metatable = true, } oG.setmetatable(gv, tmpmt) ---- indirect C array access ---- sector = {} local tmpmt = { __index = function(tab, key) if (key >= 0 and key < ffiC.numsectors) then return ffiC.sector[key] end error('out-of-bounds sector[] read access', 2) end, __newindex = function(tab, key, val) error('cannot write directly to sector[] struct', 2) end, __metatable = true, } oG.setmetatable(sector, tmpmt) wall = {} local tmpmt = { __index = function(tab, key) if (key >= 0 and key < ffiC.numwalls) then return ffiC.wall[key] end error('out-of-bounds wall[] read access', 2) end, __newindex = function(tab, key, val) error('cannot write directly to wall[] struct', 2) end, __metatable = true, } oG.setmetatable(wall, tmpmt) -- create a safe indirection for a ffi.C array local function creategtab(ctab, maxidx, name) local tab = {} local tmpmt = { __index = function(tab, key) if (key>=0 and key < maxidx) then return ctab[key] end error('out-of-bounds '..name..' read access', 2) end, __newindex = function(tab, key, val) error('cannot write directly to '..name, 2) end, __metatable = true, } oG.setmetatable(tab, tmpmt) return tab end sprite = creategtab(ffiC.sprite, ffiC.MAXSPRITES, 'sprite[] struct') spriteext = creategtab(ffiC.spriteext, ffiC.MAXSPRITES, 'spriteext[] struct') headspritesect = creategtab(ffiC.headspritesect, ffiC.MAXSECTORS, 'headspritesect[]') headspritestat = creategtab(ffiC.headspritestat, ffiC.MAXSTATUS, 'headspritestat[]') nextspritesect = creategtab(ffiC.nextspritesect, ffiC.MAXSPRITES, 'nextspritesect[]') nextspritestat = creategtab(ffiC.nextspritestat, ffiC.MAXSPRITES, 'nextspritestat[]') prevspritesect = creategtab(ffiC.prevspritesect, ffiC.MAXSPRITES, 'prevspritesect[]') prevspritestat = creategtab(ffiC.prevspritestat, ffiC.MAXSPRITES, 'prevspritestat[]') actor = creategtab(ffiC.actor, ffiC.MAXSPRITES, 'actor[]') function TEMP_getvollev() -- REMOVE return ffiC.ud.volume_number+1, ffiC.ud.level_number+1 end ---- per-sector/per-statnum sprite iterators ---- local function iter_spritesofsect(sect, i) if (i < 0) then i = ffiC.headspritesect[sect] else i = ffiC.nextspritesect[i] end if (i >= 0) then return i end end function spritesofsect(sect) if (sect < 0 or sect >= ffiC.numsectors) then error("passed invalid sectnum to spritesofsect iterator", 2) end return iter_spritesofsect, sect, -1 end local function iter_spritesofstat(stat, i) if (i < 0) then i = ffiC.headspritestat[stat] else i = ffiC.nextspritestat[i] end if (i >= 0) then return i end end function spritesofstat(stat) if (stat < 0 or stat >= ffiC.MAXSTATUS) then error("passed invalid statnum to spritesofstat iterator", 2) end return iter_spritesofstat, stat, -1 end -- TROR iterators local function iter_sectorsofbunch(cf, i) if (i < 0) then i = ffiC.headsectbunch[cf][-i-1]; else i = ffiC.nextsectbunch[cf][i]; end if (i >= 0) then return i end end function sectorsofbunch(bunchnum, cf) if (bunchnum < 0 or bunchnum >= ffiC.numyaxbunches) then error("passed invalid bunchnum to sectorsofbunch iterator", 2) end if (cf ~= 0 and cf ~= 1) then error("passed invalid 'cf' to sectorsofbunch iterator, must be 0 or 1", 2) end return iter_sectorsofbunch, cf, -bunchnum-1 end function getbunch(sectnum, cf) if (sectnum < 0 or sectnum >= ffiC.numsectors) then error('out-of-bounds sector[] read access', 2) end if (cf ~= 0 and cf ~= 1) then error("passed invalid 'cf' to getbunch, must be 0 or 1", 2) end return yax_getbunch(sectnum, cf) end -- 'simple' code for prohibiting initial assignments to create new variables, -- from 14.2 of PiL function gamevar(name, initval) -- aka 'declare' rawset(G_, name, initval or false) end -- add new variables/functions living in the global environment G_.DBG_ = DBG_ -- REMOVE this for release G_.gv = gv G_.sector = sector G_.wall = wall G_.sprite = sprite G_.spriteext = spriteext G_.headspritesect = headspritesect G_.headspritestat = headspritestat G_.nextspritesect = nextspritesect G_.nextspritestat = nextspritestat G_.prevspritesect = prevspritesect G_.prevspritestat = prevspritestat G_.actor = actor -- functions G_.spritesofsect = spritesofsect G_.spritesofstat = spritesofstat G_.sectorsofbunch = sectorsofbunch G_.getbunch = getbunch G_.TEMP_getvollev = TEMP_getvollev -- REMOVE G_.gamevar = gamevar -- PiL 14.2 continued setmetatable( G_, { __newindex = function (_, n) error("attempt to write to undeclared variable "..n, 2) end, __index = function (_, n) error("attempt to read undeclared variable "..n, 2) end, }) -- change the environment of the running Lua thread to the table G_ setfenv(0, G_)