raze/polymer/eduke32/source/lunatic/defs.ilua

740 lines
19 KiB
Text
Raw Normal View History

-- INTERNAL
-- definitions of BUILD and game types for the Lunatic Interpreter
_EDUKE32_LUNATIC = true
local ffi = require("ffi")
---=== Duke3D engine and game definitions ===---
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,noclip;
char ridecule[10][40];
char savegame[10][22];
char pwlockout[128],rtsname[128];
char display_bonus_screen;
char show_level_text;
} user_defs;
// KEEPINSYNC2
enum GameEvent_t {
EVENT_INIT,
EVENT_ENTERLEVEL,
EVENT_RESETWEAPONS,
EVENT_RESETINVENTORY,
EVENT_HOLSTER,
EVENT_LOOKLEFT,
EVENT_LOOKRIGHT,
EVENT_SOARUP,
EVENT_SOARDOWN,
EVENT_CROUCH,
EVENT_JUMP,
EVENT_RETURNTOCENTER,
EVENT_LOOKUP,
EVENT_LOOKDOWN,
EVENT_AIMUP,
EVENT_FIRE,
EVENT_CHANGEWEAPON,
EVENT_GETSHOTRANGE,
EVENT_GETAUTOAIMANGLE,
EVENT_GETLOADTILE,
EVENT_CHEATGETSTEROIDS,
EVENT_CHEATGETHEAT,
EVENT_CHEATGETBOOT,
EVENT_CHEATGETSHIELD,
EVENT_CHEATGETSCUBA,
EVENT_CHEATGETHOLODUKE,
EVENT_CHEATGETJETPACK,
EVENT_CHEATGETFIRSTAID,
EVENT_QUICKKICK,
EVENT_INVENTORY,
EVENT_USENIGHTVISION,
EVENT_USESTEROIDS,
EVENT_INVENTORYLEFT,
EVENT_INVENTORYRIGHT,
EVENT_HOLODUKEON,
EVENT_HOLODUKEOFF,
EVENT_USEMEDKIT,
EVENT_USEJETPACK,
EVENT_TURNAROUND,
EVENT_DISPLAYWEAPON,
EVENT_FIREWEAPON,
EVENT_SELECTWEAPON,
EVENT_MOVEFORWARD,
EVENT_MOVEBACKWARD,
EVENT_TURNLEFT,
EVENT_TURNRIGHT,
EVENT_STRAFELEFT,
EVENT_STRAFERIGHT,
EVENT_WEAPKEY1,
EVENT_WEAPKEY2,
EVENT_WEAPKEY3,
EVENT_WEAPKEY4,
EVENT_WEAPKEY5,
EVENT_WEAPKEY6,
EVENT_WEAPKEY7,
EVENT_WEAPKEY8,
EVENT_WEAPKEY9,
EVENT_WEAPKEY10,
EVENT_DRAWWEAPON,
EVENT_DISPLAYCROSSHAIR,
EVENT_DISPLAYREST,
EVENT_DISPLAYSBAR,
EVENT_RESETPLAYER,
EVENT_INCURDAMAGE,
EVENT_AIMDOWN,
EVENT_GAME,
EVENT_PREVIOUSWEAPON,
EVENT_NEXTWEAPON,
EVENT_SWIMUP,
EVENT_SWIMDOWN,
EVENT_GETMENUTILE,
EVENT_SPAWN,
EVENT_LOGO,
EVENT_EGS,
EVENT_DOFIRE,
EVENT_PRESSEDFIRE,
EVENT_USE,
EVENT_PROCESSINPUT,
EVENT_FAKEDOMOVETHINGS,
EVENT_DISPLAYROOMS,
EVENT_KILLIT,
EVENT_LOADACTOR,
EVENT_DISPLAYBONUSSCREEN,
EVENT_DISPLAYMENU,
EVENT_DISPLAYMENUREST,
EVENT_DISPLAYLOADINGSCREEN,
EVENT_ANIMATESPRITES,
EVENT_NEWGAME,
MAXEVENTS
};
]]
ffi.cdef[[
actor_t actor[MAXSPRITES];
user_defs ud;
]]
---=== Set up restricted global environment ===---
-- _G tweaks -- pull in only 'safe' stuff
local G_ = {} -- our soon-to-be global environment
local oG = _G -- old genv, to access thrown-out functions later in this chunk
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_
-- http://lua-users.org/wiki/SandBoxes says "potentially unsafe"
-- as it allows to see implementations of functions.
local string_dump = string.dump
string.dump = nil
--- non-default data and functions
G_._EDUKE32_LUNATIC = _EDUKE32_LUNATIC
G_.setevent = setevent -- included in lunatic.c
---=== Lunatic interpreter setup ===---
local lunacon = require("lunacon")
-- change the environment of this chunk to the table G_
setfenv(1, G_)
-- print keys and values of a table
local function printkv(label, table)
print('========== Keys and values of '..label)
for k,v in pairs(table) do
print(k .. ': ' .. tostring(v))
end
print('----------')
end
printkv('_G AFTER SETFENV', _G)
---=== Restricted access to C variables 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,
luaJIT_BC_con_lang,
luaJIT_BC_lunacon,
}
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[]')
-- TODO: allow sprite freelist access via the status list for CON compatibility?
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
---=== Game variables ===---
-- gamevarNames[name] is true if that gamevar was declared
local gamevarNames = {}
-- code for prohibiting initial assignments to create new variables,
-- based on 14.2 of PiL
function gamevar(name, initval) -- aka 'declare'
if (type(name) ~= "string") then
error("First argument to 'gamevar' must be a string", 2);
end
if (not string.match(name, "^[%a_][%w_]*$")) then
error("First argument to 'gamevar' must be a valid identifier", 2);
end
if (gamevarNames[name]) then
error(string.format("Duplicate declaration of identifier '%s'", name), 2)
end
if (oG.rawget(G_, name) ~= nil) then
error(string.format("Identifier name '%s' is already used in the global environment", name), 2)
end
gamevarNames[name] = true
oG.rawset(G_, name, initval or false)
end
---=== Serialization, from PiL with modifications ===---
local function basicSerialize(o)
if (type(o) == "number") then
if (o ~= o) then return "0/0" end -- nan
if (o == 1/0) then return "1/0" end -- inf
if (o == -1/0) then return "-1/0" end -- -inf
return tostring(o)
else -- must be a string
if (type(o) ~= "string") then
error("cannot save a " .. type(o) .. " as key of a table", 3);
end
return string.format("%q", o)
end
end
-- will contain all gamevar tables as keys (and true as value)
local tmpgvtabs = {}
local function save(name, value, saved, strings)
saved = saved or {} -- initial value
strings[#strings+1] = name .. "="
if (type(value) == "number" or type(value) == "string") then
strings[#strings+1] = basicSerialize(value) .. "\n"
elseif (type(value) == "table") then
if (saved[value]) then -- value already saved?
strings[#strings+1] = saved[value] .. "\n" -- use its previous name
else
saved[value] = name -- save name for next time
strings[#strings+1] = "{}\n" -- create a new table
for k,v in pairs(value) do -- save its fields
local fieldname = string.format("%s[%s]", name, basicSerialize(k))
if (type(v)=="table" and not tmpgvtabs[v]) then
error("cannot save \""..name..
"\": gamevar tables may only contain tables that are also gamevars", 2)
end
save(fieldname, v, saved, strings)
end
end
else
error("cannot save \""..name.."\", a " .. type(value), 2)
end
end
local function serializeGamevars()
local saved = {}
local strings = {}
for gvname,_ in pairs(gamevarNames) do
if (type(G_[gvname])=="table") then
tmpgvtabs[G_[gvname]] = true
end
end
-- TODO: catch errors in save()
for gvname,_ in pairs(gamevarNames) do
save(gvname, G_[gvname], saved, strings)
end
strings[#strings+1] = "\n"
tmpgvtabs = {}
return table.concat(strings)
end
local function loadGamevarsString(string)
assert(oG.loadstring(string))()
end
-- REMOVE this for release
DBG_ = {}
DBG_.printkv = printkv
DBG_.loadstring = oG.loadstring
DBG_.serializeGamevars = serializeGamevars
DBG_.loadGamevarsString = loadGamevarsString
---=== Finishing environment setup ===---
printkv('_G AFTER DECLS', _G)
-- PiL 14.2 continued
-- We need this at the end because we were previously doing just that!
-- XXX: but user modules will want to do "function thisfunc() ... "
oG.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 so that everything
-- what we've set up will be available when this chunk is left.
-- In particular, we need the functions defined after setting this chunk's
-- environment earlier.
oG.setfenv(0, _G)