mirror of
https://github.com/DrBeef/Raze.git
synced 2024-11-16 09:21:12 +00:00
86a3603edd
git-svn-id: https://svn.eduke32.com/eduke32@2859 1a8010ca-5511-0410-912e-c29ae57300e0
1106 lines
29 KiB
Text
1106 lines
29 KiB
Text
-- 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;
|
|
|
|
typedef struct {
|
|
vec3_t pos;
|
|
int16_t hitsprite, hitwall, hitsect;
|
|
} hitdata_t;
|
|
|
|
typedef struct {
|
|
char r,g,b,f;
|
|
} palette_t;
|
|
#pragma pack(pop)
|
|
]]
|
|
|
|
local vec3_ct = ffi.typeof("vec3_t")
|
|
local hitdata_ct = ffi.typeof("hitdata_t")
|
|
|
|
---- engine data and functions ----
|
|
|
|
-- GLOBAL gv: provides access to C global *scalars* and safe functions
|
|
-- XXX: still exposes C library functions etc. contained in ffi.C.
|
|
-- Is this a problem?
|
|
local gv_ = {
|
|
-- all non-scalars need to be explicitly listed here and access to them is
|
|
-- redirected to the dummy empty table...
|
|
}
|
|
|
|
local dummy_empty_table = {}
|
|
|
|
-- This is for declarations of arrays or pointers which should not be
|
|
-- accessible through the gv global.
|
|
-- NOTE: don't declare multiple pointers on one line (like int32_t *a, *b;)!
|
|
local function decl(str)
|
|
for varname in string.gmatch(str, "([%a_][%w_]*)[[(;]") do
|
|
-- print("protect "..varname)
|
|
gv_[varname] = dummy_empty_table
|
|
end
|
|
|
|
ffi.cdef(str)
|
|
end
|
|
|
|
ffi.cdef[[int32_t engine_main_arrays_are_static, engine_v8;]]
|
|
|
|
-- 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');
|
|
decl[[
|
|
sectortype sector[];
|
|
walltype wall[];
|
|
spritetype sprite[];
|
|
spriteext_t spriteext[];
|
|
]]
|
|
else
|
|
-- print('main arrays are pointers');
|
|
decl[[
|
|
sectortype *sector;
|
|
walltype *wall;
|
|
spritetype *sprite;
|
|
spriteext_t *spriteext;
|
|
]]
|
|
end
|
|
|
|
if (ffi.C.engine_v8 == 0) then
|
|
-- V7
|
|
ffi.cdef[[
|
|
enum
|
|
{
|
|
MAXSECTORS = 1024,
|
|
MAXWALLS = 8192,
|
|
MAXSPRITES = 4096,
|
|
}
|
|
]]
|
|
else
|
|
-- V8
|
|
ffi.cdef[[
|
|
enum
|
|
{
|
|
MAXSECTORS = 4096,
|
|
MAXWALLS = 16384,
|
|
MAXSPRITES = 16384,
|
|
}
|
|
]]
|
|
end
|
|
|
|
ffi.cdef[[
|
|
enum {
|
|
MAXSTATUS = 1024,
|
|
|
|
MAXBUNCHES = 256,
|
|
CEILING = 0,
|
|
FLOOR = 1,
|
|
|
|
CLIPMASK0 = (1<<16)+1, // blocking
|
|
CLIPMASK1 = (256<<16)+64, // hittable
|
|
};
|
|
]]
|
|
|
|
ffi.cdef[[
|
|
const int16_t numsectors, numwalls;
|
|
const int32_t numyaxbunches;
|
|
]]
|
|
|
|
decl[[
|
|
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);
|
|
|
|
int32_t hitscan(const vec3_t *sv, int16_t sectnum, int32_t vx, int32_t vy, int32_t vz,
|
|
hitdata_t *hitinfo, uint32_t cliptype);
|
|
]]
|
|
|
|
-- array -> element flattening inside structs,
|
|
-- e.g. int32_t a[4] --> int32_t _a0, _a1, _a2, _a3;
|
|
local function repeat_n_elts(type, namebase, num)
|
|
local strbuf = { "const "..type.." " }
|
|
|
|
for i=1,num do
|
|
local str = namebase..tostring(i-1)
|
|
if (i < num) then
|
|
str = str..","
|
|
end
|
|
strbuf[#strbuf+1] = str
|
|
end
|
|
strbuf[#strbuf+1] = ";"
|
|
|
|
return table.concat(strbuf)
|
|
end
|
|
|
|
---- game structs ----
|
|
ffi.cdef[[
|
|
enum dukeinv_t {
|
|
GET_STEROIDS,
|
|
GET_SHIELD,
|
|
GET_SCUBA,
|
|
GET_HOLODUKE,
|
|
GET_JETPACK,
|
|
GET_DUMMY1,
|
|
GET_ACCESS,
|
|
GET_HEATS,
|
|
GET_DUMMY2,
|
|
GET_FIRSTAID,
|
|
GET_BOOTS,
|
|
GET_MAX
|
|
};
|
|
|
|
enum dukeweapon_t {
|
|
KNEE_WEAPON,
|
|
PISTOL_WEAPON,
|
|
SHOTGUN_WEAPON,
|
|
CHAINGUN_WEAPON,
|
|
RPG_WEAPON,
|
|
HANDBOMB_WEAPON,
|
|
SHRINKER_WEAPON,
|
|
DEVISTATOR_WEAPON,
|
|
TRIPBOMB_WEAPON,
|
|
FREEZE_WEAPON,
|
|
HANDREMOTE_WEAPON,
|
|
GROW_WEAPON,
|
|
MAX_WEAPONS
|
|
};
|
|
|
|
enum {
|
|
MAXPLAYERS = 16,
|
|
};
|
|
]]
|
|
|
|
ffi.cdef([[
|
|
#pragma pack(push,1)
|
|
// TODO: still need to make some fields read-only
|
|
// NOTE: must not expose arrays in structs!!!
|
|
typedef struct
|
|
{
|
|
]]
|
|
..repeat_n_elts("int32_t", "_t", 14)..
|
|
[[
|
|
// const int32_t t_data[14]; // 56b 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
|
|
]]
|
|
..repeat_n_elts("int8_t", "_d", 8)..
|
|
[[
|
|
// const int8_t _do_not_use[8];
|
|
} actor_t;
|
|
|
|
typedef struct {
|
|
vec3_t pos, opos, vel, npos;
|
|
int32_t bobposx, bobposy;
|
|
int32_t truefz, truecz, player_par;
|
|
int32_t randomflamex, exitx, exity;
|
|
int32_t runspeed, max_player_health, max_shield_amount;
|
|
|
|
uint32_t interface_toggle_flag;
|
|
|
|
uint8_t palette;
|
|
|
|
uint16_t max_actors_killed, actors_killed;
|
|
uint16_t gotweapon, zoom;
|
|
]]
|
|
..repeat_n_elts("int16_t", "_lx", 64)..repeat_n_elts("int16_t", "_ly", 64)..
|
|
[[
|
|
// int16_t loogiex[64], loogiey[64];
|
|
int16_t sbs, sound_pitch;
|
|
|
|
int16_t ang, oang, angvel, cursectnum, look_ang, last_extra, subweapon;
|
|
]]
|
|
..repeat_n_elts("int16_t", "_ma", ffi.C.MAX_WEAPONS)
|
|
..repeat_n_elts("int16_t", "_aa", ffi.C.MAX_WEAPONS)
|
|
..repeat_n_elts("int16_t", "_ia", ffi.C.GET_MAX)..
|
|
[[
|
|
// int16_t max_ammo_amount[MAX_WEAPONS], ammo_amount[MAX_WEAPONS], inv_amount[GET_MAX];
|
|
int16_t wackedbyactor, pyoff, opyoff;
|
|
|
|
int16_t horiz, horizoff, ohoriz, ohorizoff;
|
|
int16_t newowner, jumping_counter, airleft;
|
|
int16_t fta, ftq, access_wallnum, access_spritenum;
|
|
int16_t got_access, weapon_ang, visibility;
|
|
int16_t somethingonplayer, on_crane, i, one_parallax_sectnum;
|
|
int16_t random_club_frame, one_eighty_count;
|
|
int16_t dummyplayersprite, extra_extra8;
|
|
int16_t actorsqu, timebeforeexit, customexitsound, last_pissed_time;
|
|
]]
|
|
..repeat_n_elts("int16_t", "_w", ffi.C.MAX_WEAPONS)..
|
|
[[
|
|
// int16_t weaprecs[MAX_WEAPONS];
|
|
int16_t weapon_sway, crack_time, bobcounter;
|
|
|
|
int16_t orotscrnang, rotscrnang, dead_flag; // JBF 20031220: added orotscrnang
|
|
int16_t holoduke_on, pycount;
|
|
int16_t transporter_hold;
|
|
|
|
uint8_t max_secret_rooms, secret_rooms;
|
|
uint8_t frag, fraggedself, quick_kick, last_quick_kick;
|
|
uint8_t return_to_center, reloading, weapreccnt;
|
|
uint8_t aim_mode, auto_aim, weaponswitch, movement_lock, team;
|
|
uint8_t tipincs, hbomb_hold_delay, frag_ps, kickback_pic;
|
|
|
|
uint8_t gm, on_warping_sector, footprintcount, hurt_delay;
|
|
uint8_t hbomb_on, jumping_toggle, rapid_fire_hold, on_ground;
|
|
uint8_t inven_icon, buttonpalette, over_shoulder_on, show_empty_weapon;
|
|
|
|
uint8_t jetpack_on, spritebridge, lastrandomspot;
|
|
uint8_t scuba_on, footprintpal, heat_on, invdisptime;
|
|
|
|
uint8_t holster_weapon, falling_counter, footprintshade;
|
|
uint8_t refresh_inventory, last_full_weapon;
|
|
|
|
uint8_t toggle_key_flag, knuckle_incs, knee_incs, access_incs;
|
|
uint8_t walking_snd_toggle, palookup, hard_landing, fist_incs;
|
|
|
|
int8_t numloogs, loogcnt, scream_voice;
|
|
int8_t last_weapon, cheat_phase, weapon_pos, wantweaponfire, curr_weapon;
|
|
|
|
palette_t pals;
|
|
]]
|
|
..repeat_n_elts("char", "_n", 32)..
|
|
[[
|
|
// char name[32];
|
|
|
|
int8_t padding_;
|
|
} DukePlayer_t;
|
|
|
|
typedef struct {
|
|
uint32_t bits; // 4b
|
|
int16_t fvel, svel; // 4b
|
|
int8_t avel, horz; // 2b
|
|
int8_t extbits, filler; // 2b
|
|
} input_t;
|
|
|
|
typedef struct {
|
|
DukePlayer_t *ps;
|
|
input_t *sync;
|
|
|
|
int32_t netsynctime;
|
|
int16_t ping, filler;
|
|
int32_t pcolor, pteam;
|
|
uint8_t frags[MAXPLAYERS], wchoice[MAX_WEAPONS];
|
|
|
|
char vote, gotvote, pingcnt, playerquitflag;
|
|
char user_name[32];
|
|
uint32_t revision;
|
|
} playerdata_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;
|
|
]])
|
|
|
|
decl[[
|
|
const char *g_sizes_of_what[];
|
|
int32_t g_sizes_of[];
|
|
actor_t actor[MAXSPRITES];
|
|
user_defs ud;
|
|
playerdata_t g_player[MAXPLAYERS];
|
|
]]
|
|
|
|
-- functions
|
|
decl[[
|
|
int32_t kopen4loadfrommod(const char *filename, char searchfirst);
|
|
int32_t kfilelength(int32_t handle);
|
|
void kclose(int32_t handle);
|
|
int32_t kread(int32_t handle, void *buffer, int32_t leng);
|
|
]]
|
|
|
|
-- JKISS PRNG state struct and functions, see randgen module
|
|
ffi.cdef[[
|
|
typedef struct {
|
|
uint32_t x, y, z, c;
|
|
} rng_jkiss_t;
|
|
]]
|
|
decl[[
|
|
uint32_t rand_jkiss_u32(rng_jkiss_t *s);
|
|
double rand_jkiss_dbl(rng_jkiss_t *s);
|
|
|
|
void md4once(const unsigned char *block, unsigned int len, unsigned char digest[16]);
|
|
]]
|
|
|
|
ffi.cdef[[
|
|
int32_t ksqrt(uint32_t num);
|
|
]]
|
|
|
|
ffi.cdef "double gethitickms(void);"
|
|
|
|
local ffiC = ffi.C
|
|
|
|
-- sanity-check struct type sizes
|
|
for i=0,6 do
|
|
assert(ffi.sizeof(ffi.typeof(ffi.string(ffiC.g_sizes_of_what[i])))
|
|
== ffiC.g_sizes_of[i])
|
|
end
|
|
|
|
--- default defines
|
|
local con = require("con_lang")
|
|
|
|
for i=1,#con.labels do
|
|
local strbuf = {"enum {"}
|
|
|
|
for label, val in pairs(con.labels[i]) do
|
|
strbuf[#strbuf+1] = string.format("%s = %d,", label, val)
|
|
end
|
|
strbuf[#strbuf+1] = "};"
|
|
|
|
ffi.cdef(table.concat(strbuf))
|
|
end
|
|
|
|
|
|
---=== Set up restricted global environment ===---
|
|
|
|
-- These are for this file...
|
|
local string = string
|
|
local table = table
|
|
|
|
-- 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
|
|
|
|
|
|
gv_tmp = gv_ -- required by randgen
|
|
|
|
local allowed_modules = {
|
|
coroutine=coroutine, bit=bit, table=table, math=math, string=string,
|
|
|
|
os = {
|
|
clock = function() return gv_.gethitickms()/1000 end,
|
|
},
|
|
|
|
randgen = require("randgen"),
|
|
geom = require("geom"),
|
|
stat = require("stat"),
|
|
}
|
|
|
|
local package_loaded = {}
|
|
|
|
for modname, themodule in pairs(allowed_modules) do
|
|
local mt = {
|
|
__index = themodule,
|
|
__newindex = function(tab,idx,val)
|
|
error("modifying base module table forbidden")
|
|
end,
|
|
__metatable = true,
|
|
}
|
|
|
|
-- Comment out to make base modules not protected:
|
|
allowed_modules[modname] = setmetatable({}, mt)
|
|
end
|
|
|
|
local function check_valid_modname(modname)
|
|
if (type(modname) ~= "string") then
|
|
error("module name must be a string", 5)
|
|
end
|
|
|
|
-- TODO: restrict valid names?
|
|
end
|
|
|
|
local function errorf(level, fmt, ...)
|
|
local errmsg = string.format(fmt, ...)
|
|
error(errmsg, level+1)
|
|
end
|
|
|
|
local ERRLEV = 5
|
|
|
|
|
|
local function readintostr(fn)
|
|
-- XXX: this is pretty much the same as the code in El_RunOnce()
|
|
|
|
local fd = ffiC.kopen4loadfrommod(fn, 0) -- TODO: g_loadFromGroupOnly
|
|
if (fd < 0) then
|
|
errorf(ERRLEV, "Couldn't open file \"%s\"", fn)
|
|
end
|
|
|
|
local sz = ffiC.kfilelength(fd)
|
|
if (sz == 0) then
|
|
ffiC.kclose(fd)
|
|
errorf(ERRLEV, "Didn't load module \"%s\": zero-length file", fn)
|
|
end
|
|
|
|
if (sz < 0) then
|
|
ffi.kclose(fd)
|
|
error("INTERNAL ERROR: kfilelength() returned negative length", 5)
|
|
end
|
|
|
|
local str = ffi.new("char [?]", sz) -- XXX: what does it do on out of mem?
|
|
local readlen = ffiC.kread(fd, str, sz)
|
|
|
|
ffiC.kclose(fd); fd=-1
|
|
|
|
if (readlen ~= sz) then
|
|
errorf(ERRLEV, "INTERNAL ERROR: couldn't read \"%s\" wholly", fn)
|
|
end
|
|
|
|
return ffi.string(str, sz)
|
|
end
|
|
|
|
-- The "require" function accessible to Lunatic code.
|
|
-- Base modules in allowed_modules are wrapped so that they cannot be
|
|
-- modified, user modules are searched in the EDuke32 search
|
|
-- path. Also, our require never messes with the global environment,
|
|
-- it only returns the module.
|
|
local function our_require(modname)
|
|
check_valid_modname(modname)
|
|
|
|
-- see whether it's a base module name first
|
|
if (allowed_modules[modname] ~= nil) then
|
|
return allowed_modules[modname]
|
|
end
|
|
|
|
--- search user modules
|
|
|
|
if (package_loaded[modname] ~= nil) then
|
|
-- already loaded
|
|
return package_loaded[modname]
|
|
end
|
|
|
|
-- TODO: better pattern-matching (permit "", ".lua", ".elua" ?)
|
|
local str = readintostr(modname .. ".lua")
|
|
|
|
local modfunc, errmsg = loadstring(str)
|
|
if (modfunc == nil) then
|
|
errorf(ERRLEV-1, "Couldn't load \"%s\": %s", modname, errmsg)
|
|
end
|
|
|
|
local modtab = modfunc(modname)
|
|
|
|
if (modtab ~= nil) then
|
|
if (type(modtab) ~= "table") then
|
|
errorf(ERRLEV-1, "Didn't load module \"%s\": expected table as return value", modname)
|
|
end
|
|
|
|
package_loaded[modname] = modtab
|
|
else
|
|
modtab = package_loaded[modname]
|
|
|
|
if (type(modtab) ~= "table") then
|
|
errorf(ERRLEV-1, "Didn't load module \"%s\": expected module() to be called", modname)
|
|
end
|
|
end
|
|
|
|
-- Protect module table...
|
|
local mt = {
|
|
__index = modtab,
|
|
__newindex = function(tab,idx,val)
|
|
error("modifying module table forbidden", 2)
|
|
end,
|
|
}
|
|
-- ..here:
|
|
setmetatable(modtab, mt)
|
|
|
|
return modtab
|
|
end
|
|
|
|
|
|
-- _G tweaks -- pull in only 'safe' stuff
|
|
local G_ = {} -- our soon-to-be global environment
|
|
|
|
-- Old global environment, to access thrown-out functions later in this chunk.
|
|
-- Also, inside this file, we must refer to functions like 'pairs' by using
|
|
-- this table, since user code could later do pairs=nil.
|
|
local oG = _G
|
|
|
|
local module_mt = {
|
|
__index = function (_, n)
|
|
error("attempt to read undeclared variable '"..n.."'", 2)
|
|
end,
|
|
}
|
|
|
|
-- replacement for "module"
|
|
local function our_module(modname)
|
|
check_valid_modname(modname)
|
|
|
|
local M = oG.setmetatable({}, module_mt)
|
|
package_loaded[modname] = M
|
|
-- change the environment of the function which called us:
|
|
oG.setfenv(2, M)
|
|
end
|
|
|
|
-- overridden 'error' that always passes a string to the base 'error'
|
|
local function our_error(errmsg, level)
|
|
if (type(errmsg) ~= "string") then
|
|
oG.error("error using 'error': error message must be a string", 2)
|
|
end
|
|
|
|
if (level) then
|
|
if (type(level) ~= "number") then
|
|
oG.error("error using 'error': error level must be a number", 2)
|
|
end
|
|
|
|
oG.error(errmsg, level==0 and 0 or level+1)
|
|
end
|
|
|
|
oG.error(errmsg, 2)
|
|
end
|
|
|
|
G_.require = our_require
|
|
G_.module = our_module
|
|
---G_.coroutine = coroutine
|
|
G_.assert = assert
|
|
G_.tostring = tostring
|
|
G_.tonumber = tonumber
|
|
--rawget
|
|
G_.xpcall = xpcall
|
|
G_.ipairs = ipairs
|
|
G_.print = print -- TODO: --> initprintf or OSD_Printf; why not needed on linux?
|
|
G_.pcall = pcall
|
|
--gcinfo --DEPRECATED
|
|
--module
|
|
--setfenv
|
|
--require
|
|
--rawset
|
|
--jit
|
|
---G_.bit = bit
|
|
--package
|
|
G_.error = our_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_
|
|
|
|
--- non-default data and functions
|
|
G_._EDUKE32_LUNATIC = _EDUKE32_LUNATIC
|
|
G_.gameevent = gameevent -- included in lunatic.c
|
|
G_.gameactor = gameactor -- included in lunatic.c
|
|
|
|
|
|
---=== Lunatic interpreter setup ===---
|
|
|
|
local lunacon = require("lunacon")
|
|
|
|
|
|
-- change the environment of this chunk to the table G_
|
|
-- NOTE: all references to global variables from this point on
|
|
-- (also in functions created after this point) refer to G_ !
|
|
setfenv(1, G_)
|
|
|
|
-- We MUST remember to call base lib functions through oG here.
|
|
-- 'tostring' is a special case: it is used in 'print', i.e. the global 'tostring',
|
|
-- which is looked up in the current environment! Thus, we must REMOVE it and
|
|
-- 'print' for release, or write our own, atomic 'print'.
|
|
local error = oG.error
|
|
local type = oG.type
|
|
local pairs = oG.pairs
|
|
|
|
-- print keys and values of a table
|
|
local function printkv(label, table)
|
|
oG.print('========== Keys and values of '..label)
|
|
for k,v in oG.pairs(table) do
|
|
oG.print(k .. ': ' .. tostring(v))
|
|
end
|
|
oG.print('----------')
|
|
end
|
|
|
|
--printkv('_G AFTER SETFENV', _G)
|
|
|
|
|
|
---=== Restricted access to C variables from Lunatic ===---
|
|
|
|
-- error(..., 2) is to blame the caller and get its line numbers
|
|
|
|
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(dummy_empty_table, tmpmt)
|
|
|
|
gv = gv_
|
|
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 an 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('passed out-of-bounds sector number'..sectnum, 2)
|
|
end
|
|
if (cf ~= 0 and cf ~= 1) then
|
|
error("passed invalid 'cf' to getbunch, must be 0 or 1", 2)
|
|
end
|
|
|
|
return ffiC.yax_getbunch(sectnum, cf)
|
|
end
|
|
|
|
|
|
---=== Engine functions, wrapped for Lua convenience ===---
|
|
-- returns a hitdata_ct
|
|
function hitscan(x,y,z, sectnum, vx,vy,vz, cliptype)
|
|
if (sectnum < 0 or sectnum >= ffiC.numsectors) then
|
|
error('passed out-of-bounds sector number'..sectnum, 2)
|
|
end
|
|
|
|
local vec = vec3_ct(x,y,z)
|
|
local hitdata = hitdata_ct()
|
|
|
|
ffiC.hitscan(vec, sectnum, vx,vy,vz, hitdata, cliptype)
|
|
return hitdata
|
|
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 (string.match(name, "^_")) then
|
|
error("Identifier names starting with an underscore are reserved", 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)
|
|
elseif (type(o) == "boolean") then
|
|
return tostring(o)
|
|
elseif (type(o) == "string") then
|
|
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 .. "="
|
|
|
|
local str = basicSerialize(value)
|
|
|
|
if (str ~= nil) then
|
|
strings[#strings+1] = str .. "\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 keystr = basicSerialize(k)
|
|
if (keystr == nil) then
|
|
error("cannot save a " .. type(k) .. " as key of a table", 2);
|
|
end
|
|
local fieldname = string.format("%s[%s]", name, keystr)
|
|
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)
|
|
--[=[
|
|
for gvname,_ in pairs(gamevarNames) do
|
|
G_[gvname] = nil;
|
|
end
|
|
gamevarNames = {}; -- clear gamevars
|
|
--]=]
|
|
|
|
oG.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 (_1, n, _2)
|
|
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)
|