mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-11-05 20:41:06 +00:00
7b6bf4dda2
The Lunatic build compiles with new structures for sector and wall types. They have separate members for TROR {up,down}{bunch,nextwall}, so there are no conflicts with other uses of members into which they were previously shoehorned. Also, the maximum bunch limit is bumped to 512 in that build. Currently, loading from V7/8/9 and saving to V7 and V8 are supported. git-svn-id: https://svn.eduke32.com/eduke32@3658 1a8010ca-5511-0410-912e-c29ae57300e0
1009 lines
28 KiB
Lua
1009 lines
28 KiB
Lua
|
|
-- This file contains LuaJIT definitions of stuff that's common to the game and
|
|
-- editor. The 'decl' function is expected to be defined in the global
|
|
-- environment.
|
|
-- See the included license file "BUILDLIC.TXT" for license info.
|
|
|
|
local ffi = require("ffi")
|
|
local ffiC = ffi.C
|
|
local bit = require("bit")
|
|
|
|
-- Lunatic debugging (mostly bitfield):
|
|
-- ~=0: print diagnostic information
|
|
-- 2: disable JIT compilation
|
|
-- 4: load LuaJIT's 'v' module, printing trace info
|
|
-- 8: load LuaJIT's 'dump' module, printing generated IR/machine code
|
|
ffi.cdef "enum { _DEBUG_LUNATIC=1 }"
|
|
|
|
if (bit.band(ffiC._DEBUG_LUNATIC, 2)~=0) then
|
|
require("jit").off()
|
|
end
|
|
|
|
if (bit.band(ffiC._DEBUG_LUNATIC, 8)~=0) then
|
|
require("dump").on("+rs")
|
|
elseif (bit.band(ffiC._DEBUG_LUNATIC, 4)~=0) then
|
|
require("v").on()
|
|
end
|
|
|
|
local math = require("math")
|
|
local string = require("string")
|
|
local table = require("table")
|
|
|
|
local assert = assert
|
|
local error = error
|
|
local pairs = pairs
|
|
local require = require
|
|
local setmetatable = setmetatable
|
|
local tostring = tostring
|
|
local type = type
|
|
|
|
local decl = decl
|
|
local getfenv = getfenv
|
|
|
|
decl "void OSD_Printf(const char *fmt, ...);"
|
|
print = function(str)
|
|
-- our "print" doesn't use the global "tostring", but the initial one
|
|
str = tostring(str)
|
|
if (type(str) ~= "string") then
|
|
error("invalid argument to print: must be convertible to a string")
|
|
end
|
|
ffiC.OSD_Printf("%s\n", str)
|
|
end
|
|
|
|
local print=print
|
|
|
|
|
|
module(...)
|
|
|
|
|
|
local band = bit.band
|
|
local bor = bit.bor
|
|
local bnot = bit.bnot
|
|
local lshift = bit.lshift
|
|
local rshift = bit.rshift
|
|
local xor = bit.bxor
|
|
|
|
|
|
--== bitint type factory ==--
|
|
|
|
-- Metatable for an integer type that is treated as bitfield. The integer
|
|
-- itself must be named '_v'.
|
|
local bitint_mt = {
|
|
__index = {
|
|
set = function(self, bits)
|
|
self._v = bor(self._v, bits)
|
|
end,
|
|
|
|
clear = function(self, bits)
|
|
self._v = band(self._v, bnot(bits))
|
|
end,
|
|
|
|
flip = function(self, bits)
|
|
self._v = xor(self._v, bits)
|
|
end,
|
|
|
|
test = function(self, bits)
|
|
return (band(self._v, bits) ~= 0)
|
|
end,
|
|
},
|
|
|
|
__metatable = true,
|
|
}
|
|
|
|
local bitint_to_base_type = {}
|
|
|
|
function bitint_new_struct_type(basetypename, newtypename)
|
|
assert(bitint_to_base_type[newtypename] == nil)
|
|
assert(type(basetypename)=="string")
|
|
assert(type(newtypename)=="string")
|
|
|
|
local bitint_struct_t = ffi.typeof("struct { $ _v; }", ffi.typeof(basetypename))
|
|
ffi.metatype(bitint_struct_t, bitint_mt)
|
|
ffi.cdef("typedef $ $;", bitint_struct_t, newtypename)
|
|
|
|
bitint_to_base_type[newtypename] = basetypename
|
|
end
|
|
|
|
function bitint_member(bitint_struct_typename, membname)
|
|
return string.format("union { %s %s; %s %sx; };",
|
|
bitint_to_base_type[bitint_struct_typename], membname,
|
|
bitint_struct_typename, membname)
|
|
end
|
|
|
|
bitint_new_struct_type("uint8_t", "UBit8")
|
|
bitint_new_struct_type("uint16_t", "UBit16")
|
|
|
|
|
|
--== Core engine structs ==--
|
|
|
|
local SECTOR_STRUCT = [[
|
|
struct {
|
|
const int16_t wallptr, wallnum;
|
|
int32_t ceilingz, floorz;
|
|
uint16_t ceilingstat, floorstat;
|
|
const int16_t ceilingpicnum;
|
|
int16_t ceilingheinum;
|
|
const int16_t ceilingbunch;
|
|
int8_t ceilingshade;
|
|
uint8_t ceilingpal, ceilingxpanning, ceilingypanning;
|
|
const int16_t floorpicnum;
|
|
int16_t floorheinum;
|
|
const int16_t floorbunch;
|
|
int8_t floorshade;
|
|
uint8_t floorpal, floorxpanning, floorypanning;
|
|
uint8_t visibility, filler;
|
|
int16_t lotag, hitag, extra;
|
|
}]]
|
|
|
|
local SPRITE_STRUCT = [[
|
|
struct {
|
|
int32_t x, y, z;
|
|
]]..bitint_member("UBit16", "cstat")..[[
|
|
const int16_t 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;
|
|
|
|
const int16_t owner;
|
|
int16_t xvel;
|
|
// NOTE: yvel is often used as player index in game code.
|
|
const int16_t yvel;
|
|
int16_t zvel;
|
|
|
|
int16_t lotag, hitag, extra;
|
|
}]]
|
|
|
|
local WALL_STRUCT = [[
|
|
struct {
|
|
int32_t x, y;
|
|
const int16_t point2, nextwall, nextsector;
|
|
const int16_t upwall, dnwall;
|
|
]]..bitint_member("UBit16", "cstat")..[[
|
|
const int16_t picnum, overpicnum;
|
|
int8_t shade;
|
|
uint8_t pal, xrepeat, yrepeat, xpanning, ypanning;
|
|
int16_t lotag, hitag, extra;
|
|
}]]
|
|
|
|
-- Converts a template struct definition to an internal, unrestricted one.
|
|
-- NOTE: "[^ ]*" for const decorations in defs.ilua.
|
|
function strip_const(structstr)
|
|
return (string.gsub(structstr, "const[^ ]* ", ""));
|
|
end
|
|
|
|
-- NOTE for FFI definitions: we're compiling EDuke32 with -funsigned-char, so
|
|
-- we need to take care to declare chars as unsigned whenever it matters, for
|
|
-- example if it represents a palette index. (I think it's harmless for stuff
|
|
-- like passing a function argument, but it should be done there for clarity.)
|
|
|
|
-- TODO: provide getters for unsigned {hi,lo}tag?
|
|
ffi.cdef([[
|
|
typedef $ sectortype;
|
|
typedef $ walltype;
|
|
// NOTE: spritetype and tspritetype are different types with the same data members.
|
|
typedef $ spritetype;
|
|
typedef struct { spritetype; } tspritetype;
|
|
|
|
typedef struct {
|
|
const uint32_t mdanimtims;
|
|
const int16_t mdanimcur;
|
|
int16_t angoff, pitch, roll;
|
|
// TODO: make into an ivec3_t
|
|
int32_t xoff, yoff, zoff;
|
|
]]..bitint_member("UBit8", "flags")..[[
|
|
uint8_t xpanning, ypanning;
|
|
const uint8_t filler;
|
|
float alpha;
|
|
union {
|
|
const intptr_t _tspr;
|
|
struct { const int32_t _dummy0, _dummy1; };
|
|
};
|
|
} spriteext_t;
|
|
|
|
typedef struct {
|
|
int32_t x, y, z;
|
|
} vec3_t;
|
|
|
|
typedef struct {
|
|
vec3_t pos;
|
|
int16_t sprite, wall, sect;
|
|
} hitdata_t;
|
|
]],
|
|
ffi.typeof(SECTOR_STRUCT), ffi.typeof(WALL_STRUCT),
|
|
ffi.typeof(SPRITE_STRUCT))
|
|
|
|
-- Define the "palette_t" type, which for us has .{r,g,b} fields and a
|
|
-- bound-checking array of length 3 overlaid.
|
|
local rgbarray_t = require("bcarray").new("uint8_t", 3, "RGB array", "palette_t",
|
|
{ "r", "g", "b", "f" })
|
|
ffi.cdef("typedef union { \
|
|
struct { uint8_t r, g, b, f; }; \
|
|
$ col; \
|
|
} palette_t", rgbarray_t)
|
|
assert(ffi.alignof("palette_t")==1)
|
|
|
|
local vec3_ct = ffi.typeof("vec3_t") -- will be metatype'd in geom.lua:
|
|
require("geom")
|
|
local hitdata_ct = ffi.typeof("hitdata_t")
|
|
|
|
decl[[
|
|
const int32_t engine_main_arrays_are_static;
|
|
const int32_t engine_v8;
|
|
]]
|
|
|
|
|
|
--== Engine data and functions ==--
|
|
|
|
|
|
-- NOTE TO SELF: This is not C, never EVER write
|
|
-- if (x)
|
|
-- when checking a C variable x for 'thuthiness'
|
|
if (ffiC.engine_main_arrays_are_static ~= 0) then
|
|
decl[[
|
|
sectortype sector[];
|
|
walltype wall[];
|
|
spritetype sprite[];
|
|
tspritetype tsprite[];
|
|
spriteext_t spriteext[];
|
|
]]
|
|
else
|
|
decl[[
|
|
sectortype *sector;
|
|
walltype *wall;
|
|
spritetype *sprite;
|
|
tspritetype *tsprite;
|
|
spriteext_t *spriteext;
|
|
]]
|
|
end
|
|
|
|
if (ffiC.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,
|
|
MAXTILES = 30720,
|
|
MAXSPRITESONSCREEN = 4096,
|
|
|
|
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; // XXX
|
|
const int32_t totalclock;
|
|
int32_t randomseed; // DEPRECATED
|
|
const int32_t xdim, ydim;
|
|
const int32_t windowx1, windowy1, windowx2, windowy2;
|
|
]]
|
|
|
|
decl[[
|
|
int32_t yxaspect;
|
|
int32_t viewingrange;
|
|
int32_t spritesortcnt;
|
|
int32_t guniqhudid;
|
|
const int32_t rendmode;
|
|
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 tilesizx[MAXTILES], tilesizy[MAXTILES];
|
|
|
|
uint8_t show2dsector[(MAXSECTORS+7)>>3];
|
|
|
|
const int16_t headsectbunch[2][MAXBUNCHES], nextsectbunch[2][MAXSECTORS];
|
|
|
|
int16_t yax_getbunch(int16_t i, int16_t cf);
|
|
|
|
int32_t getceilzofslopeptr(const sectortype *sec, int32_t dax, int32_t day);
|
|
int32_t getflorzofslopeptr(const sectortype *sec, int32_t dax, int32_t day);
|
|
void getzsofslopeptr(const sectortype *sec, int32_t dax, int32_t day,
|
|
int32_t *ceilz, int32_t *florz);
|
|
|
|
int32_t changespritesect(int16_t spritenum, int16_t newsectnum);
|
|
int32_t changespritestat(int16_t spritenum, int16_t newstatnum);
|
|
|
|
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);
|
|
int32_t cansee(int32_t x1, int32_t y1, int32_t z1, int16_t sect1,
|
|
int32_t x2, int32_t y2, int32_t z2, int16_t sect2);
|
|
void neartag(int32_t xs, int32_t ys, int32_t zs, int16_t sectnum, int16_t ange, int16_t *neartagsector, int16_t *neartagwall,
|
|
int16_t *neartagsprite, int32_t *neartaghitdist, int32_t neartagrange, uint8_t tagsearch,
|
|
int32_t (*blacklist_sprite_func)(int32_t));
|
|
void getzrange(const vec3_t *pos, int16_t sectnum,
|
|
int32_t *ceilz, int32_t *ceilhit, int32_t *florz, int32_t *florhit,
|
|
int32_t walldist, uint32_t cliptype);
|
|
|
|
int32_t ldist(const spritetype *s1, const spritetype *s2);
|
|
int32_t dist(const spritetype *s1, const spritetype *s2);
|
|
|
|
int32_t inside(int32_t x, int32_t y, int16_t sectnum);
|
|
void updatesector(int32_t x, int32_t y, int16_t *sectnum);
|
|
void updatesectorbreadth(int32_t x, int32_t y, int16_t *sectnum);
|
|
void updatesectorz(int32_t x, int32_t y, int32_t z, int16_t *sectnum);
|
|
|
|
void rotatesprite_(int32_t sx, int32_t sy, int32_t z, int16_t a, int16_t picnum,
|
|
int8_t dashade, unsigned char dapalnum, int32_t dastat, uint8_t alpha,
|
|
int32_t cx1, int32_t cy1, int32_t cx2, int32_t cy2);
|
|
|
|
void setaspect(int32_t daxrange, int32_t daaspect);
|
|
|
|
int32_t kopen4load(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);
|
|
int32_t klseek(int32_t handle, int32_t offset, int32_t whence);
|
|
]]
|
|
|
|
-- misc. functions
|
|
ffi.cdef[[
|
|
uint32_t getticks(void);
|
|
double gethitickms(void);
|
|
|
|
int32_t krand(void);
|
|
int32_t ksqrt(uint32_t num);
|
|
int32_t __fastcall getangle(int32_t xvect, int32_t yvect);
|
|
int32_t Mulscale(int32_t a, int32_t b, int32_t sh);
|
|
]]
|
|
|
|
local bcheck = require("bcheck")
|
|
local check_sector_idx = bcheck.sector_idx
|
|
local check_sprite_idx = bcheck.sprite_idx
|
|
local check_tile_idx = bcheck.tile_idx
|
|
|
|
local wallsofsec -- fwd-decl
|
|
|
|
local sectortype_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(SECTOR_STRUCT)))
|
|
|
|
local function get_sector_idx(sec)
|
|
local i = ffi.cast(sectortype_ptr_ct, sec)-ffi.cast(sectortype_ptr_ct, ffiC.sector)
|
|
assert(not (i >= ffiC.numsectors+0ULL))
|
|
return i
|
|
end
|
|
|
|
local zret = ffi.new("int32_t [4]")
|
|
local zret_t = ffi.typeof[[const struct {
|
|
struct {
|
|
bool spritep;
|
|
int32_t num; // number of sector or sprite
|
|
int32_t z;
|
|
} c, f;
|
|
}]]
|
|
|
|
local sectortype_mt = {
|
|
__index = {
|
|
--- Setters
|
|
set_ceilingpicnum = function(s, picnum)
|
|
check_tile_idx(picnum)
|
|
ffi.cast(sectortype_ptr_ct, s).ceilingpicnum = picnum
|
|
end,
|
|
|
|
set_floorpicnum = function(s, picnum)
|
|
check_tile_idx(picnum)
|
|
ffi.cast(sectortype_ptr_ct, s).floorpicnum = picnum
|
|
end,
|
|
|
|
--- Other methods
|
|
ceilingzat = function(s, pos)
|
|
return ffiC.getceilzofslopeptr(s, pos.x, pos.y)
|
|
end,
|
|
|
|
floorzat = function(s, pos)
|
|
return ffiC.getflorzofslopeptr(s, pos.x, pos.y)
|
|
end,
|
|
|
|
-- getzrange() interface
|
|
zrangeat = function(s, pos, walldist, cliptype)
|
|
local sectnum = get_sector_idx(s)
|
|
local ipos = vec3_ct(pos.x, pos.y, pos.z)
|
|
walldist = walldist or 128
|
|
cliptype = cliptype or ffiC.CLIPMASK0
|
|
|
|
ffiC.getzrange(ipos, sectnum, zret+0, zret+1, zret+2, zret+3,
|
|
walldist, cliptype)
|
|
local ceilz, ceilhit, florz, florhit = zret[0], zret[1], zret[2], zret[3]
|
|
|
|
return zret_t({ ceilhit>=49152, bit.band(ceilhit,16383), ceilz },
|
|
{ florhit>=49152, bit.band(florhit,16383), florz })
|
|
end,
|
|
|
|
-- inside() port
|
|
contains = function(s, pos)
|
|
local x, y = pos.x, pos.y
|
|
local cnt = 0
|
|
|
|
for w in wallsofsec(s) do
|
|
local wal2 = ffiC.wall[ffiC.wall[w].point2]
|
|
local y1, y2 = ffiC.wall[w].y-y, wal2.y-y
|
|
|
|
if (xor(y1, y2) < 0) then
|
|
local x1, x2 = ffiC.wall[w].x-x, wal2.x-x
|
|
|
|
if (xor(x1, x2)>=0) then
|
|
cnt = xor(cnt, x1)
|
|
else
|
|
cnt = xor(cnt, xor(x1*y2-x2*y1, y2))
|
|
end
|
|
end
|
|
end
|
|
|
|
return (cnt < 0)
|
|
end,
|
|
}
|
|
}
|
|
ffi.metatype("sectortype", sectortype_mt)
|
|
|
|
local walltype_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(WALL_STRUCT)))
|
|
|
|
local walltype_mt = {
|
|
__index = {
|
|
--- Setters
|
|
set_picnum = function(w, picnum)
|
|
check_tile_idx(picnum)
|
|
ffi.cast(walltype_ptr_ct, w).picnum = picnum
|
|
end,
|
|
|
|
set_overpicnum = function(w, picnum)
|
|
check_tile_idx(picnum)
|
|
ffi.cast(walltype_ptr_ct, w).overpicnum = picnum
|
|
end,
|
|
|
|
_set_nextwall = function(w, nextwall)
|
|
-- NOTE: Allow setting a wall to white too, but no checking of the
|
|
-- consistency invariant ".nextwall>=0 iff .nextsector>=0".
|
|
if (not (nextwall < 0)) then
|
|
bcheck.wall_idx(nextwall)
|
|
end
|
|
ffi.cast(walltype_ptr_ct, w).nextwall = nextwall
|
|
end,
|
|
|
|
_set_nextsector = function(w, nextsector)
|
|
if (not (nextsector < 0)) then
|
|
check_sector_idx(nextsector)
|
|
end
|
|
ffi.cast(walltype_ptr_ct, w).nextsector = nextsector
|
|
end,
|
|
|
|
--- Predicates
|
|
isblocking = function(w)
|
|
return (band(w.cstat, 1)~=0)
|
|
end,
|
|
|
|
ismasked = function(w)
|
|
return (band(w.cstat, 16)~=0)
|
|
end,
|
|
|
|
isoneway = function(w)
|
|
return (band(w.cstat, 32)~=0)
|
|
end,
|
|
|
|
ishittable = function(w)
|
|
return (band(w.cstat, 64)~=0)
|
|
end,
|
|
}
|
|
}
|
|
ffi.metatype("walltype", walltype_mt)
|
|
|
|
local spriteext_mt = {
|
|
__index = {
|
|
-- Enable EVENT_ANIMATESPRITES for this sprite.
|
|
make_animated = function(sx)
|
|
sx.flags = bor(sx.flags, 16)
|
|
end,
|
|
},
|
|
}
|
|
ffi.metatype("spriteext_t", spriteext_mt)
|
|
|
|
local spritetype_ptr_ct = ffi.typeof("$ *", ffi.typeof(strip_const(SPRITE_STRUCT)))
|
|
-- NOTE: this is the *protected* tspritetype pointer.
|
|
local tspritetype_ptr_ct = ffi.typeof("$ *", ffi.typeof("tspritetype"))
|
|
|
|
local spritetype_mt = {
|
|
__pow = function(s, zofs)
|
|
return vec3_ct(s.x, s.y, s.z-zofs)
|
|
end,
|
|
|
|
__index = {
|
|
--- Setters
|
|
set_picnum = function(s, tilenum)
|
|
check_tile_idx(tilenum)
|
|
ffi.cast(spritetype_ptr_ct, s).picnum = tilenum
|
|
end,
|
|
|
|
_set_yvel = function(s, yvel)
|
|
-- XXX: no protection against malicious use (might set picnum to
|
|
-- another one temporarily)
|
|
-- XXX: this belongs into game-side Lunatic
|
|
if (s.picnum==1405) then -- APLAYER
|
|
error("setting yvel on an APLAYER sprite forbidden", 2)
|
|
end
|
|
ffi.cast(spritetype_ptr_ct, s).yvel = yvel
|
|
end,
|
|
|
|
_set_owner = function(s, owner)
|
|
-- XXX: AMC TC sets owner to -1 in the cutscene.
|
|
check_sprite_idx(owner)
|
|
ffi.cast(spritetype_ptr_ct, s).owner = owner
|
|
end,
|
|
},
|
|
}
|
|
|
|
local function deep_copy(tab)
|
|
local ntab = {}
|
|
for key, val in pairs(tab) do
|
|
if (type(val)=="table") then
|
|
ntab[key] = deep_copy(val)
|
|
else
|
|
assert(type(val)=="function")
|
|
ntab[key] = val
|
|
end
|
|
end
|
|
return ntab
|
|
end
|
|
|
|
local tspritetype_mt = deep_copy(spritetype_mt)
|
|
|
|
local function get_sprite_idx(spr)
|
|
local i = ffi.cast(spritetype_ptr_ct, spr)-ffi.cast(spritetype_ptr_ct, ffiC.sprite)
|
|
assert(not (i >= ffiC.MAXSPRITES+0ULL))
|
|
return i
|
|
end
|
|
|
|
-- Methods that are specific to sprites
|
|
function spritetype_mt.__index.setpos(spr, pos) -- setsprite() clone
|
|
spr.x, spr.y, spr.z = pos.x, pos.y, pos.z
|
|
|
|
local newsect = updatesector(spr, spr.sectnum)
|
|
if (newsect < 0) then
|
|
return -1
|
|
end
|
|
|
|
if (spr.sectnum ~= newsect) then
|
|
ffiC.changespritesect(get_sprite_idx(spr), newsect)
|
|
end
|
|
return newsect
|
|
end
|
|
|
|
-- Methods that are specific to tsprites
|
|
function tspritetype_mt.__index.dup(tspr)
|
|
if (ffiC.spritesortcnt >= ffiC.MAXSPRITESONSCREEN+0ULL) then
|
|
return nil
|
|
end
|
|
|
|
local newtspr = ffiC.tsprite[ffiC.spritesortcnt]
|
|
ffi.copy(newtspr, tspr, ffi.sizeof(tspr))
|
|
ffiC.spritesortcnt = ffiC.spritesortcnt+1
|
|
|
|
return newtspr
|
|
end
|
|
|
|
function tspritetype_mt.__index.getspr(tspr)
|
|
return sprite[tspr.owner]
|
|
end
|
|
|
|
|
|
-- The user of this module can insert additional "spritetype" index
|
|
-- methods and register them with "ffi.metatype".
|
|
function finish_spritetype(mt_index)
|
|
for name, func in pairs(mt_index) do
|
|
spritetype_mt.__index[name] = func
|
|
tspritetype_mt.__index[name] = func
|
|
end
|
|
ffi.metatype("spritetype", spritetype_mt)
|
|
ffi.metatype("tspritetype", tspritetype_mt)
|
|
end
|
|
|
|
|
|
---=== Restricted access to C variables from Lunatic ===---
|
|
|
|
-- set metatable and forbid setting it further
|
|
function setmtonce(tab, mt)
|
|
mt.__metatable = true
|
|
return setmetatable(tab, mt)
|
|
end
|
|
|
|
---- indirect C array access ----
|
|
|
|
-- create a safe indirection for an ffi.C array
|
|
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()
|
|
error('cannot write directly to '..name, 2)
|
|
end,
|
|
}
|
|
|
|
return setmtonce(tab, tmpmt)
|
|
end
|
|
|
|
-- Construct const struct from table
|
|
function conststruct(tab)
|
|
local strtab = { "const struct { int32_t " }
|
|
local vals = {}
|
|
|
|
for member, val in pairs(tab) do
|
|
strtab[#strtab+1] = member..","
|
|
vals[#vals+1] = val
|
|
end
|
|
strtab[#strtab] = strtab[#strtab]:gsub(',',';')
|
|
strtab[#strtab+1] = "}"
|
|
|
|
return ffi.new(table.concat(strtab), vals)
|
|
end
|
|
|
|
-- Static, non-instance members. Used to hold constants, for example
|
|
-- sprite.CSTAT.TRANSLUCENT1
|
|
local static_members = { sector={}, wall={}, sprite={} }
|
|
|
|
static_members.sector.STAT = conststruct
|
|
{
|
|
PARALLAX = 1,
|
|
SLOPED = 2,
|
|
|
|
XFLIP = 16,
|
|
YFLIP = 32,
|
|
RELATIVE = 64,
|
|
MASKED = 128,
|
|
-- NOTE the reversed order
|
|
TRANSLUCENT2 = 128,
|
|
TRANSLUCENT1 = 256,
|
|
TRANSLUCENT_BOTH_BITS = 256+128,
|
|
BLOCKING = 512,
|
|
HITSCAN = 1024,
|
|
}
|
|
|
|
static_members.wall.CSTAT = conststruct
|
|
{
|
|
BLOCKING = 1,
|
|
XFLIP = 8,
|
|
MASKED = 16,
|
|
ONEWAY = 32,
|
|
HITSCAN = 64,
|
|
TRANSLUCENT1 = 128,
|
|
YFLIP = 256,
|
|
TRANSLUCENT2 = 512,
|
|
TRANSLUCENT_BOTH_BITS = 512+128,
|
|
}
|
|
|
|
static_members.sprite.CSTAT = conststruct
|
|
{
|
|
BLOCKING = 1,
|
|
TRANSLUCENT1 = 2,
|
|
XFLIP = 4,
|
|
YFLIP = 8,
|
|
WALLALIGN = 16,
|
|
FLOORALIGN = 32,
|
|
ONESIDED = 64,
|
|
CENTERED = 128,
|
|
HITSCAN = 256,
|
|
TRANSLUCENT2 = 512,
|
|
TRANSLUCENT_BOTH_BITS = 512+2,
|
|
}
|
|
|
|
local bitar = require("bitar")
|
|
-- XXX: bitar uses int32_t arrays, while show2dsector[] is a uint8_t
|
|
-- array. Potential unaligned access. Also, only works on little-endian
|
|
-- machines. This sucks.
|
|
static_members.sector.showbitmap = bitar.new(ffiC.MAXSECTORS-1, ffi.cast("int32_t *", ffiC.show2dsector))
|
|
|
|
local sms = static_members.sprite
|
|
sms._headspritesect = creategtab(ffiC.headspritesect, ffiC.MAXSECTORS, 'headspritesect[]')
|
|
-- NOTE: don't allow freelist access
|
|
sms._headspritestat = creategtab(ffiC.headspritestat, ffiC.MAXSTATUS, 'headspritestat[]')
|
|
sms._nextspritesect = creategtab(ffiC.nextspritesect, ffiC.MAXSPRITES, 'nextspritesect[]')
|
|
sms._nextspritestat = creategtab(ffiC.nextspritestat, ffiC.MAXSPRITES, 'nextspritestat[]')
|
|
sms._prevspritesect = creategtab(ffiC.prevspritesect, ffiC.MAXSPRITES, 'prevspritesect[]')
|
|
sms._prevspritestat = creategtab(ffiC.prevspritestat, ffiC.MAXSPRITES, 'prevspritestat[]')
|
|
|
|
function static_members.sprite.changesect(spritenum, sectnum, noerr)
|
|
check_sprite_idx(spritenum)
|
|
check_sector_idx(sectnum)
|
|
if (ffiC.changespritesect(spritenum, sectnum)==-1 and not noerr) then
|
|
error("cannot change sector number of sprite not in the game world", 2)
|
|
end
|
|
end
|
|
|
|
function static_members.sprite.changestat(spritenum, statnum, noerr)
|
|
check_sprite_idx(spritenum)
|
|
if (statnum >= ffiC.MAXSTATUS+0ULL) then
|
|
error("invalid status number "..statnum, 2)
|
|
end
|
|
if (ffiC.changespritestat(spritenum, statnum)==-1 and not noerr) then
|
|
error("cannot change status number of sprite not in the game world", 2)
|
|
end
|
|
end
|
|
|
|
function GenStructMetatable(Structname, Boundname, StaticMembersTab)
|
|
StaticMembersTab = StaticMembersTab or static_members[Structname]
|
|
|
|
return {
|
|
__index = function(tab, key)
|
|
if (type(key)=="number") then
|
|
if (key >= 0 and key < ffiC[Boundname]) then
|
|
return ffiC[Structname][key]
|
|
end
|
|
error("out-of-bounds "..Structname.."[] read access with index "..key, 2)
|
|
elseif (type(key)=="string") then
|
|
return StaticMembersTab[key]
|
|
end
|
|
end,
|
|
|
|
__newindex = function() error("cannot write directly to "..Structname.."[]", 2) end,
|
|
}
|
|
end
|
|
|
|
local sector_mt = GenStructMetatable("sector", "numsectors")
|
|
local wall_mt = GenStructMetatable("wall", "numwalls")
|
|
local sprite_mt = GenStructMetatable("sprite", "MAXSPRITES")
|
|
|
|
local atsprite_mt = {
|
|
__index = function(tab, idx)
|
|
check_sprite_idx(idx)
|
|
|
|
local tspr = ffi.cast(tspritetype_ptr_ct, ffiC.spriteext[idx]._tspr)
|
|
if (tspr == nil) then
|
|
error("tsprite of actor "..idx.." unavailable", 2)
|
|
end
|
|
|
|
-- Return a reference to a tsprite[] element.
|
|
return tspr[0]
|
|
end,
|
|
|
|
__newindex = function() error('cannot write directly to atsprite[]', 2) end,
|
|
}
|
|
|
|
|
|
local vars_to_ignore = {}
|
|
for varname,_ in pairs(getfenv(1)) do
|
|
if (ffiC._DEBUG_LUNATIC ~= 0) then
|
|
print("IGNORE "..varname)
|
|
end
|
|
vars_to_ignore[varname] = true
|
|
end
|
|
|
|
--== ALL GLOBALS FROM HERE ON ARE EXPORTED UPWARDS (see create_globals() below) ==--
|
|
|
|
sector = setmtonce({}, sector_mt)
|
|
wall = setmtonce({}, wall_mt)
|
|
sprite = setmtonce({}, sprite_mt)
|
|
spriteext = creategtab(ffiC.spriteext, ffiC.MAXSPRITES, 'spriteext[]')
|
|
atsprite = setmtonce({}, atsprite_mt)
|
|
|
|
local function iter_wallsofsec(endwall, w)
|
|
w = w+1
|
|
if (w < endwall) then
|
|
return w
|
|
end
|
|
end
|
|
|
|
wallsofsec = function(sec) -- local
|
|
return iter_wallsofsec, sec.wallptr+sec.wallnum, sec.wallptr-1
|
|
end
|
|
|
|
function wallsofsect(sect)
|
|
check_sector_idx(sect)
|
|
return iter_wallsofsec, sector[sect].wallptr+sector[sect].wallnum, sector[sect].wallptr-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
|
|
|
|
-- sprites of sectnum iterator that allows deleting the iterated sprite
|
|
local function iter_spritesofsect_safe(tab, i)
|
|
if (i < 0) then
|
|
i = ffiC.headspritesect[-i]
|
|
else
|
|
i = tab[1]
|
|
end
|
|
|
|
if (i >= 0) then
|
|
tab[1] = ffiC.nextspritesect[i]
|
|
return i
|
|
end
|
|
end
|
|
|
|
function spritesofsect(sect, maydelete)
|
|
check_sector_idx(sect)
|
|
|
|
if (maydelete) then
|
|
return iter_spritesofsect_safe, { -1 }, -sect
|
|
else
|
|
return iter_spritesofsect, sect, -1
|
|
end
|
|
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
|
|
|
|
-- sprites of statnum iterator that allows deleting the iterated sprite
|
|
local function iter_spritesofstat_safe(tab, i)
|
|
if (i < 0) then
|
|
i = ffiC.headspritestat[-i]
|
|
else
|
|
i = tab[1]
|
|
end
|
|
|
|
if (i >= 0) then
|
|
tab[1] = ffiC.nextspritestat[i]
|
|
return i
|
|
end
|
|
end
|
|
|
|
function spritesofstat(stat, maydelete)
|
|
if (stat >= ffiC.MAXSTATUS+0ULL) then
|
|
error("passed invalid statnum to spritesofstat iterator", 2)
|
|
end
|
|
|
|
if (maydelete) then
|
|
return iter_spritesofstat_safe, { -1 }, -stat
|
|
else
|
|
return iter_spritesofstat, stat, -1
|
|
end
|
|
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 >= ffiC.numyaxbunches+0ULL) then
|
|
error("passed invalid bunchnum to sectorsofbunch iterator", 2)
|
|
end
|
|
if (not (cf == 0 or 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)
|
|
check_sector_idx(sectnum)
|
|
if (not (cf == 0 or 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
|
|
-- TODO: make v[xyz] be passed as one aggregate, too?
|
|
-- Additionally, permit different coordinates? (ang&horiz, ...)
|
|
function hitscan(pos, sectnum, vx,vy,vz, cliptype)
|
|
check_sector_idx(sectnum)
|
|
local vec = vec3_ct(pos.x, pos.y, pos.z)
|
|
local hitdata = hitdata_ct()
|
|
|
|
ffiC.hitscan(vec, sectnum, vx,vy,vz, hitdata, cliptype)
|
|
return hitdata
|
|
end
|
|
|
|
function cansee(pos1,sect1, pos2,sect2)
|
|
check_sector_idx(sect1)
|
|
check_sector_idx(sect2)
|
|
|
|
local ret = ffiC.cansee(pos1.x,pos1.y,pos1.z, sect1,
|
|
pos2.x,pos2.y,pos2.z, sect2)
|
|
return (ret~=0)
|
|
end
|
|
|
|
local neartag_ret_ct = ffi.typeof[[
|
|
const struct {
|
|
int32_t sector, wall, sprite;
|
|
int32_t dist;
|
|
}
|
|
]]
|
|
|
|
local function newar() return ffi.new("int16_t [1]") end
|
|
-- TODO: make tagsearch something more convenient
|
|
function neartag(pos, sectnum, ang, range, tagsearch)
|
|
check_sector_idx(sectnum)
|
|
local a, b, c, d = newar(), newar(), newar(), ffi.new("int32_t [1]")
|
|
ffiC.neartag(pos.x, pos.y, pos.z, sectnum, ang, a, b, c, d, range, tagsearch, nil)
|
|
return neartag_ret_ct(a[0], b[0], c[0], d[0])
|
|
end
|
|
|
|
function inside(pos, sectnum)
|
|
check_sector_idx(sectnum)
|
|
return (ffiC.inside(pos.x, pos.y, sectnum)==1)
|
|
end
|
|
|
|
-- TODO: should these rather be one function, and the specific kind of updating
|
|
-- controlled by an argument?
|
|
local us_retsect = ffi.new("int16_t [1]")
|
|
|
|
function updatesector(pos, sectnum)
|
|
us_retsect[0] = sectnum
|
|
ffiC.updatesector(pos.x, pos.y, us_retsect)
|
|
return us_retsect[0]
|
|
end
|
|
|
|
function updatesectorbreadth(pos, sectnum)
|
|
us_retsect[0] = sectnum
|
|
ffiC.updatesectorbreadth(pos.x, pos.y, us_retsect)
|
|
return us_retsect[0]
|
|
end
|
|
|
|
function updatesectorz(pos, sectnum)
|
|
us_retsect[0] = sectnum
|
|
ffiC.updatesectorz(pos.x, pos.y, pos.z, us_retsect)
|
|
return us_retsect[0]
|
|
end
|
|
|
|
function printf(fmt, ...)
|
|
print(string.format(fmt, ...))
|
|
end
|
|
|
|
|
|
-- This is supposed to be run from the file that 'require's this module to take
|
|
-- over the non-local variables from here into its global environment.
|
|
function create_globals(_G_their)
|
|
local _G_our = getfenv(1)
|
|
vars_to_ignore["create_globals"] = true
|
|
|
|
for varname,obj in pairs(_G_our) do
|
|
if (not vars_to_ignore[varname]) then
|
|
if (ffiC._DEBUG_LUNATIC ~= 0) then
|
|
print("EXPORT "..varname)
|
|
end
|
|
_G_their[varname] = obj
|
|
end
|
|
end
|
|
end
|