mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-26 03:30:46 +00:00
- removed Lunatic remains.
This looks as dead as it could be, so away it goes.
This commit is contained in:
parent
639d1fb4d3
commit
7721ed31c0
182 changed files with 0 additions and 54282 deletions
|
@ -150,26 +150,14 @@ EDUKE32_STATIC_ASSERT(7 <= MAXTILES-MAXUSERTILES);
|
||||||
#include "sounds.h"
|
#include "sounds.h"
|
||||||
#include "soundsdyn.h"
|
#include "soundsdyn.h"
|
||||||
|
|
||||||
#ifdef LUNATIC
|
|
||||||
# include "lunatic_game.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline int32_t G_HaveActor(int spriteNum)
|
static inline int32_t G_HaveActor(int spriteNum)
|
||||||
{
|
{
|
||||||
#ifdef LUNATIC
|
|
||||||
return El_HaveActor(spriteNum);
|
|
||||||
#else
|
|
||||||
return g_tile[spriteNum].execPtr!=NULL;
|
return g_tile[spriteNum].execPtr!=NULL;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int32_t G_DefaultActorHealth(int spriteNum)
|
static inline int32_t G_DefaultActorHealth(int spriteNum)
|
||||||
{
|
{
|
||||||
#ifdef LUNATIC
|
|
||||||
return g_elActors[spriteNum].strength;
|
|
||||||
#else
|
|
||||||
return G_HaveActor(spriteNum) ? g_tile[spriteNum].execPtr[0] : 0;
|
return G_HaveActor(spriteNum) ? g_tile[spriteNum].execPtr[0] : 0;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,10 +32,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
#include "savegame.h"
|
#include "savegame.h"
|
||||||
#include "scriplib.h"
|
#include "scriplib.h"
|
||||||
|
|
||||||
#ifdef LUNATIC
|
|
||||||
# include "lunatic_game.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "vfs.h"
|
#include "vfs.h"
|
||||||
|
|
||||||
#if KRANDDEBUG
|
#if KRANDDEBUG
|
||||||
|
|
|
@ -28,10 +28,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
#include "gamedef.h" // vmstate_t
|
#include "gamedef.h" // vmstate_t
|
||||||
#include "sector.h" // mapstate_t
|
#include "sector.h" // mapstate_t
|
||||||
|
|
||||||
#ifdef LUNATIC
|
|
||||||
# include "lunatic_game.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum, int const nDist, int32_t const nReturn);
|
int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum, int const nDist, int32_t const nReturn);
|
||||||
int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum, int const nDist);
|
int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum, int const nDist);
|
||||||
int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum);
|
int32_t VM_ExecuteEvent(int const nEventID, int const spriteNum, int const playerNum);
|
||||||
|
@ -39,11 +35,7 @@ int32_t VM_ExecuteEventWithValue(int const nEventID, int const spriteNum, int co
|
||||||
|
|
||||||
static FORCE_INLINE bool VM_HaveEvent(int const nEventID)
|
static FORCE_INLINE bool VM_HaveEvent(int const nEventID)
|
||||||
{
|
{
|
||||||
#ifdef LUNATIC
|
|
||||||
return L_IsInitialized(&g_ElState) && El_HaveEvent(nEventID);
|
|
||||||
#else
|
|
||||||
return !!apScriptEvents[nEventID];
|
return !!apScriptEvents[nEventID];
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static FORCE_INLINE int32_t VM_OnEvent(int nEventID, int spriteNum, int playerNum, int nDist, int32_t nReturn)
|
static FORCE_INLINE int32_t VM_OnEvent(int nEventID, int spriteNum, int playerNum, int nDist, int32_t nReturn)
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
-- INTERNAL
|
|
||||||
-- definitions of BUILD and game types for the Lunatic Interpreter
|
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local ffiC = ffi.C
|
|
||||||
|
|
||||||
ffi.cdef[[
|
|
||||||
enum {
|
|
||||||
LUNATIC_CLIENT_MAPSTER32 = 0,
|
|
||||||
LUNATIC_CLIENT_EDUKE32 = 1,
|
|
||||||
|
|
||||||
LUNATIC_CLIENT = LUNATIC_CLIENT_MAPSTER32
|
|
||||||
}
|
|
||||||
]]
|
|
||||||
|
|
||||||
--== First, load the definitions common to the game's and editor's Lua interface.
|
|
||||||
decl = ffi.cdef
|
|
||||||
local defs_c = require("defs_common")
|
|
||||||
defs_c.finish_spritetype({})
|
|
||||||
|
|
||||||
defs_c.create_globals(_G)
|
|
||||||
|
|
||||||
-- TODO: provide access to only a subset, restict access to ffiC?
|
|
||||||
gv = ffiC
|
|
||||||
|
|
||||||
--== Mapster32-specific initialization
|
|
||||||
|
|
||||||
ffi.cdef "char *listsearchpath(int32_t initp);"
|
|
||||||
|
|
||||||
-- Add the search path directories to the Lua load path.
|
|
||||||
local initp = 1
|
|
||||||
while (true) do
|
|
||||||
local sp_c = ffiC.listsearchpath(initp)
|
|
||||||
|
|
||||||
if (sp_c == nil) then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local sp = ffi.string(sp_c)
|
|
||||||
assert(sp:sub(-1)=='/')
|
|
||||||
package.path = sp..'?.lua;'..package.path
|
|
||||||
|
|
||||||
initp = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper functions
|
|
||||||
local package = package
|
|
||||||
local require = require
|
|
||||||
|
|
||||||
function reload(modname)
|
|
||||||
package.loaded[modname] = nil
|
|
||||||
return require(modname)
|
|
||||||
end
|
|
||||||
|
|
||||||
--print('Lua load path: '..package.path)
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,124 +0,0 @@
|
||||||
-- Implementation of a bound-checked array type factory for LuaJIT 2.0 or later.
|
|
||||||
--
|
|
||||||
-- Usage example:
|
|
||||||
--
|
|
||||||
-- > bcarray.new("int8_t", 3, "test", "three_pigs")
|
|
||||||
-- > a = ffi.new("struct { int32_t a; three_pigs p; int16_t b; }")
|
|
||||||
-- > =ffi.sizeof(a) --> 12
|
|
||||||
-- > b = ffi.new("__attribute__((packed)) struct { int32_t a; three_pigs p; int16_t b; }")
|
|
||||||
-- > =ffi.sizeof(b) --> 9
|
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
|
|
||||||
local string = require("string")
|
|
||||||
local table = require("table")
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local error = error
|
|
||||||
local pairs = pairs
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
|
|
||||||
local bcarray = {}
|
|
||||||
|
|
||||||
|
|
||||||
-- Generate C decl for a sequence of <nelts> const struct members.
|
|
||||||
-- For example, for 4 elements,
|
|
||||||
-- "const $ _r1, _f2, _u3, _n4;"
|
|
||||||
local function flatten_array(nelts, rng)
|
|
||||||
local strtab = { "$ " }
|
|
||||||
|
|
||||||
if (rng and rng.getu32==nil) then
|
|
||||||
assert(type(rng)=="table")
|
|
||||||
|
|
||||||
for i=1,#rng do
|
|
||||||
strtab[i+1] = rng[i]..((i<#rng) and "," or ";")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for i=1,nelts do
|
|
||||||
local ch = 97 + (rng and (rng:getu32() % 25) or 0) -- 'a'..'z'
|
|
||||||
strtab[i+1] = string.format("_%c%x%s", ch, i, (i<nelts) and "," or ";")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.concat(strtab)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ctype = bcarray.new(basetype, numelts, showname [, typename] [, rng] [, mtadd])
|
|
||||||
-- (optional fields may be nil)
|
|
||||||
--
|
|
||||||
-- <numelts>: Number of elements in array (small number)
|
|
||||||
-- <showname>: The name to be shown for the derived type in error messages
|
|
||||||
-- <typename>: If non-nil, the name under which the derived type is typedef'd
|
|
||||||
-- <rng>: Random generator state + method :getu32(). If nil, then members are
|
|
||||||
-- named _a1, _a2, ...
|
|
||||||
-- It also may be a table containing member names at numeric indices 1..#rng.
|
|
||||||
-- <mtadd>: A table containing functions __index and/or __newindex. They are
|
|
||||||
-- called first and the bound-checking ones are tail-called then. If the
|
|
||||||
-- custom __index one returns something, it is returned by the composite one.
|
|
||||||
function bcarray.new(basetype, numelts, showname, typename, rng, mtadd)
|
|
||||||
local eltptr_t = ffi.typeof("$ *", ffi.typeof(basetype))
|
|
||||||
|
|
||||||
local mt = {
|
|
||||||
__index = function(ar, idx)
|
|
||||||
if (not (idx >= 0 and idx < numelts)) then
|
|
||||||
error("out-of-bounds "..showname.." read access", 2)
|
|
||||||
end
|
|
||||||
return ffi.cast(eltptr_t, ar)[idx]
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- NOTE: this function will be dead code if the prefixed __newindex
|
|
||||||
-- errors out unconditionally or the bcarray is declared 'const'.
|
|
||||||
__newindex = function(ar, idx, val)
|
|
||||||
if (not (idx >= 0 and idx < numelts)) then
|
|
||||||
error("out-of-bounds "..showname.." write access", 2)
|
|
||||||
end
|
|
||||||
ffi.cast(eltptr_t, ar)[idx] = val
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mtadd ~= nil) then
|
|
||||||
local curindexf, curnewindexf = mt.__index, mt.__newindex
|
|
||||||
local addindexf, addnewindexf = mtadd.__index, mtadd.__newindex
|
|
||||||
|
|
||||||
if (addindexf) then
|
|
||||||
-- Additional __index metamethod given.
|
|
||||||
mt.__index = function(ar, idx)
|
|
||||||
local sth = addindexf(ar, idx)
|
|
||||||
if (sth ~= nil) then
|
|
||||||
return sth
|
|
||||||
end
|
|
||||||
return curindexf(ar, idx)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (addnewindexf) then
|
|
||||||
-- Additional __newindex metamethod given.
|
|
||||||
mt.__newindex = function(ar, idx, val)
|
|
||||||
addnewindexf(ar, idx, val)
|
|
||||||
return curnewindexf(ar, idx, val)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local cdeclstr = "struct {"..flatten_array(numelts, rng).."}"
|
|
||||||
local bcarray_t = ffi.typeof(cdeclstr, ffi.typeof(basetype));
|
|
||||||
|
|
||||||
bcarray_t = ffi.metatype(bcarray_t, mt)
|
|
||||||
if (not (rng and rng.getu32==nil)) then
|
|
||||||
-- When passing a member name table, it is allowed to have a different
|
|
||||||
-- number of named members than array elements.
|
|
||||||
assert(ffi.sizeof(bcarray_t) == ffi.sizeof(basetype)*numelts)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (typename ~= nil) then
|
|
||||||
-- Register the type name in the global namespace.
|
|
||||||
assert(type(typename)=="string")
|
|
||||||
ffi.cdef("typedef $ $;", bcarray_t, typename)
|
|
||||||
end
|
|
||||||
|
|
||||||
return bcarray_t
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
return bcarray
|
|
|
@ -1,126 +0,0 @@
|
||||||
-- Bound-checking functions for engine and game "things".
|
|
||||||
|
|
||||||
local ffiC = require("ffi").C
|
|
||||||
local type = type
|
|
||||||
local error = error
|
|
||||||
|
|
||||||
local bcheck = {}
|
|
||||||
|
|
||||||
--== ENGINE ==--
|
|
||||||
|
|
||||||
function bcheck.sector_idx(sectnum)
|
|
||||||
if (not (sectnum >= 0 and sectnum < ffiC.numsectors)) then
|
|
||||||
error("invalid sector number "..sectnum, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.wall_idx(wallnum)
|
|
||||||
if (not (wallnum >= 0 and wallnum < ffiC.numwalls)) then
|
|
||||||
error("invalid wall number "..wallnum, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO: Provide another function that also checks whether the sprite exists in
|
|
||||||
-- the game world (statnum != MAXSTATUS).
|
|
||||||
function bcheck.sprite_idx(spritenum)
|
|
||||||
-- if (not (spritenum >= 0 and spritenum < ffiC.MAXSPRITES)) then
|
|
||||||
if (not (spritenum >= 0 and spritenum < ffiC.MAXSPRITES)) then
|
|
||||||
error("invalid sprite number "..spritenum, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.tile_idx(tilenum)
|
|
||||||
if (not (tilenum >= 0 and tilenum < ffiC.MAXTILES)) then
|
|
||||||
error("invalid tile number "..tilenum, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--== HELPERS ==--
|
|
||||||
|
|
||||||
function bcheck.number(val, errlev)
|
|
||||||
if (type(val)~="number" or val~=val) then
|
|
||||||
error("invalid argument: must be a non-NaN number", errlev or 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.type(val, typestr, errlev)
|
|
||||||
if (type(val)~=typestr) then
|
|
||||||
error("invalid argument: must be a "..typestr, errlev or 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--== GAME ==--
|
|
||||||
if (ffiC.LUNATIC_CLIENT == ffiC.LUNATIC_CLIENT_MAPSTER32) then
|
|
||||||
return bcheck
|
|
||||||
end
|
|
||||||
|
|
||||||
local con_lang = require("con_lang")
|
|
||||||
|
|
||||||
function bcheck.player_idx(snum)
|
|
||||||
if (not (snum >= 0 and snum < ffiC.g_mostConcurrentPlayers)) then
|
|
||||||
error("invalid player number "..snum, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.sound_idx(sndidx)
|
|
||||||
if (not (sndidx >= 0 and sndidx < con_lang.MAXSOUNDS)) then
|
|
||||||
error("invalid sound number "..sndidx, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.weapon_idx(weap)
|
|
||||||
if (not (weap >= 0 and weap < ffiC.MAX_WEAPONS)) then
|
|
||||||
error("Invalid weapon ID "..weap, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.inventory_idx(inv)
|
|
||||||
if (not (inv >= 0 and inv < ffiC.GET_MAX)) then
|
|
||||||
error("Invalid inventory ID "..inv, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.volume_idx(volume)
|
|
||||||
if (not (volume >= 0 and volume < con_lang.MAXVOLUMES)) then
|
|
||||||
error("invalid volume number "..volume, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.level_idx(level)
|
|
||||||
if (not (level >= 0 and level < con_lang.MAXLEVELS)) then
|
|
||||||
error("invalid level number "..level, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.linear_map_idx(idx)
|
|
||||||
if (not (idx >= 0 and idx <= con_lang.MAXLEVELS * con_lang.MAXVOLUMES)) then
|
|
||||||
error("invalid linear map index "..idx, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.quote_idx(qnum, onlyidx)
|
|
||||||
if (not (qnum >= 0 and qnum < con_lang.MAXQUOTES)) then
|
|
||||||
error("invalid quote number "..qnum, 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
local cstr = ffiC.apStrings[qnum]
|
|
||||||
if (onlyidx) then
|
|
||||||
return cstr
|
|
||||||
end
|
|
||||||
|
|
||||||
if (cstr == nil) then
|
|
||||||
error("null quote "..qnum, 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
return cstr
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.top_level(funcname, errlev)
|
|
||||||
if (ffiC.g_elCallDepth > 0) then
|
|
||||||
error("Invalid use of "..funcname..": must be called from top level", errlev or 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return bcheck
|
|
|
@ -1,293 +0,0 @@
|
||||||
|
|
||||||
-- "Bit array" module based on LuaJIT's BitOp.
|
|
||||||
|
|
||||||
local bit = require "bit"
|
|
||||||
local math = require "math"
|
|
||||||
|
|
||||||
local ffi = require "ffi"
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local error = error
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
local tostring = tostring
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
local bitar_ct = ffi.typeof[[
|
|
||||||
struct {
|
|
||||||
const double maxbidx; // last permissible bit index
|
|
||||||
const double maxidx; // last permissible int32_t index
|
|
||||||
const intptr_t arptr; // address of the int32_t array
|
|
||||||
}]]
|
|
||||||
local ptr_to_int = ffi.typeof("int32_t *")
|
|
||||||
|
|
||||||
local anchor = {}
|
|
||||||
|
|
||||||
-- population count of a nibble
|
|
||||||
local nibpop = ffi.new("double [?]", 16,
|
|
||||||
{ 0, 1, 1, 2, 1, 2, 2, 3,
|
|
||||||
1, 2, 2, 3, 2, 3, 3, 4 })
|
|
||||||
-- ...and of a byte
|
|
||||||
local bytepop = ffi.new("double [?]", 256)
|
|
||||||
for i=0,255 do
|
|
||||||
bytepop[i] = nibpop[bit.band(i, 15)] + nibpop[bit.rshift(i, 4)]
|
|
||||||
end
|
|
||||||
nibpop = nil
|
|
||||||
|
|
||||||
local function bitar_from_intar(maxbidx, maxidx, ar)
|
|
||||||
-- We need to have the int32_t[?] array be reachable so that it will not be
|
|
||||||
-- garbage collected
|
|
||||||
local ar_intptr = ffi.cast("intptr_t", ar)
|
|
||||||
anchor[tostring(ar_intptr)] = ar
|
|
||||||
|
|
||||||
-- Leaving the (potential) high trailing bits at 0 lets us not worry
|
|
||||||
-- about them in the population count calculation (__len metamethod).
|
|
||||||
-- Also, this is correct for maxbidx%32 == 0, since BitOp's shifts
|
|
||||||
-- mask the 5 lower bits of the counts.
|
|
||||||
local numremain = bit.band(maxbidx+1, 31)
|
|
||||||
ar[maxidx] = bit.band(ar[maxidx], bit.rshift(-1, 32-numremain))
|
|
||||||
|
|
||||||
return bitar_ct(maxbidx, maxidx, ar_intptr)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setop_common_rel(s1, s2)
|
|
||||||
if (s1.maxbidx ~= s2.maxbidx) then
|
|
||||||
error("bad arguments to bit array set op: must be of same length", 4)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ar1 = ffi.cast(ptr_to_int, s1.arptr)
|
|
||||||
local ar2 = ffi.cast(ptr_to_int, s2.arptr)
|
|
||||||
|
|
||||||
return ar1, ar2
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setop_common(s1, s2)
|
|
||||||
if (not ffi.istype(bitar_ct, s1) or not ffi.istype(bitar_ct, s2)) then
|
|
||||||
error("bad arguments to bit array set op: both must be 'bitar' types", 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ar1, ar2 = setop_common_rel(s1, s2)
|
|
||||||
local ar = ffi.new("int32_t [?]", s1.maxidx+1)
|
|
||||||
|
|
||||||
return ar, ar1, ar2
|
|
||||||
end
|
|
||||||
|
|
||||||
local mt = {
|
|
||||||
--- Operational methods
|
|
||||||
|
|
||||||
__add = function(s1, s2) -- set union
|
|
||||||
local ar, ar1, ar2 = setop_common(s1, s2)
|
|
||||||
for i=0,s1.maxidx do
|
|
||||||
ar[i] = bit.bor(ar1[i], ar2[i])
|
|
||||||
end
|
|
||||||
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
|
|
||||||
end,
|
|
||||||
|
|
||||||
__mul = function(s1, s2) -- set intersection
|
|
||||||
local ar, ar1, ar2 = setop_common(s1, s2)
|
|
||||||
for i=0,s1.maxidx do
|
|
||||||
ar[i] = bit.band(ar1[i], ar2[i])
|
|
||||||
end
|
|
||||||
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
|
|
||||||
end,
|
|
||||||
|
|
||||||
__sub = function(s1, s2) -- set difference
|
|
||||||
local ar, ar1, ar2 = setop_common(s1, s2)
|
|
||||||
for i=0,s1.maxidx do
|
|
||||||
ar[i] = bit.band(ar1[i], bit.bnot(ar2[i]))
|
|
||||||
end
|
|
||||||
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
|
|
||||||
end,
|
|
||||||
|
|
||||||
__unm = function(s) -- bitwise NOT
|
|
||||||
local newar = ffi.new("int32_t [?]", s.maxidx+1)
|
|
||||||
local oldar = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
for i=0,s.maxidx do
|
|
||||||
newar[i] = bit.bnot(oldar[i])
|
|
||||||
end
|
|
||||||
return bitar_from_intar(s.maxbidx, s.maxidx, newar)
|
|
||||||
end,
|
|
||||||
|
|
||||||
|
|
||||||
--- Additional operations
|
|
||||||
|
|
||||||
__index = {
|
|
||||||
-- TODO: Rename to 'testi', 'seti', 'cleari'; add 'flipi'?
|
|
||||||
|
|
||||||
-- Is bit i set?
|
|
||||||
isset = function(s, i)
|
|
||||||
if (not (i >= 0 and i<=s.maxbidx)) then
|
|
||||||
error("bad bit index for isset: must be in [0.."..s.maxbidx.."]", 2)
|
|
||||||
end
|
|
||||||
s = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
return (bit.band(s[bit.rshift(i, 5)], bit.lshift(1, i)) ~= 0)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Clear bit i.
|
|
||||||
set0 = function(s, i)
|
|
||||||
if (not (i >= 0 and i<=s.maxbidx)) then
|
|
||||||
error("bad bit index for set0: must be in [0.."..s.maxbidx.."]", 2)
|
|
||||||
end
|
|
||||||
s = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
local jx = bit.rshift(i, 5)
|
|
||||||
s[jx] = bit.band(s[jx], bit.rol(0xfffffffe, i))
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Set bit i.
|
|
||||||
set1 = function(s, i)
|
|
||||||
if (not (i >= 0 and i<=s.maxbidx)) then
|
|
||||||
error("bad bit index for set1: must be in [0.."..s.maxbidx.."]", 2)
|
|
||||||
end
|
|
||||||
s = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
local jx = bit.rshift(i, 5)
|
|
||||||
s[jx] = bit.bor(s[jx], bit.rol(0x00000001, i))
|
|
||||||
end
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
--- Relational methods
|
|
||||||
|
|
||||||
__eq = function(s1, s2) -- set identity
|
|
||||||
local ar1, ar2 = setop_common_rel(s1, s2)
|
|
||||||
for i=0,s1.maxidx do
|
|
||||||
if (bit.bxor(ar1[i], ar2[i]) ~= 0) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
|
|
||||||
__le = function(s1, s2)
|
|
||||||
local ar1, ar2 = setop_common_rel(s1, s2)
|
|
||||||
for i=0,s1.maxidx do
|
|
||||||
if (bit.band(ar1[i], bit.bnot(ar2[i])) ~= 0) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
|
|
||||||
__lt = function(s1, s2)
|
|
||||||
return s1 <= s2 and not (s2 == s1)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- The length operator gets the population count of the bit array, i.e. the
|
|
||||||
-- number of set bits.
|
|
||||||
__len = function(s)
|
|
||||||
local ar = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
local popcnt = 0
|
|
||||||
for i=0,s.maxidx do
|
|
||||||
popcnt = popcnt + bytepop[bit.band(ar[i], 255)] +
|
|
||||||
bytepop[bit.band(bit.rshift(ar[i], 8), 255)] +
|
|
||||||
bytepop[bit.band(bit.rshift(ar[i], 16), 255)] +
|
|
||||||
bytepop[bit.rshift(ar[i], 24)]
|
|
||||||
end
|
|
||||||
return popcnt
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- serialization
|
|
||||||
__tostring = function(s)
|
|
||||||
local size=s.maxidx+1
|
|
||||||
local ar = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
|
|
||||||
local hdr = "bitar.new("..s.maxbidx..", '"
|
|
||||||
local ofs = #hdr
|
|
||||||
local totalstrlen = ofs+8*size+2
|
|
||||||
local str = ffi.new("char [?]", totalstrlen)
|
|
||||||
|
|
||||||
ffi.copy(str, hdr, ofs)
|
|
||||||
|
|
||||||
for i=0,s.maxidx do
|
|
||||||
-- 'a' is ASCII 97
|
|
||||||
for nib=0,7 do
|
|
||||||
str[ofs + 8*i + nib] = 97 + bit.band(bit.rshift(ar[i], 4*nib), 0x0000000f)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ffi.copy(str+totalstrlen-2, "')", 2)
|
|
||||||
|
|
||||||
return ffi.string(str, totalstrlen)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- On garbage collection of the bitar, clear the array's anchor so that it
|
|
||||||
-- can be collected too.
|
|
||||||
__gc = function(s)
|
|
||||||
anchor[tostring(s.arptr)] = nil
|
|
||||||
end,
|
|
||||||
|
|
||||||
__metatable = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi.metatype(bitar_ct, mt)
|
|
||||||
|
|
||||||
|
|
||||||
-- Create new bit array.
|
|
||||||
function new(maxbidx, initval)
|
|
||||||
if (type(maxbidx) ~= "number" or not (maxbidx >= 0 and maxbidx <= (2^31)-2)) then
|
|
||||||
-- NOTE: Uh-oh, we can't write '2^31' because that would be interpreted
|
|
||||||
-- as color by OSD_Printf.
|
|
||||||
error("bad argument #1 to bitar.new (must be a number in [0..(2**31)-2])", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (math.floor(maxbidx) ~= maxbidx) then
|
|
||||||
error("bad argument #1 to bitar.new (must be an integral number)")
|
|
||||||
end
|
|
||||||
|
|
||||||
if (ffi.istype(ptr_to_int, initval)) then
|
|
||||||
-- Initialization from an array on the C side. INTERNAL.
|
|
||||||
-- Cannot be reached by user code since there's no access to the FFI
|
|
||||||
-- (and thus no way to create pointers).
|
|
||||||
|
|
||||||
return bitar_from_intar(maxbidx, (maxbidx+1)/32-1, initval)
|
|
||||||
|
|
||||||
elseif (type(initval)=="string") then
|
|
||||||
-- String containing hex digits (a..p) given, for INTERNAL use only.
|
|
||||||
-- XXX: Can be reached by user code.
|
|
||||||
|
|
||||||
local lstr = initval
|
|
||||||
|
|
||||||
local numnibs = #lstr
|
|
||||||
assert(numnibs%8 == 0)
|
|
||||||
|
|
||||||
local size = numnibs/8
|
|
||||||
local maxidx = size-1
|
|
||||||
local ar = ffi.new("int32_t [?]", size)
|
|
||||||
|
|
||||||
local str = ffi.new("char [?]", numnibs)
|
|
||||||
ffi.copy(str, lstr, numnibs)
|
|
||||||
|
|
||||||
for i=0,maxidx do
|
|
||||||
ar[i] = 0
|
|
||||||
|
|
||||||
for nib=0,7 do
|
|
||||||
local hexdig = str[8*i + nib]
|
|
||||||
assert(hexdig >= 97 and hexdig < 97+16)
|
|
||||||
ar[i] = bit.bor(ar[i], bit.lshift(hexdig-97, 4*nib))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- NOTE: <maxbidx> cannot be extracted from the string, use the passed one.
|
|
||||||
return bitar_from_intar(maxbidx, maxidx, ar)
|
|
||||||
|
|
||||||
else
|
|
||||||
-- User-requested bitar creation.
|
|
||||||
|
|
||||||
if (initval ~= 0 and initval ~= 1) then
|
|
||||||
error("bad argument #2 to bitar.new (must be either 0 or 1)", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local maxidx = math.floor(maxbidx/32)
|
|
||||||
local size = maxidx+1
|
|
||||||
|
|
||||||
local ar = ffi.new("int32_t [?]", size)
|
|
||||||
|
|
||||||
if (initval==1) then
|
|
||||||
ffi.fill(ar, size*4, -1)
|
|
||||||
end
|
|
||||||
|
|
||||||
return bitar_from_intar(maxbidx, maxidx, ar)
|
|
||||||
end
|
|
||||||
end
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,20 +0,0 @@
|
||||||
----------------------------------------------------------------------------
|
|
||||||
-- LuaJIT x64 disassembler wrapper module.
|
|
||||||
--
|
|
||||||
-- Copyright (C) 2005-2015 Mike Pall. All rights reserved.
|
|
||||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
|
||||||
----------------------------------------------------------------------------
|
|
||||||
-- This module just exports the 64 bit functions from the combined
|
|
||||||
-- x86/x64 disassembler module. All the interesting stuff is there.
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local require = require
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
local dis_x86 = require(_PACKAGE.."dis_x86")
|
|
||||||
|
|
||||||
create = dis_x86.create64
|
|
||||||
disass = dis_x86.disass64
|
|
||||||
regname = dis_x86.regname64
|
|
||||||
|
|
|
@ -1,836 +0,0 @@
|
||||||
----------------------------------------------------------------------------
|
|
||||||
-- LuaJIT x86/x64 disassembler module.
|
|
||||||
--
|
|
||||||
-- Copyright (C) 2005-2015 Mike Pall. All rights reserved.
|
|
||||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
|
||||||
----------------------------------------------------------------------------
|
|
||||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
|
||||||
--
|
|
||||||
-- Sending small code snippets to an external disassembler and mixing the
|
|
||||||
-- output with our own stuff was too fragile. So I had to bite the bullet
|
|
||||||
-- and write yet another x86 disassembler. Oh well ...
|
|
||||||
--
|
|
||||||
-- The output format is very similar to what ndisasm generates. But it has
|
|
||||||
-- been developed independently by looking at the opcode tables from the
|
|
||||||
-- Intel and AMD manuals. The supported instruction set is quite extensive
|
|
||||||
-- and reflects what a current generation Intel or AMD CPU implements in
|
|
||||||
-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
|
|
||||||
-- SSE4.1, SSE4.2, SSE4a and even privileged and hypervisor (VMX/SVM)
|
|
||||||
-- instructions.
|
|
||||||
--
|
|
||||||
-- Notes:
|
|
||||||
-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
|
|
||||||
-- * No attempt at optimization has been made -- it's fast enough for my needs.
|
|
||||||
-- * The public API may change when more architectures are added.
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local type = type
|
|
||||||
local sub, byte, format = string.sub, string.byte, string.format
|
|
||||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
|
||||||
local lower, rep = string.lower, string.rep
|
|
||||||
|
|
||||||
-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
|
|
||||||
local map_opc1_32 = {
|
|
||||||
--0x
|
|
||||||
[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
|
|
||||||
"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
|
|
||||||
--1x
|
|
||||||
"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
|
|
||||||
"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
|
|
||||||
--2x
|
|
||||||
"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
|
|
||||||
"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
|
|
||||||
--3x
|
|
||||||
"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
|
|
||||||
"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
|
|
||||||
--4x
|
|
||||||
"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
|
|
||||||
"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
|
|
||||||
--5x
|
|
||||||
"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
|
|
||||||
"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
|
|
||||||
--6x
|
|
||||||
"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
|
|
||||||
"fs:seg","gs:seg","o16:","a16",
|
|
||||||
"pushUi","imulVrmi","pushBs","imulVrms",
|
|
||||||
"insb","insVS","outsb","outsVS",
|
|
||||||
--7x
|
|
||||||
"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
|
|
||||||
"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
|
|
||||||
--8x
|
|
||||||
"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
|
|
||||||
"testBmr","testVmr","xchgBrm","xchgVrm",
|
|
||||||
"movBmr","movVmr","movBrm","movVrm",
|
|
||||||
"movVmg","leaVrm","movWgm","popUm",
|
|
||||||
--9x
|
|
||||||
"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
|
|
||||||
"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
|
|
||||||
"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
|
|
||||||
"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
|
|
||||||
--Ax
|
|
||||||
"movBao","movVao","movBoa","movVoa",
|
|
||||||
"movsb","movsVS","cmpsb","cmpsVS",
|
|
||||||
"testBai","testVai","stosb","stosVS",
|
|
||||||
"lodsb","lodsVS","scasb","scasVS",
|
|
||||||
--Bx
|
|
||||||
"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
|
|
||||||
"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
|
|
||||||
--Cx
|
|
||||||
"shift!Bmu","shift!Vmu","retBw","ret","$lesVrm","$ldsVrm","movBmi","movVmi",
|
|
||||||
"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
|
|
||||||
--Dx
|
|
||||||
"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
|
|
||||||
"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
|
|
||||||
--Ex
|
|
||||||
"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
|
|
||||||
"inBau","inVau","outBua","outVua",
|
|
||||||
"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
|
|
||||||
--Fx
|
|
||||||
"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
|
|
||||||
"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
|
|
||||||
}
|
|
||||||
assert(#map_opc1_32 == 255)
|
|
||||||
|
|
||||||
-- Map for 1st opcode byte in 64 bit mode (overrides only).
|
|
||||||
local map_opc1_64 = setmetatable({
|
|
||||||
[0x06]=false, [0x07]=false, [0x0e]=false,
|
|
||||||
[0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
|
|
||||||
[0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
|
|
||||||
[0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
|
|
||||||
[0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
|
|
||||||
[0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
|
|
||||||
[0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
|
|
||||||
[0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
|
|
||||||
[0x82]=false, [0x9a]=false, [0xc4]=false, [0xc5]=false, [0xce]=false,
|
|
||||||
[0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
|
|
||||||
}, { __index = map_opc1_32 })
|
|
||||||
|
|
||||||
-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
|
|
||||||
-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
|
|
||||||
local map_opc2 = {
|
|
||||||
--0x
|
|
||||||
[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
|
|
||||||
"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
|
|
||||||
--1x
|
|
||||||
"movupsXrm|movssXrm|movupdXrm|movsdXrm",
|
|
||||||
"movupsXmr|movssXmr|movupdXmr|movsdXmr",
|
|
||||||
"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
|
|
||||||
"movlpsXmr||movlpdXmr",
|
|
||||||
"unpcklpsXrm||unpcklpdXrm",
|
|
||||||
"unpckhpsXrm||unpckhpdXrm",
|
|
||||||
"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
|
|
||||||
"movhpsXmr||movhpdXmr",
|
|
||||||
"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
|
|
||||||
"hintnopVm","hintnopVm","hintnopVm","hintnopVm",
|
|
||||||
--2x
|
|
||||||
"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
|
|
||||||
"movapsXrm||movapdXrm",
|
|
||||||
"movapsXmr||movapdXmr",
|
|
||||||
"cvtpi2psXrMm|cvtsi2ssXrVmt|cvtpi2pdXrMm|cvtsi2sdXrVmt",
|
|
||||||
"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
|
|
||||||
"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
|
|
||||||
"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
|
|
||||||
"ucomissXrm||ucomisdXrm",
|
|
||||||
"comissXrm||comisdXrm",
|
|
||||||
--3x
|
|
||||||
"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
|
|
||||||
"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
|
|
||||||
--4x
|
|
||||||
"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
|
|
||||||
"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
|
|
||||||
"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
|
|
||||||
"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
|
|
||||||
--5x
|
|
||||||
"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
|
|
||||||
"rsqrtpsXrm|rsqrtssXrm","rcppsXrm|rcpssXrm",
|
|
||||||
"andpsXrm||andpdXrm","andnpsXrm||andnpdXrm",
|
|
||||||
"orpsXrm||orpdXrm","xorpsXrm||xorpdXrm",
|
|
||||||
"addpsXrm|addssXrm|addpdXrm|addsdXrm","mulpsXrm|mulssXrm|mulpdXrm|mulsdXrm",
|
|
||||||
"cvtps2pdXrm|cvtss2sdXrm|cvtpd2psXrm|cvtsd2ssXrm",
|
|
||||||
"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
|
|
||||||
"subpsXrm|subssXrm|subpdXrm|subsdXrm","minpsXrm|minssXrm|minpdXrm|minsdXrm",
|
|
||||||
"divpsXrm|divssXrm|divpdXrm|divsdXrm","maxpsXrm|maxssXrm|maxpdXrm|maxsdXrm",
|
|
||||||
--6x
|
|
||||||
"punpcklbwPrm","punpcklwdPrm","punpckldqPrm","packsswbPrm",
|
|
||||||
"pcmpgtbPrm","pcmpgtwPrm","pcmpgtdPrm","packuswbPrm",
|
|
||||||
"punpckhbwPrm","punpckhwdPrm","punpckhdqPrm","packssdwPrm",
|
|
||||||
"||punpcklqdqXrm","||punpckhqdqXrm",
|
|
||||||
"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
|
|
||||||
--7x
|
|
||||||
"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pmu",
|
|
||||||
"pshiftd!Pmu","pshiftq!Mmu||pshiftdq!Xmu",
|
|
||||||
"pcmpeqbPrm","pcmpeqwPrm","pcmpeqdPrm","emms|",
|
|
||||||
"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
|
|
||||||
nil,nil,
|
|
||||||
"||haddpdXrm|haddpsXrm","||hsubpdXrm|hsubpsXrm",
|
|
||||||
"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
|
|
||||||
--8x
|
|
||||||
"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
|
|
||||||
"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
|
|
||||||
--9x
|
|
||||||
"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
|
|
||||||
"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
|
|
||||||
--Ax
|
|
||||||
"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
|
|
||||||
"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
|
|
||||||
--Bx
|
|
||||||
"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
|
|
||||||
"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
|
|
||||||
"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
|
|
||||||
"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
|
|
||||||
--Cx
|
|
||||||
"xaddBmr","xaddVmr",
|
|
||||||
"cmppsXrmu|cmpssXrmu|cmppdXrmu|cmpsdXrmu","$movntiVmr|",
|
|
||||||
"pinsrwPrWmu","pextrwDrPmu",
|
|
||||||
"shufpsXrmu||shufpdXrmu","$cmpxchg!Qmp",
|
|
||||||
"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
|
|
||||||
--Dx
|
|
||||||
"||addsubpdXrm|addsubpsXrm","psrlwPrm","psrldPrm","psrlqPrm",
|
|
||||||
"paddqPrm","pmullwPrm",
|
|
||||||
"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
|
|
||||||
"psubusbPrm","psubuswPrm","pminubPrm","pandPrm",
|
|
||||||
"paddusbPrm","padduswPrm","pmaxubPrm","pandnPrm",
|
|
||||||
--Ex
|
|
||||||
"pavgbPrm","psrawPrm","psradPrm","pavgwPrm",
|
|
||||||
"pmulhuwPrm","pmulhwPrm",
|
|
||||||
"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
|
|
||||||
"psubsbPrm","psubswPrm","pminswPrm","porPrm",
|
|
||||||
"paddsbPrm","paddswPrm","pmaxswPrm","pxorPrm",
|
|
||||||
--Fx
|
|
||||||
"|||lddquXrm","psllwPrm","pslldPrm","psllqPrm",
|
|
||||||
"pmuludqPrm","pmaddwdPrm","psadbwPrm","maskmovqMrm||maskmovdquXrm$",
|
|
||||||
"psubbPrm","psubwPrm","psubdPrm","psubqPrm",
|
|
||||||
"paddbPrm","paddwPrm","padddPrm","ud",
|
|
||||||
}
|
|
||||||
assert(map_opc2[255] == "ud")
|
|
||||||
|
|
||||||
-- Map for three-byte opcodes. Can't wait for their next invention.
|
|
||||||
local map_opc3 = {
|
|
||||||
["38"] = { -- [66] 0f 38 xx
|
|
||||||
--0x
|
|
||||||
[0]="pshufbPrm","phaddwPrm","phadddPrm","phaddswPrm",
|
|
||||||
"pmaddubswPrm","phsubwPrm","phsubdPrm","phsubswPrm",
|
|
||||||
"psignbPrm","psignwPrm","psigndPrm","pmulhrswPrm",
|
|
||||||
nil,nil,nil,nil,
|
|
||||||
--1x
|
|
||||||
"||pblendvbXrma",nil,nil,nil,
|
|
||||||
"||blendvpsXrma","||blendvpdXrma",nil,"||ptestXrm",
|
|
||||||
nil,nil,nil,nil,
|
|
||||||
"pabsbPrm","pabswPrm","pabsdPrm",nil,
|
|
||||||
--2x
|
|
||||||
"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
|
|
||||||
"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
|
|
||||||
"||pmuldqXrm","||pcmpeqqXrm","||$movntdqaXrm","||packusdwXrm",
|
|
||||||
nil,nil,nil,nil,
|
|
||||||
--3x
|
|
||||||
"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
|
|
||||||
"||pmovzxwqXrm","||pmovzxdqXrm",nil,"||pcmpgtqXrm",
|
|
||||||
"||pminsbXrm","||pminsdXrm","||pminuwXrm","||pminudXrm",
|
|
||||||
"||pmaxsbXrm","||pmaxsdXrm","||pmaxuwXrm","||pmaxudXrm",
|
|
||||||
--4x
|
|
||||||
"||pmulddXrm","||phminposuwXrm",
|
|
||||||
--Fx
|
|
||||||
[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
|
|
||||||
},
|
|
||||||
|
|
||||||
["3a"] = { -- [66] 0f 3a xx
|
|
||||||
--0x
|
|
||||||
[0x00]=nil,nil,nil,nil,nil,nil,nil,nil,
|
|
||||||
"||roundpsXrmu","||roundpdXrmu","||roundssXrmu","||roundsdXrmu",
|
|
||||||
"||blendpsXrmu","||blendpdXrmu","||pblendwXrmu","palignrPrmu",
|
|
||||||
--1x
|
|
||||||
nil,nil,nil,nil,
|
|
||||||
"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
|
|
||||||
nil,nil,nil,nil,nil,nil,nil,nil,
|
|
||||||
--2x
|
|
||||||
"||pinsrbXrVmu","||insertpsXrmu","||pinsrXrVmuS",nil,
|
|
||||||
--4x
|
|
||||||
[0x40] = "||dppsXrmu",
|
|
||||||
[0x41] = "||dppdXrmu",
|
|
||||||
[0x42] = "||mpsadbwXrmu",
|
|
||||||
--6x
|
|
||||||
[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
|
|
||||||
[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
|
|
||||||
local map_opcvm = {
|
|
||||||
[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
|
|
||||||
[0xc8]="monitor",[0xc9]="mwait",
|
|
||||||
[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
|
|
||||||
[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
|
|
||||||
[0xf8]="swapgs",[0xf9]="rdtscp",
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Map for FP opcodes. And you thought stack machines are simple?
|
|
||||||
local map_opcfp = {
|
|
||||||
-- D8-DF 00-BF: opcodes with a memory operand.
|
|
||||||
-- D8
|
|
||||||
[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
|
|
||||||
"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
|
|
||||||
-- DA
|
|
||||||
"fiaddDm","fimulDm","ficomDm","ficompDm",
|
|
||||||
"fisubDm","fisubrDm","fidivDm","fidivrDm",
|
|
||||||
-- DB
|
|
||||||
"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
|
|
||||||
-- DC
|
|
||||||
"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
|
|
||||||
-- DD
|
|
||||||
"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
|
|
||||||
-- DE
|
|
||||||
"fiaddWm","fimulWm","ficomWm","ficompWm",
|
|
||||||
"fisubWm","fisubrWm","fidivWm","fidivrWm",
|
|
||||||
-- DF
|
|
||||||
"fildWm","fisttpWm","fistWm","fistpWm",
|
|
||||||
"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
|
|
||||||
-- xx C0-FF: opcodes with a pseudo-register operand.
|
|
||||||
-- D8
|
|
||||||
"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
|
|
||||||
-- D9
|
|
||||||
"fldFf","fxchFf",{"fnop"},nil,
|
|
||||||
{"fchs","fabs",nil,nil,"ftst","fxam"},
|
|
||||||
{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
|
|
||||||
{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
|
|
||||||
{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
|
|
||||||
-- DA
|
|
||||||
"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
|
|
||||||
-- DB
|
|
||||||
"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
|
|
||||||
{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
|
|
||||||
-- DC
|
|
||||||
"fadd toFf","fmul toFf",nil,nil,
|
|
||||||
"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
|
|
||||||
-- DD
|
|
||||||
"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
|
|
||||||
-- DE
|
|
||||||
"faddpFf","fmulpFf",nil,{nil,"fcompp"},
|
|
||||||
"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
|
|
||||||
-- DF
|
|
||||||
nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
|
|
||||||
}
|
|
||||||
assert(map_opcfp[126] == "fcomipFf")
|
|
||||||
|
|
||||||
-- Map for opcode groups. The subkey is sp from the ModRM byte.
|
|
||||||
local map_opcgroup = {
|
|
||||||
arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
|
|
||||||
shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
|
|
||||||
testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
|
|
||||||
testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
|
|
||||||
incb = { "inc", "dec" },
|
|
||||||
incd = { "inc", "dec", "callUmp", "$call farDmp",
|
|
||||||
"jmpUmp", "$jmp farDmp", "pushUm" },
|
|
||||||
sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
|
|
||||||
sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
|
|
||||||
"smsw", nil, "lmsw", "vm*$invlpg" },
|
|
||||||
bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
|
|
||||||
cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
|
|
||||||
nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
|
|
||||||
pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
|
|
||||||
pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
|
|
||||||
pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
|
|
||||||
pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
|
|
||||||
fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
|
|
||||||
nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
|
|
||||||
prefetch = { "prefetch", "prefetchw" },
|
|
||||||
prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
|
|
||||||
}
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Maps for register names.
|
|
||||||
local map_regs = {
|
|
||||||
B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
|
|
||||||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
|
|
||||||
B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
|
|
||||||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
|
|
||||||
W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
|
|
||||||
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
|
|
||||||
D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
|
|
||||||
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
|
|
||||||
Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
|
||||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
|
|
||||||
M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
|
|
||||||
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
|
|
||||||
X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
|
|
||||||
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
|
|
||||||
}
|
|
||||||
local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
|
|
||||||
|
|
||||||
-- Maps for size names.
|
|
||||||
local map_sz2n = {
|
|
||||||
B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16,
|
|
||||||
}
|
|
||||||
local map_sz2prefix = {
|
|
||||||
B = "byte", W = "word", D = "dword",
|
|
||||||
Q = "qword",
|
|
||||||
M = "qword", X = "xword",
|
|
||||||
F = "dword", G = "qword", -- No need for sizes/register names for these two.
|
|
||||||
}
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Output a nicely formatted line with an opcode and operands.
|
|
||||||
local function putop(ctx, text, operands)
|
|
||||||
local code, pos, hex = ctx.code, ctx.pos, ""
|
|
||||||
local hmax = ctx.hexdump
|
|
||||||
if hmax > 0 then
|
|
||||||
for i=ctx.start,pos-1 do
|
|
||||||
hex = hex..format("%02X", byte(code, i, i))
|
|
||||||
end
|
|
||||||
if #hex > hmax then hex = sub(hex, 1, hmax)..". "
|
|
||||||
else hex = hex..rep(" ", hmax-#hex+2) end
|
|
||||||
end
|
|
||||||
if operands then text = text.." "..operands end
|
|
||||||
if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
|
|
||||||
if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
|
|
||||||
if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
|
|
||||||
if ctx.rex then
|
|
||||||
local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
|
|
||||||
(ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")
|
|
||||||
if t ~= "" then text = "rex."..t.." "..text end
|
|
||||||
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
|
|
||||||
ctx.rex = false
|
|
||||||
end
|
|
||||||
if ctx.seg then
|
|
||||||
local text2, n = gsub(text, "%[", "["..ctx.seg..":")
|
|
||||||
if n == 0 then text = ctx.seg.." "..text else text = text2 end
|
|
||||||
ctx.seg = false
|
|
||||||
end
|
|
||||||
if ctx.lock then text = "lock "..text; ctx.lock = false end
|
|
||||||
local imm = ctx.imm
|
|
||||||
if imm then
|
|
||||||
local sym = ctx.symtab[imm]
|
|
||||||
if sym then text = text.."\t->"..sym end
|
|
||||||
end
|
|
||||||
ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
|
|
||||||
ctx.mrm = false
|
|
||||||
ctx.start = pos
|
|
||||||
ctx.imm = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Clear all prefix flags.
|
|
||||||
local function clearprefixes(ctx)
|
|
||||||
ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
|
|
||||||
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
|
|
||||||
ctx.rex = false; ctx.a32 = false
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Fallback for incomplete opcodes at the end.
|
|
||||||
local function incomplete(ctx)
|
|
||||||
ctx.pos = ctx.stop+1
|
|
||||||
clearprefixes(ctx)
|
|
||||||
return putop(ctx, "(incomplete)")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Fallback for unknown opcodes.
|
|
||||||
local function unknown(ctx)
|
|
||||||
clearprefixes(ctx)
|
|
||||||
return putop(ctx, "(unknown)")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return an immediate of the specified size.
|
|
||||||
local function getimm(ctx, pos, n)
|
|
||||||
if pos+n-1 > ctx.stop then return incomplete(ctx) end
|
|
||||||
local code = ctx.code
|
|
||||||
if n == 1 then
|
|
||||||
local b1 = byte(code, pos, pos)
|
|
||||||
return b1
|
|
||||||
elseif n == 2 then
|
|
||||||
local b1, b2 = byte(code, pos, pos+1)
|
|
||||||
return b1+b2*256
|
|
||||||
else
|
|
||||||
local b1, b2, b3, b4 = byte(code, pos, pos+3)
|
|
||||||
local imm = b1+b2*256+b3*65536+b4*16777216
|
|
||||||
ctx.imm = imm
|
|
||||||
return imm
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Process pattern string and generate the operands.
|
|
||||||
local function putpat(ctx, name, pat)
|
|
||||||
local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
|
|
||||||
local code, pos, stop = ctx.code, ctx.pos, ctx.stop
|
|
||||||
|
|
||||||
-- Chars used: 1DFGIMPQRSTUVWXacdfgijmoprstuwxyz
|
|
||||||
for p in gmatch(pat, ".") do
|
|
||||||
local x = nil
|
|
||||||
if p == "V" or p == "U" then
|
|
||||||
if ctx.rexw then sz = "Q"; ctx.rexw = false
|
|
||||||
elseif ctx.o16 then sz = "W"; ctx.o16 = false
|
|
||||||
elseif p == "U" and ctx.x64 then sz = "Q"
|
|
||||||
else sz = "D" end
|
|
||||||
regs = map_regs[sz]
|
|
||||||
elseif p == "T" then
|
|
||||||
if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
|
|
||||||
regs = map_regs[sz]
|
|
||||||
elseif p == "B" then
|
|
||||||
sz = "B"
|
|
||||||
regs = ctx.rex and map_regs.B64 or map_regs.B
|
|
||||||
elseif match(p, "[WDQMXFG]") then
|
|
||||||
sz = p
|
|
||||||
regs = map_regs[sz]
|
|
||||||
elseif p == "P" then
|
|
||||||
sz = ctx.o16 and "X" or "M"; ctx.o16 = false
|
|
||||||
regs = map_regs[sz]
|
|
||||||
elseif p == "S" then
|
|
||||||
name = name..lower(sz)
|
|
||||||
elseif p == "s" then
|
|
||||||
local imm = getimm(ctx, pos, 1); if not imm then return end
|
|
||||||
x = imm <= 127 and format("+0x%02x", imm)
|
|
||||||
or format("-0x%02x", 256-imm)
|
|
||||||
pos = pos+1
|
|
||||||
elseif p == "u" then
|
|
||||||
local imm = getimm(ctx, pos, 1); if not imm then return end
|
|
||||||
x = format("0x%02x", imm)
|
|
||||||
pos = pos+1
|
|
||||||
elseif p == "w" then
|
|
||||||
local imm = getimm(ctx, pos, 2); if not imm then return end
|
|
||||||
x = format("0x%x", imm)
|
|
||||||
pos = pos+2
|
|
||||||
elseif p == "o" then -- [offset]
|
|
||||||
if ctx.x64 then
|
|
||||||
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
|
|
||||||
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
|
|
||||||
x = format("[0x%08x%08x]", imm2, imm1)
|
|
||||||
pos = pos+8
|
|
||||||
else
|
|
||||||
local imm = getimm(ctx, pos, 4); if not imm then return end
|
|
||||||
x = format("[0x%08x]", imm)
|
|
||||||
pos = pos+4
|
|
||||||
end
|
|
||||||
elseif p == "i" or p == "I" then
|
|
||||||
local n = map_sz2n[sz]
|
|
||||||
if n == 8 and ctx.x64 and p == "I" then
|
|
||||||
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
|
|
||||||
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
|
|
||||||
x = format("0x%08x%08x", imm2, imm1)
|
|
||||||
else
|
|
||||||
if n == 8 then n = 4 end
|
|
||||||
local imm = getimm(ctx, pos, n); if not imm then return end
|
|
||||||
if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
|
|
||||||
imm = (0xffffffff+1)-imm
|
|
||||||
x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
|
|
||||||
else
|
|
||||||
x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
pos = pos+n
|
|
||||||
elseif p == "j" then
|
|
||||||
local n = map_sz2n[sz]
|
|
||||||
if n == 8 then n = 4 end
|
|
||||||
local imm = getimm(ctx, pos, n); if not imm then return end
|
|
||||||
if sz == "B" and imm > 127 then imm = imm-256
|
|
||||||
elseif imm > 2147483647 then imm = imm-4294967296 end
|
|
||||||
pos = pos+n
|
|
||||||
imm = imm + pos + ctx.addr
|
|
||||||
if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
|
|
||||||
ctx.imm = imm
|
|
||||||
if sz == "W" then
|
|
||||||
x = format("word 0x%04x", imm%65536)
|
|
||||||
elseif ctx.x64 then
|
|
||||||
local lo = imm % 0x1000000
|
|
||||||
x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
|
|
||||||
else
|
|
||||||
x = format("0x%08x", imm)
|
|
||||||
end
|
|
||||||
elseif p == "R" then
|
|
||||||
local r = byte(code, pos-1, pos-1)%8
|
|
||||||
if ctx.rexb then r = r + 8; ctx.rexb = false end
|
|
||||||
x = regs[r+1]
|
|
||||||
elseif p == "a" then x = regs[1]
|
|
||||||
elseif p == "c" then x = "cl"
|
|
||||||
elseif p == "d" then x = "dx"
|
|
||||||
elseif p == "1" then x = "1"
|
|
||||||
else
|
|
||||||
if not mode then
|
|
||||||
mode = ctx.mrm
|
|
||||||
if not mode then
|
|
||||||
if pos > stop then return incomplete(ctx) end
|
|
||||||
mode = byte(code, pos, pos)
|
|
||||||
pos = pos+1
|
|
||||||
end
|
|
||||||
rm = mode%8; mode = (mode-rm)/8
|
|
||||||
sp = mode%8; mode = (mode-sp)/8
|
|
||||||
sdisp = ""
|
|
||||||
if mode < 3 then
|
|
||||||
if rm == 4 then
|
|
||||||
if pos > stop then return incomplete(ctx) end
|
|
||||||
sc = byte(code, pos, pos)
|
|
||||||
pos = pos+1
|
|
||||||
rm = sc%8; sc = (sc-rm)/8
|
|
||||||
rx = sc%8; sc = (sc-rx)/8
|
|
||||||
if ctx.rexx then rx = rx + 8; ctx.rexx = false end
|
|
||||||
if rx == 4 then rx = nil end
|
|
||||||
end
|
|
||||||
if mode > 0 or rm == 5 then
|
|
||||||
local dsz = mode
|
|
||||||
if dsz ~= 1 then dsz = 4 end
|
|
||||||
local disp = getimm(ctx, pos, dsz); if not disp then return end
|
|
||||||
if mode == 0 then rm = nil end
|
|
||||||
if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
|
|
||||||
if dsz == 1 and disp > 127 then
|
|
||||||
sdisp = format("-0x%x", 256-disp)
|
|
||||||
elseif disp >= 0 and disp <= 0x7fffffff then
|
|
||||||
sdisp = format("+0x%x", disp)
|
|
||||||
else
|
|
||||||
sdisp = format("-0x%x", (0xffffffff+1)-disp)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
sdisp = format(ctx.x64 and not ctx.a32 and
|
|
||||||
not (disp >= 0 and disp <= 0x7fffffff)
|
|
||||||
and "0xffffffff%08x" or "0x%08x", disp)
|
|
||||||
end
|
|
||||||
pos = pos+dsz
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
|
|
||||||
if ctx.rexr then sp = sp + 8; ctx.rexr = false end
|
|
||||||
end
|
|
||||||
if p == "m" then
|
|
||||||
if mode == 3 then x = regs[rm+1]
|
|
||||||
else
|
|
||||||
local aregs = ctx.a32 and map_regs.D or ctx.aregs
|
|
||||||
local srm, srx = "", ""
|
|
||||||
if rm then srm = aregs[rm+1]
|
|
||||||
elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
|
|
||||||
ctx.a32 = false
|
|
||||||
if rx then
|
|
||||||
if rm then srm = srm.."+" end
|
|
||||||
srx = aregs[rx+1]
|
|
||||||
if sc > 0 then srx = srx.."*"..(2^sc) end
|
|
||||||
end
|
|
||||||
x = format("[%s%s%s]", srm, srx, sdisp)
|
|
||||||
end
|
|
||||||
if mode < 3 and
|
|
||||||
(not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
|
|
||||||
x = map_sz2prefix[sz].." "..x
|
|
||||||
end
|
|
||||||
elseif p == "r" then x = regs[sp+1]
|
|
||||||
elseif p == "g" then x = map_segregs[sp+1]
|
|
||||||
elseif p == "p" then -- Suppress prefix.
|
|
||||||
elseif p == "f" then x = "st"..rm
|
|
||||||
elseif p == "x" then
|
|
||||||
if sp == 0 and ctx.lock and not ctx.x64 then
|
|
||||||
x = "CR8"; ctx.lock = false
|
|
||||||
else
|
|
||||||
x = "CR"..sp
|
|
||||||
end
|
|
||||||
elseif p == "y" then x = "DR"..sp
|
|
||||||
elseif p == "z" then x = "TR"..sp
|
|
||||||
elseif p == "t" then
|
|
||||||
else
|
|
||||||
error("bad pattern `"..pat.."'")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if x then operands = operands and operands..", "..x or x end
|
|
||||||
end
|
|
||||||
ctx.pos = pos
|
|
||||||
return putop(ctx, name, operands)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Forward declaration.
|
|
||||||
local map_act
|
|
||||||
|
|
||||||
-- Fetch and cache MRM byte.
|
|
||||||
local function getmrm(ctx)
|
|
||||||
local mrm = ctx.mrm
|
|
||||||
if not mrm then
|
|
||||||
local pos = ctx.pos
|
|
||||||
if pos > ctx.stop then return nil end
|
|
||||||
mrm = byte(ctx.code, pos, pos)
|
|
||||||
ctx.pos = pos+1
|
|
||||||
ctx.mrm = mrm
|
|
||||||
end
|
|
||||||
return mrm
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Dispatch to handler depending on pattern.
|
|
||||||
local function dispatch(ctx, opat, patgrp)
|
|
||||||
if not opat then return unknown(ctx) end
|
|
||||||
if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
|
|
||||||
local p
|
|
||||||
if ctx.rep then
|
|
||||||
p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
|
|
||||||
ctx.rep = false
|
|
||||||
elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
|
|
||||||
else p = "^[^%|]*" end
|
|
||||||
opat = match(opat, p)
|
|
||||||
if not opat then return unknown(ctx) end
|
|
||||||
-- ctx.rep = false; ctx.o16 = false
|
|
||||||
--XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
|
|
||||||
--XXX remove in branches?
|
|
||||||
end
|
|
||||||
if match(opat, "%$") then -- reg$mem variants.
|
|
||||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
|
||||||
opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
|
|
||||||
if opat == "" then return unknown(ctx) end
|
|
||||||
end
|
|
||||||
if opat == "" then return unknown(ctx) end
|
|
||||||
local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
|
|
||||||
if pat == "" and patgrp then pat = patgrp end
|
|
||||||
return map_act[sub(pat, 1, 1)](ctx, name, pat)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Get a pattern from an opcode map and dispatch to handler.
|
|
||||||
local function dispatchmap(ctx, opcmap)
|
|
||||||
local pos = ctx.pos
|
|
||||||
local opat = opcmap[byte(ctx.code, pos, pos)]
|
|
||||||
pos = pos + 1
|
|
||||||
ctx.pos = pos
|
|
||||||
return dispatch(ctx, opat)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Map for action codes. The key is the first char after the name.
|
|
||||||
map_act = {
|
|
||||||
-- Simple opcodes without operands.
|
|
||||||
[""] = function(ctx, name, pat)
|
|
||||||
return putop(ctx, name)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Operand size chars fall right through.
|
|
||||||
B = putpat, W = putpat, D = putpat, Q = putpat,
|
|
||||||
V = putpat, U = putpat, T = putpat,
|
|
||||||
M = putpat, X = putpat, P = putpat,
|
|
||||||
F = putpat, G = putpat,
|
|
||||||
|
|
||||||
-- Collect prefixes.
|
|
||||||
[":"] = function(ctx, name, pat)
|
|
||||||
ctx[pat == ":" and name or sub(pat, 2)] = name
|
|
||||||
if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Chain to special handler specified by name.
|
|
||||||
["*"] = function(ctx, name, pat)
|
|
||||||
return map_act[name](ctx, name, sub(pat, 2))
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Use named subtable for opcode group.
|
|
||||||
["!"] = function(ctx, name, pat)
|
|
||||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
|
||||||
return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- o16,o32[,o64] variants.
|
|
||||||
sz = function(ctx, name, pat)
|
|
||||||
if ctx.o16 then ctx.o16 = false
|
|
||||||
else
|
|
||||||
pat = match(pat, ",(.*)")
|
|
||||||
if ctx.rexw then
|
|
||||||
local p = match(pat, ",(.*)")
|
|
||||||
if p then pat = p; ctx.rexw = false end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
pat = match(pat, "^[^,]*")
|
|
||||||
return dispatch(ctx, pat)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Two-byte opcode dispatch.
|
|
||||||
opc2 = function(ctx, name, pat)
|
|
||||||
return dispatchmap(ctx, map_opc2)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Three-byte opcode dispatch.
|
|
||||||
opc3 = function(ctx, name, pat)
|
|
||||||
return dispatchmap(ctx, map_opc3[pat])
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- VMX/SVM dispatch.
|
|
||||||
vm = function(ctx, name, pat)
|
|
||||||
return dispatch(ctx, map_opcvm[ctx.mrm])
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Floating point opcode dispatch.
|
|
||||||
fp = function(ctx, name, pat)
|
|
||||||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
|
|
||||||
local rm = mrm%8
|
|
||||||
local idx = pat*8 + ((mrm-rm)/8)%8
|
|
||||||
if mrm >= 192 then idx = idx + 64 end
|
|
||||||
local opat = map_opcfp[idx]
|
|
||||||
if type(opat) == "table" then opat = opat[rm+1] end
|
|
||||||
return dispatch(ctx, opat)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- REX prefix.
|
|
||||||
rex = function(ctx, name, pat)
|
|
||||||
if ctx.rex then return unknown(ctx) end -- Only 1 REX prefix allowed.
|
|
||||||
for p in gmatch(pat, ".") do ctx["rex"..p] = true end
|
|
||||||
ctx.rex = true
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Special case for nop with REX prefix.
|
|
||||||
nop = function(ctx, name, pat)
|
|
||||||
return dispatch(ctx, ctx.rex and pat or "nop")
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Disassemble a block of code.
|
|
||||||
local function disass_block(ctx, ofs, len)
|
|
||||||
if not ofs then ofs = 0 end
|
|
||||||
local stop = len and ofs+len or #ctx.code
|
|
||||||
ofs = ofs + 1
|
|
||||||
ctx.start = ofs
|
|
||||||
ctx.pos = ofs
|
|
||||||
ctx.stop = stop
|
|
||||||
ctx.imm = nil
|
|
||||||
ctx.mrm = false
|
|
||||||
clearprefixes(ctx)
|
|
||||||
while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
|
|
||||||
if ctx.pos ~= ctx.start then incomplete(ctx) end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
|
||||||
local function create_(code, addr, out)
|
|
||||||
local ctx = {}
|
|
||||||
ctx.code = code
|
|
||||||
ctx.addr = (addr or 0) - 1
|
|
||||||
ctx.out = out or io.write
|
|
||||||
ctx.symtab = {}
|
|
||||||
ctx.disass = disass_block
|
|
||||||
ctx.hexdump = 16
|
|
||||||
ctx.x64 = false
|
|
||||||
ctx.map1 = map_opc1_32
|
|
||||||
ctx.aregs = map_regs.D
|
|
||||||
return ctx
|
|
||||||
end
|
|
||||||
|
|
||||||
local function create64_(code, addr, out)
|
|
||||||
local ctx = create_(code, addr, out)
|
|
||||||
ctx.x64 = true
|
|
||||||
ctx.map1 = map_opc1_64
|
|
||||||
ctx.aregs = map_regs.Q
|
|
||||||
return ctx
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Simple API: disassemble code (a string) at address and output via out.
|
|
||||||
local function disass_(code, addr, out)
|
|
||||||
create_(code, addr, out):disass()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function disass64_(code, addr, out)
|
|
||||||
create64_(code, addr, out):disass()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return register name for RID.
|
|
||||||
local function regname_(r)
|
|
||||||
if r < 8 then return map_regs.D[r+1] end
|
|
||||||
return map_regs.X[r-7]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function regname64_(r)
|
|
||||||
if r < 16 then return map_regs.Q[r+1] end
|
|
||||||
return map_regs.X[r-15]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Public module functions.
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
create = create_
|
|
||||||
create64 = create64_
|
|
||||||
disass = disass_
|
|
||||||
disass64 = disass64_
|
|
||||||
regname = regname_
|
|
||||||
regname64 = regname64_
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
FILES=lunatic.html lunacon.html
|
|
||||||
|
|
||||||
# NOTE: the 'source-highlight-args' attribute is not present in the default
|
|
||||||
# AsciiDoc distribution. It has to be hacked into its
|
|
||||||
# filters/source/source-highlight-filter.conf like this:
|
|
||||||
#
|
|
||||||
# filter="source-highlight -f xhtml -s {language} (...)"
|
|
||||||
# -->
|
|
||||||
# filter="source-highlight {source-highlight-args} -f xhtml -s {language} (...)"
|
|
||||||
%.html: %.txt Makefile lunatic_sh.style
|
|
||||||
asciidoc -v -a latexmath -a source-highlight-args="--style-file=$(shell pwd)/lunatic_sh.style" $<
|
|
||||||
|
|
||||||
all: $(FILES)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(FILES)
|
|
Binary file not shown.
Before Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.6 KiB |
|
@ -1,14 +0,0 @@
|
||||||
|
|
||||||
Lunatic depends on the following:
|
|
||||||
|
|
||||||
* LuaJIT 2.0.3 or later [http://luajit.org]. It is recommended to get the
|
|
||||||
latest git HEAD of the 2.0 branch. As of time of writing, LuaJIT 2.1 is
|
|
||||||
in alpha, and has not been successfully tested with Lunatic.
|
|
||||||
|
|
||||||
* LPeg 0.12 [http://www.inf.puc-rio.br/~roberto/lpeg].
|
|
||||||
|
|
||||||
For Windows, static libraries and headers have been provided.
|
|
||||||
|
|
||||||
See platform/Windows/lib/instructions.txt for information on building.
|
|
||||||
|
|
||||||
Build EDuke32 with the GNU Make invocation "make LUNATIC=1".
|
|
|
@ -1,73 +0,0 @@
|
||||||
diff --git a/lpcap.h b/lpcap.h
|
|
||||||
index c0a0e38..1bfc109 100644
|
|
||||||
--- a/lpcap.h
|
|
||||||
+++ b/lpcap.h
|
|
||||||
@@ -18,7 +18,7 @@ typedef enum CapKind {
|
|
||||||
|
|
||||||
typedef struct Capture {
|
|
||||||
const char *s; /* subject position */
|
|
||||||
- short idx; /* extra info about capture (group name, arg index, etc.) */
|
|
||||||
+ int idx; /* extra info about capture (group name, arg index, etc.) */
|
|
||||||
byte kind; /* kind of capture */
|
|
||||||
byte siz; /* size of full capture + 1 (0 = not a full capture) */
|
|
||||||
} Capture;
|
|
||||||
diff --git a/makefile b/makefile
|
|
||||||
index 57a18fb..88e8a60 100644
|
|
||||||
--- a/makefile
|
|
||||||
+++ b/makefile
|
|
||||||
@@ -1,5 +1,8 @@
|
|
||||||
LIBNAME = lpeg
|
|
||||||
-LUADIR = ../lua/
|
|
||||||
+# Linux example:
|
|
||||||
+LUADIR = /usr/local/include/luajit-2.0
|
|
||||||
+# Windows example (from MSYS bash):
|
|
||||||
+#LUADIR = /f/g/mod/luajit-2.0/src
|
|
||||||
|
|
||||||
COPT = -O2
|
|
||||||
# COPT = -DLPEG_DEBUG -g
|
|
||||||
@@ -19,10 +22,21 @@ CWARNS = -Wall -Wextra -pedantic \
|
|
||||||
-Wmissing-prototypes \
|
|
||||||
-Wnested-externs \
|
|
||||||
-Wstrict-prototypes \
|
|
||||||
+ -Werror-implicit-function-declaration \
|
|
||||||
# -Wunreachable-code \
|
|
||||||
|
|
||||||
+# Example:
|
|
||||||
+# --------
|
|
||||||
+# 1. Build shared lib (for LunaCON standalone)
|
|
||||||
+# $ make linux
|
|
||||||
+# 2. Remove shared-lib *.o
|
|
||||||
+# $ rm *.o
|
|
||||||
+# 3. Build static lib (for EDuke32/Lunatic)
|
|
||||||
+# $ PIC='' make liblpeg.a
|
|
||||||
|
|
||||||
-CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) -fPIC
|
|
||||||
+PIC ?= -fPIC
|
|
||||||
+
|
|
||||||
+CFLAGS = $(CWARNS) $(COPT) -std=c99 -I$(LUADIR) $(PIC)
|
|
||||||
CC = gcc
|
|
||||||
|
|
||||||
FILES = lpvm.o lpcap.o lptree.o lpcode.o lpprint.o
|
|
||||||
@@ -38,13 +52,16 @@ macosx:
|
|
||||||
lpeg.so: $(FILES)
|
|
||||||
env $(CC) $(DLLFLAGS) $(FILES) -o lpeg.so
|
|
||||||
|
|
||||||
+liblpeg.a: $(FILES)
|
|
||||||
+ $(AR) rc $@ $^
|
|
||||||
+
|
|
||||||
$(FILES): makefile
|
|
||||||
|
|
||||||
test: test.lua re.lua lpeg.so
|
|
||||||
./test.lua
|
|
||||||
|
|
||||||
clean:
|
|
||||||
- rm -f $(FILES) lpeg.so
|
|
||||||
+ rm -f $(FILES) lpeg.so liblpeg.a
|
|
||||||
|
|
||||||
|
|
||||||
lpcap.o: lpcap.c lpcap.h lptypes.h
|
|
||||||
@@ -52,4 +69,3 @@ lpcode.o: lpcode.c lptypes.h lpcode.h lptree.h lpvm.h lpcap.h
|
|
||||||
lpprint.o: lpprint.c lptypes.h lpprint.h lptree.h lpvm.h lpcap.h
|
|
||||||
lptree.o: lptree.c lptypes.h lpcap.h lpcode.h lptree.h lpvm.h lpprint.h
|
|
||||||
lpvm.o: lpvm.c lpcap.h lptypes.h lpvm.h lpprint.h lptree.h
|
|
||||||
-
|
|
|
@ -1,366 +0,0 @@
|
||||||
LunaCON User Manual -- The ``Lunatic Translator''
|
|
||||||
=================================================
|
|
||||||
Helixhorned <contact: Duke4.net forums>
|
|
||||||
:max-width: 56em
|
|
||||||
:numbered:
|
|
||||||
:icons:
|
|
||||||
:toc:
|
|
||||||
:conf-files: lunatic.conf
|
|
||||||
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
|
|
||||||
The Lunatic build of EDuke32 completely reimplements the CON scripting language
|
|
||||||
using the framework provided by its Lua interface. It does so by generating at
|
|
||||||
start-up time one Lunatic module from all specified CON files and
|
|
||||||
mutators.footnote:[In CON, the *`include`* directive amounts to merely textual
|
|
||||||
inclusion, as is mutator appending with the `-mx` switch. All CON code is
|
|
||||||
translated in one global context sharing the same namespace.]
|
|
||||||
|
|
||||||
LunaCON implements nearly all of modern EDuke32 CON. One of its main aims is
|
|
||||||
correctness in a broad sense, which encompasses multiple issues.
|
|
||||||
|
|
||||||
* LunaCON more strictly validates the CON source at translation time. For
|
|
||||||
example, using labels of mismatched type, such as a ++define++d number where
|
|
||||||
a `move` is expected, issues an translation error.
|
|
||||||
|
|
||||||
* Lexically and syntactically, LunaCON accepts only a subset of what the C-CON
|
|
||||||
translator would pass through. This includes rejecting clearly erroneous code
|
|
||||||
such as an unfinished directive at the end of a translation unit, but also
|
|
||||||
code that implies ambiguities in the language.
|
|
||||||
|
|
||||||
* On the run-time side, most checking is done by Lunatic. For example, indexing
|
|
||||||
actor gamevars with out-of-bounds values produces an error that gets
|
|
||||||
permanently displayed on the screen. Like with Lua code, the
|
|
||||||
link:lunatic.html#error[error] entry in the log then contains a traceback
|
|
||||||
annotated with line numbers, making it possible for the CON coder to pinpoint
|
|
||||||
its location and context.
|
|
||||||
|
|
||||||
For these reasons, many existing CON mods and TCs are expected to need
|
|
||||||
amendments in order to translate and/or run properly. This is generally a
|
|
||||||
desired thing, since otherwise (most of the time, unintentional) misuse of the
|
|
||||||
CON system may produce behavior that is either erratic, or appears to work on
|
|
||||||
the surface but is hiding potential issues.
|
|
||||||
|
|
||||||
.A warning
|
|
||||||
The code generated by LunaCON is unsuitable for human consumption --
|
|
||||||
specifically, it should not be used as a base for new Lunatic code. Even though
|
|
||||||
that code runs in Lunatic's protected user environment, it uses private
|
|
||||||
interfaces that are subject to change in addition to the officially exposed and
|
|
||||||
documented ones.
|
|
||||||
|
|
||||||
Nevertheless, LunaCON is also available as a stand-alone Lua script. Primarily,
|
|
||||||
this allows it to be used as a checking tool during CON development without the
|
|
||||||
need to start EDuke32. Also, multiple CON codebases can be checked in a batch
|
|
||||||
fashion for non-runtime problems.
|
|
||||||
|
|
||||||
|
|
||||||
Usage
|
|
||||||
-----
|
|
||||||
:LuaJIT: http://luajit.org
|
|
||||||
:LPeg: http://www.inf.puc-rio.br/~roberto/lpeg/
|
|
||||||
|
|
||||||
The stand-alone LunaCON script, `lunacon.lua`, needs {LuaJIT}[LuaJIT] for
|
|
||||||
execution and {LPeg}[LPeg] as additional dependency.footnote:[In order to
|
|
||||||
translate some very large CON files, minor modifications have to be made to
|
|
||||||
LuaJIT and LPeg. Refer to `lunatic/doc/how_to_build_lunatic.txt` in the EDuke32
|
|
||||||
source distribution for details.] The script also requires `con_lang.lua` to be
|
|
||||||
present. It expects one or more names of root CON files together with any
|
|
||||||
number of options, in any order (arguments starting with a dash are always
|
|
||||||
interpreted as the latter).
|
|
||||||
|
|
||||||
.Example usage
|
|
||||||
----------
|
|
||||||
luajit ./lunacon.lua -Wall mymod.con
|
|
||||||
----------
|
|
||||||
|
|
||||||
All arguments after a single `@` argument are taken to name _file lists_.
|
|
||||||
These are files containing lines either
|
|
||||||
|
|
||||||
* a file name of a root CON file which gets processed, or
|
|
||||||
* a completely blank line or a line starting with `#`, both of which are ignored.
|
|
||||||
|
|
||||||
|
|
||||||
Options
|
|
||||||
-------
|
|
||||||
|
|
||||||
Most options documented in the following can also be passed to the Lunatic
|
|
||||||
build of EDuke32. Options on the command line are processed in their given
|
|
||||||
order, from left to right. Many of them can be negated by appeding `no-` after
|
|
||||||
the ``option category letter'', for example `-Wno-not-redefined`. This index
|
|
||||||
only lists the positive forms of each non-compound option and labels whether it
|
|
||||||
is enabled or disabled by default.
|
|
||||||
|
|
||||||
General options
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
++-I__directory__++ (stand-alone only)::
|
|
||||||
Specifies a _default directory_ to search for CON files as a last resort. This
|
|
||||||
can be useful if mods expect part of their included files to reside inside GRP
|
|
||||||
or ZIP containers, which the stand-alone translator cannot examine. This option
|
|
||||||
can only be passed once.
|
|
||||||
|
|
||||||
Warning options
|
|
||||||
~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
These options affect on which occasions warnings, or in some cases, errors
|
|
||||||
instead of warnings, are produced at translation time.
|
|
||||||
|
|
||||||
`-Wall`::
|
|
||||||
Enables all warning and errors described in this subsection.
|
|
||||||
|
|
||||||
`-Wbad-identifier` (default: off)::
|
|
||||||
Warns whenever an identifier does not match `[A-Za-z_][A-Za-z0-9_]*`. In words,
|
|
||||||
a ``good'' identifier is expected to start with a letter or an underscore,
|
|
||||||
followed by zero or more letters, underscores, or digits.
|
|
||||||
|
|
||||||
`-Wchained-loadactor` (default: on)::
|
|
||||||
Warns whenever an `eventloadactor` block appears multiple times for the same
|
|
||||||
tile number. In LunaCON, these are translated to an `EVENT_LOADACTOR` block
|
|
||||||
that checks the tile number of its current actor. This event gets chained to
|
|
||||||
the end of all preceding `EVENT_LOADACTOR` definitions, whereas with C-CON, a
|
|
||||||
new `eventloadactor` block for the same tile number would override the existing
|
|
||||||
one.
|
|
||||||
|
|
||||||
`-Werror-bad-getactorvar` (default: off)::
|
|
||||||
When enabled, produces an error whenever a global or per-player gamevar is
|
|
||||||
attempted to be read using ++getactorvar++. Otherwise, a warning is emitted. In
|
|
||||||
this case, the generated code is either a read of the (global) gamevar, or an
|
|
||||||
access of the per-player gamevar with the given index or the current player
|
|
||||||
index (depending on `-fbad-getactorvar-use-pli`), which is probably not what
|
|
||||||
the coder intended.
|
|
||||||
|
|
||||||
`-Wnot-redefined` (default: on)::
|
|
||||||
Warns whenever a `define` directive was ignored because it attempted to
|
|
||||||
redefine an already existing label to a different number. The label can exist
|
|
||||||
either due to a previous `define`, or because it is a predefined label such as
|
|
||||||
`NO`.
|
|
||||||
|
|
||||||
`-Wnumber-conversion` (default: on)::
|
|
||||||
Warns whenever a literal decimal number is encountered that is out of the range
|
|
||||||
for a 32-bit integer, but inside that of an unsigned 32-bit integer. In this
|
|
||||||
case, 2^32^ is subtracted from the number, producing a negative value without
|
|
||||||
changing the bit representation.
|
|
||||||
|
|
||||||
`-Wnever-used-gamevar` (default: off)::
|
|
||||||
After translation, issues a warning for every CON-side user gamevar definition
|
|
||||||
that was never referenced, that is, neither read nor written in the CON code.
|
|
||||||
|
|
||||||
`-Wnever-read-gamevar` (default: off)::
|
|
||||||
After translation, issues a warning for every CON-side user gamevar definition
|
|
||||||
that was assigned to but never read in the CON code.
|
|
||||||
|
|
||||||
`-Wsystem-gamevar` (default: on)::
|
|
||||||
Warns whenever the initial value of a system gamevar was overridden (by issuing
|
|
||||||
`gamevar` at file scope), but the provided gamevar flags did not match those of
|
|
||||||
the kept predefined ones.
|
|
||||||
|
|
||||||
Code generation options
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
These options change the way certain CON code constructs are translated to Lua,
|
|
||||||
set the output behavior of the stand-alone translator, or toggle various error
|
|
||||||
conditions.
|
|
||||||
|
|
||||||
`-fno` (stand-alone only)::
|
|
||||||
Disable printing out the generated code and validating its syntax.
|
|
||||||
|
|
||||||
`-fno=onlycheck` (stand-alone only)::
|
|
||||||
Disable printing out the generated code, but validate whether it is
|
|
||||||
syntactically legal Lua code. A failure of this check represents a bug with
|
|
||||||
LunaCON itself.
|
|
||||||
|
|
||||||
`-fnames` (stand-alone only)::
|
|
||||||
Instead of generating Lua code, output all ++define++d names that were ever
|
|
||||||
passed as tile number to `actor`, `useractor` or `eventloadactor` in a form
|
|
||||||
suitable for the `names.h` file read by Mapster32.
|
|
||||||
|
|
||||||
`-ferror-nostate` (default: on)::
|
|
||||||
If enabled, an attempt to call a `state` that was not previously defined
|
|
||||||
results in an error. Otherwise, a warning is issued and no code is generated
|
|
||||||
for the `state` invocation.
|
|
||||||
|
|
||||||
`-ferror-nonlocal-userdef` (default: on)::
|
|
||||||
If enabled, an attept to issue `getuserdef` or `setuserdef` when the current
|
|
||||||
player doesn't equal the local player generates an error. Otherwise, the
|
|
||||||
userdef structure can be accessed irrespective of the current player.
|
|
||||||
|
|
||||||
`-ferror-negative-tag-write` (default: off)::
|
|
||||||
If enabled, an attempt to assign a negative value to the `lotag` or `hitag`
|
|
||||||
member of the sector, wall or (t)sprite structures produces an error.
|
|
||||||
Normally, it is legal to assign negative values to these members, but since
|
|
||||||
they are unsigned 16-bit integers on the C side, such values will be converted
|
|
||||||
to positive ones and may entail undesired behavior.
|
|
||||||
+
|
|
||||||
NOTE: From CON as well as Lunatic, `hitag` and `lotag` are seen as *signed*
|
|
||||||
16-bit integers.
|
|
||||||
|
|
||||||
`-fbad-getactorvar-use-pli` (default: on)::
|
|
||||||
If enabled and `-Werror-bad-getactorvar` is off, a `getactorvar` of a
|
|
||||||
per-player variable will result the gamevar being indexed with the current
|
|
||||||
player instead of the provided index. This is the (probably unintended)
|
|
||||||
behavior of C-CON.
|
|
||||||
|
|
||||||
`-fplayervar` (default: on)::
|
|
||||||
If enabled, per-player `gamevar` definitions really generate `con.playervar`
|
|
||||||
initializations in the translated Lua code. Otherwise, per-player gamevars are
|
|
||||||
treated as global gamevars, which can be useful for code that attempts to
|
|
||||||
access them in contexts with no current player, yielding errors in Lunatic.
|
|
||||||
|
|
||||||
`-ftrapv` (default: off)::
|
|
||||||
Enable _trapping_ behavior for arithmetic operations whose result overflows the
|
|
||||||
range of a signed 32-bit integer, i.e. generate an error on overflow. By
|
|
||||||
default, overflow results in undefined behavior. Currently, only multiplication
|
|
||||||
is handled.
|
|
||||||
|
|
||||||
`-fwrapv` (default: off)::
|
|
||||||
Enable _wrapping_ behavior for arithmetic operations whose result overflows the
|
|
||||||
range of a signed 32-bit integer, i.e. after each operation, only the 32 lower
|
|
||||||
bits of the result are kept. Currently, only multiplication is handled. Only
|
|
||||||
one of `-ftrapv` or `-fwrapv` may be enabled at the same time.
|
|
||||||
|
|
||||||
|
|
||||||
Differences from C-CON
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Despite the aim to provide as much compatibility to CON as possible where
|
|
||||||
reasonable, a couple of its ``features'' -- some of which are coincidental --
|
|
||||||
cannot be implemented without unnaturally bending the implementation into
|
|
||||||
shape. On the other hand, LunaCON sports some features that C-CON lacks, and
|
|
||||||
does not exhibit some of its strange quirks or outright bugs.
|
|
||||||
|
|
||||||
|
|
||||||
Syntactic and lexical changes
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
The two command classes
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
LunaCON makes a clear distinction between ``outer'' commands that have an
|
|
||||||
effect when translating a CON file (_directives_ such as `gamevar`,
|
|
||||||
`definesound` or `spritenvg`) and ``inner'' commands meant to be effective at
|
|
||||||
execution time of `actor`/`useractor`, `state`, `event` and `eventloadactor`
|
|
||||||
blocks. Thus, issuing directives inside of these will make LunaCON reject the
|
|
||||||
input file due to a syntax error, as will an attempt to use run-time commands
|
|
||||||
such as `sizeat` at file scope.
|
|
||||||
|
|
||||||
This strict behavior is one hand a consequence of how the LunaCON parser is
|
|
||||||
structured, but on the other hand it may expose code for which the author
|
|
||||||
misunderstood its meaning. An example for the first case would be a `gamevar`
|
|
||||||
inside a block, which one mistakenly could take to have local scope. Gamevars
|
|
||||||
in CON always have both global scope and lifetime though, so such a mental
|
|
||||||
model on the coder's part may lead to unexpected bugs. In the second case,
|
|
||||||
run-time commands at file scope are really translated to bytecode by C-CON, but
|
|
||||||
as they reside outside of any block, they are never reached -- in other words,
|
|
||||||
they are dead code.
|
|
||||||
|
|
||||||
Currently, the only exception to this rule is that a `definequote` is allowed
|
|
||||||
inside delimited blocks, which however does not change its semantics in any
|
|
||||||
way: it still only defines the initial contents of a quote, and does not
|
|
||||||
magically act like `redefinequote`.
|
|
||||||
|
|
||||||
Ambiguous lexical elements
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
LunaCON is fairly relaxed as to which character sequences constitute valid
|
|
||||||
identifier names used for e.g. ++define++d labels or variables. It does so out
|
|
||||||
of the necessity of supporting CON code found ``in the wild''. An identifier
|
|
||||||
|
|
||||||
* must not be a token denoting a number,
|
|
||||||
* must start with an _allowed first character_,
|
|
||||||
* may contain any number of _allowed following characters_,
|
|
||||||
|
|
||||||
where
|
|
||||||
|
|
||||||
* allowed first characters are: letters, digits, and those in ```_*?`''
|
|
||||||
* allowed following characters are the same as allowed first characters, plus
|
|
||||||
``+++++'', ``++-++'' and ``++.++''.
|
|
||||||
|
|
||||||
// ^ `+` and `-`
|
|
||||||
|
|
||||||
Numbers can be written in either decimal or hexadecimal form, optionally
|
|
||||||
prepended by a ``++-++'' sign. In the decimal case, the modulus of the number
|
|
||||||
can be (lexically) any sequence of decimal digits, though there are
|
|
||||||
restrictions on the permitted values.footnote:[Specifically, it is forbidden to
|
|
||||||
write a number whose value falls outside the range [--2^31^-1 .. 2^32^--1\].]
|
|
||||||
Hexadecimal number literals must be prefixed with `0x` or `0X`, and may
|
|
||||||
optionally be suffixed with an `h`.
|
|
||||||
|
|
||||||
The following constructions are not allowed, as they would create ambiguities
|
|
||||||
with the definitions above:
|
|
||||||
|
|
||||||
* A sequence of digits followed by letters to mean the the digits interpreted
|
|
||||||
as a number, ignoring the trailing letters, e.g. `1267AT`.
|
|
||||||
* A hexadecimal constant denoted using only a trailing `h`, for example
|
|
||||||
`00000000h`.
|
|
||||||
|
|
||||||
Miscellaneous
|
|
||||||
^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
* Read array expressions as well as `switch` statements can be arbitrarily
|
|
||||||
nested.
|
|
||||||
|
|
||||||
// XXX: 'Read' is confusing. Need better wording.
|
|
||||||
|
|
||||||
|
|
||||||
Run-time changes
|
|
||||||
~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Behavior on error
|
|
||||||
^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
As LunaCON is implemented by translating all given CON code to a Lunatic
|
|
||||||
module, it is the Lunatic runtime that checks for proper use of its services at
|
|
||||||
execution time and takes care of error handling and reporting. In Lua, an error
|
|
||||||
link:lunatic.html#nlcf[transfers control] to the end of the innermost
|
|
||||||
``protected'' call of a Lua chunk.
|
|
||||||
|
|
||||||
This is in contrast to C-CON, which for some errors would print a message to
|
|
||||||
the log, but otherwise would continue execution as if nothing had happened. In
|
|
||||||
LunaCON, the code following an error is *not* executed. This way, the author of
|
|
||||||
the CON code is notified of the presence of the bug, and by fixing it
|
|
||||||
eventually obtains cleaner code.
|
|
||||||
|
|
||||||
Quote behavior
|
|
||||||
^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
* The `redefinequote` command can be issued even if the given quote number has
|
|
||||||
not been previously allocated using the `definequote` directive. This makes
|
|
||||||
it possible to use a range of quote numbers on the fly without the need to
|
|
||||||
declare their future use.
|
|
||||||
|
|
||||||
Miscellaneous
|
|
||||||
^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
:r5097: http://svn.eduke32.com/listing.php?repname=eduke32&rev=5097
|
|
||||||
|
|
||||||
* Issuing `break` inside a part of an event chain (defined using multiple
|
|
||||||
`onevent` blocks for one event kind) does not abort the whole chain. (Since
|
|
||||||
EDuke32 {r5097}[r5097], this is the behavior of C-CON as well.)
|
|
||||||
|
|
||||||
|
|
||||||
Unavailable functionality
|
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
C-CON's `jump` and `getcurraddress` are not available and will not be
|
|
||||||
implemented.
|
|
||||||
|
|
||||||
The following commands are not yet implemented. Those highlighted in bold give
|
|
||||||
errors, while the others merely warn either at translation or execution
|
|
||||||
time.
|
|
||||||
|
|
||||||
Directives
|
|
||||||
^^^^^^^^^^
|
|
||||||
*`includedefault`*, `definecheat`, `setcfgname`, `setgamename`
|
|
||||||
|
|
||||||
Run-time commands
|
|
||||||
^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
*`activatecheat`*, *`clearmapstate`*, *`startcutscene`*, *`ifcutscene`*, `save`,
|
|
||||||
`savenn`, *`lineintersect`*, *`rayintersect`*, *`sectorofwall`*.
|
|
||||||
|
|
||||||
Additionally, various multiplayer-related commands either unconditionally
|
|
||||||
return results as if no multiplayer game is in progress, or are non-functional
|
|
||||||
for more than the first player. Also, some MP-related variables are merely
|
|
||||||
predefined constants.
|
|
|
@ -1,3 +0,0 @@
|
||||||
[replacements]
|
|
||||||
# Make -- an en dash for good looking minus signs.
|
|
||||||
--=–
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,10 +0,0 @@
|
||||||
// Custom style file for GNU Source-highlight, derived from its default.style
|
|
||||||
|
|
||||||
bgcolor "white"; // the background color for documents
|
|
||||||
context gray; // the color for context lines (when specified with line ranges)
|
|
||||||
|
|
||||||
keyword darkblue b; // for language keywords
|
|
||||||
string "#116611" f; // for strings and chars
|
|
||||||
specialchar pink f; // for special chars, e.g., \n, \t, \\
|
|
||||||
comment "#444444" i, noref; // for comments
|
|
||||||
cbracket red; // for block brackets (e.g. {, })
|
|
|
@ -1,699 +0,0 @@
|
||||||
----------------------------------------------------------------------------
|
|
||||||
-- LuaJIT compiler dump module.
|
|
||||||
--
|
|
||||||
-- Copyright (C) 2005-2015 Mike Pall. All rights reserved.
|
|
||||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
|
||||||
----------------------------------------------------------------------------
|
|
||||||
--
|
|
||||||
-- This module can be used to debug the JIT compiler itself. It dumps the
|
|
||||||
-- code representations and structures used in various compiler stages.
|
|
||||||
--
|
|
||||||
-- Example usage:
|
|
||||||
--
|
|
||||||
-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
|
|
||||||
-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
|
|
||||||
-- luajit -jdump=is myapp.lua | less -R
|
|
||||||
-- luajit -jdump=-b myapp.lua
|
|
||||||
-- luajit -jdump=+aH,myapp.html myapp.lua
|
|
||||||
-- luajit -jdump=ixT,myapp.dump myapp.lua
|
|
||||||
--
|
|
||||||
-- The first argument specifies the dump mode. The second argument gives
|
|
||||||
-- the output file name. Default output is to stdout, unless the environment
|
|
||||||
-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
|
|
||||||
-- module is started.
|
|
||||||
--
|
|
||||||
-- Different features can be turned on or off with the dump mode. If the
|
|
||||||
-- mode starts with a '+', the following features are added to the default
|
|
||||||
-- set of features; a '-' removes them. Otherwise the features are replaced.
|
|
||||||
--
|
|
||||||
-- The following dump features are available (* marks the default):
|
|
||||||
--
|
|
||||||
-- * t Print a line for each started, ended or aborted trace (see also -jv).
|
|
||||||
-- * b Dump the traced bytecode.
|
|
||||||
-- * i Dump the IR (intermediate representation).
|
|
||||||
-- r Augment the IR with register/stack slots.
|
|
||||||
-- s Dump the snapshot map.
|
|
||||||
-- * m Dump the generated machine code.
|
|
||||||
-- x Print each taken trace exit.
|
|
||||||
-- X Print each taken trace exit and the contents of all registers.
|
|
||||||
-- a Print the IR of aborted traces, too.
|
|
||||||
--
|
|
||||||
-- The output format can be set with the following characters:
|
|
||||||
--
|
|
||||||
-- T Plain text output.
|
|
||||||
-- A ANSI-colored text output
|
|
||||||
-- H Colorized HTML + CSS output.
|
|
||||||
--
|
|
||||||
-- The default output format is plain text. It's set to ANSI-colored text
|
|
||||||
-- if the COLORTERM variable is set. Note: this is independent of any output
|
|
||||||
-- redirection, which is actually considered a feature.
|
|
||||||
--
|
|
||||||
-- You probably want to use less -R to enjoy viewing ANSI-colored text from
|
|
||||||
-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
|
|
||||||
--
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Cache some library functions and objects.
|
|
||||||
local jit = require("jit")
|
|
||||||
assert(jit.version_num == 20003, "LuaJIT core/library version mismatch")
|
|
||||||
local jutil = require("jit.util")
|
|
||||||
local vmdef = require("jit.vmdef")
|
|
||||||
local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
|
|
||||||
local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
|
|
||||||
local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
|
|
||||||
local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
|
|
||||||
local bit = require("bit")
|
|
||||||
local band, shl, shr = bit.band, bit.lshift, bit.rshift
|
|
||||||
local sub, gsub, format = string.sub, string.gsub, string.format
|
|
||||||
local byte, char, rep = string.byte, string.char, string.rep
|
|
||||||
local type, tostring = type, tostring
|
|
||||||
local stdout, stderr = io.stdout, io.stderr
|
|
||||||
|
|
||||||
-- Load other modules on-demand.
|
|
||||||
local bcline, disass
|
|
||||||
|
|
||||||
-- Active flag, output file handle and dump mode.
|
|
||||||
local active, out, dumpmode
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local symtabmt = { __index = false }
|
|
||||||
local symtab = {}
|
|
||||||
local nexitsym = 0
|
|
||||||
|
|
||||||
-- Fill nested symbol table with per-trace exit stub addresses.
|
|
||||||
local function fillsymtab_tr(tr, nexit)
|
|
||||||
local t = {}
|
|
||||||
symtabmt.__index = t
|
|
||||||
if jit.arch == "mips" or jit.arch == "mipsel" then
|
|
||||||
t[traceexitstub(tr, 0)] = "exit"
|
|
||||||
return
|
|
||||||
end
|
|
||||||
for i=0,nexit-1 do
|
|
||||||
local addr = traceexitstub(tr, i)
|
|
||||||
t[addr] = tostring(i)
|
|
||||||
end
|
|
||||||
local addr = traceexitstub(tr, nexit)
|
|
||||||
if addr then t[addr] = "stack_check" end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Fill symbol table with trace exit stub addresses.
|
|
||||||
local function fillsymtab(tr, nexit)
|
|
||||||
local t = symtab
|
|
||||||
if nexitsym == 0 then
|
|
||||||
local ircall = vmdef.ircall
|
|
||||||
for i=0,#ircall do
|
|
||||||
local addr = ircalladdr(i)
|
|
||||||
if addr ~= 0 then t[addr] = ircall[i] end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if nexitsym == 1000000 then -- Per-trace exit stubs.
|
|
||||||
fillsymtab_tr(tr, nexit)
|
|
||||||
elseif nexit > nexitsym then -- Shared exit stubs.
|
|
||||||
for i=nexitsym,nexit-1 do
|
|
||||||
local addr = traceexitstub(i)
|
|
||||||
if addr == nil then -- Fall back to per-trace exit stubs.
|
|
||||||
fillsymtab_tr(tr, nexit)
|
|
||||||
setmetatable(symtab, symtabmt)
|
|
||||||
nexit = 1000000
|
|
||||||
break
|
|
||||||
end
|
|
||||||
t[addr] = tostring(i)
|
|
||||||
end
|
|
||||||
nexitsym = nexit
|
|
||||||
end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dumpwrite(s)
|
|
||||||
out:write(s)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Disassemble machine code.
|
|
||||||
local function dump_mcode(tr)
|
|
||||||
local info = traceinfo(tr)
|
|
||||||
if not info then return end
|
|
||||||
local mcode, addr, loop = tracemc(tr)
|
|
||||||
if not mcode then return end
|
|
||||||
if not disass then disass = require("jit.dis_"..jit.arch) end
|
|
||||||
out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
|
|
||||||
local ctx = disass.create(mcode, addr, dumpwrite)
|
|
||||||
ctx.hexdump = 0
|
|
||||||
ctx.symtab = fillsymtab(tr, info.nexit)
|
|
||||||
if loop ~= 0 then
|
|
||||||
symtab[addr+loop] = "LOOP"
|
|
||||||
ctx:disass(0, loop)
|
|
||||||
out:write("->LOOP:\n")
|
|
||||||
ctx:disass(loop, #mcode-loop)
|
|
||||||
symtab[addr+loop] = nil
|
|
||||||
else
|
|
||||||
ctx:disass(0, #mcode)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local irtype_text = {
|
|
||||||
[0] = "nil",
|
|
||||||
"fal",
|
|
||||||
"tru",
|
|
||||||
"lud",
|
|
||||||
"str",
|
|
||||||
"p32",
|
|
||||||
"thr",
|
|
||||||
"pro",
|
|
||||||
"fun",
|
|
||||||
"p64",
|
|
||||||
"cdt",
|
|
||||||
"tab",
|
|
||||||
"udt",
|
|
||||||
"flt",
|
|
||||||
"num",
|
|
||||||
"i8 ",
|
|
||||||
"u8 ",
|
|
||||||
"i16",
|
|
||||||
"u16",
|
|
||||||
"int",
|
|
||||||
"u32",
|
|
||||||
"i64",
|
|
||||||
"u64",
|
|
||||||
"sfp",
|
|
||||||
}
|
|
||||||
|
|
||||||
local colortype_ansi = {
|
|
||||||
[0] = "%s",
|
|
||||||
"%s",
|
|
||||||
"%s",
|
|
||||||
"\027[36m%s\027[m",
|
|
||||||
"\027[32m%s\027[m",
|
|
||||||
"%s",
|
|
||||||
"\027[1m%s\027[m",
|
|
||||||
"%s",
|
|
||||||
"\027[1m%s\027[m",
|
|
||||||
"%s",
|
|
||||||
"\027[33m%s\027[m",
|
|
||||||
"\027[31m%s\027[m",
|
|
||||||
"\027[36m%s\027[m",
|
|
||||||
"\027[34m%s\027[m",
|
|
||||||
"\027[34m%s\027[m",
|
|
||||||
"\027[35m%s\027[m",
|
|
||||||
"\027[35m%s\027[m",
|
|
||||||
"\027[35m%s\027[m",
|
|
||||||
"\027[35m%s\027[m",
|
|
||||||
"\027[35m%s\027[m",
|
|
||||||
"\027[35m%s\027[m",
|
|
||||||
"\027[35m%s\027[m",
|
|
||||||
"\027[35m%s\027[m",
|
|
||||||
"\027[35m%s\027[m",
|
|
||||||
}
|
|
||||||
|
|
||||||
local function colorize_text(s, t)
|
|
||||||
return s
|
|
||||||
end
|
|
||||||
|
|
||||||
local function colorize_ansi(s, t)
|
|
||||||
return format(colortype_ansi[t], s)
|
|
||||||
end
|
|
||||||
|
|
||||||
local irtype_ansi = setmetatable({},
|
|
||||||
{ __index = function(tab, t)
|
|
||||||
local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
|
|
||||||
|
|
||||||
local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", }
|
|
||||||
|
|
||||||
local function colorize_html(s, t)
|
|
||||||
s = gsub(s, "[<>&]", html_escape)
|
|
||||||
return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
|
|
||||||
end
|
|
||||||
|
|
||||||
local irtype_html = setmetatable({},
|
|
||||||
{ __index = function(tab, t)
|
|
||||||
local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
|
|
||||||
|
|
||||||
local header_html = [[
|
|
||||||
<style type="text/css">
|
|
||||||
background { background: #ffffff; color: #000000; }
|
|
||||||
pre.ljdump {
|
|
||||||
font-size: 10pt;
|
|
||||||
background: #f0f4ff;
|
|
||||||
color: #000000;
|
|
||||||
border: 1px solid #bfcfff;
|
|
||||||
padding: 0.5em;
|
|
||||||
margin-left: 2em;
|
|
||||||
margin-right: 2em;
|
|
||||||
}
|
|
||||||
span.irt_str { color: #00a000; }
|
|
||||||
span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
|
|
||||||
span.irt_tab { color: #c00000; }
|
|
||||||
span.irt_udt, span.irt_lud { color: #00c0c0; }
|
|
||||||
span.irt_num { color: #4040c0; }
|
|
||||||
span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
|
|
||||||
</style>
|
|
||||||
]]
|
|
||||||
|
|
||||||
local colorize, irtype
|
|
||||||
|
|
||||||
-- Lookup tables to convert some literals into names.
|
|
||||||
local litname = {
|
|
||||||
["SLOAD "] = setmetatable({}, { __index = function(t, mode)
|
|
||||||
local s = ""
|
|
||||||
if band(mode, 1) ~= 0 then s = s.."P" end
|
|
||||||
if band(mode, 2) ~= 0 then s = s.."F" end
|
|
||||||
if band(mode, 4) ~= 0 then s = s.."T" end
|
|
||||||
if band(mode, 8) ~= 0 then s = s.."C" end
|
|
||||||
if band(mode, 16) ~= 0 then s = s.."R" end
|
|
||||||
if band(mode, 32) ~= 0 then s = s.."I" end
|
|
||||||
t[mode] = s
|
|
||||||
return s
|
|
||||||
end}),
|
|
||||||
["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
|
|
||||||
["CONV "] = setmetatable({}, { __index = function(t, mode)
|
|
||||||
local s = irtype[band(mode, 31)]
|
|
||||||
s = irtype[band(shr(mode, 5), 31)].."."..s
|
|
||||||
if band(mode, 0x400) ~= 0 then s = s.." trunc"
|
|
||||||
elseif band(mode, 0x800) ~= 0 then s = s.." sext" end
|
|
||||||
local c = shr(mode, 14)
|
|
||||||
if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end
|
|
||||||
t[mode] = s
|
|
||||||
return s
|
|
||||||
end}),
|
|
||||||
["FLOAD "] = vmdef.irfield,
|
|
||||||
["FREF "] = vmdef.irfield,
|
|
||||||
["FPMATH"] = vmdef.irfpm,
|
|
||||||
}
|
|
||||||
|
|
||||||
local function ctlsub(c)
|
|
||||||
if c == "\n" then return "\\n"
|
|
||||||
elseif c == "\r" then return "\\r"
|
|
||||||
elseif c == "\t" then return "\\t"
|
|
||||||
else return format("\\%03d", byte(c))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function fmtfunc(func, pc)
|
|
||||||
local fi = funcinfo(func, pc)
|
|
||||||
if fi.loc then
|
|
||||||
return fi.loc
|
|
||||||
elseif fi.ffid then
|
|
||||||
return vmdef.ffnames[fi.ffid]
|
|
||||||
elseif fi.addr then
|
|
||||||
return format("C:%x", fi.addr)
|
|
||||||
else
|
|
||||||
return "(?)"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function formatk(tr, idx)
|
|
||||||
local k, t, slot = tracek(tr, idx)
|
|
||||||
local tn = type(k)
|
|
||||||
local s
|
|
||||||
if tn == "number" then
|
|
||||||
if k == 2^52+2^51 then
|
|
||||||
s = "bias"
|
|
||||||
else
|
|
||||||
s = format("%+.14g", k)
|
|
||||||
end
|
|
||||||
elseif tn == "string" then
|
|
||||||
s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
|
|
||||||
elseif tn == "function" then
|
|
||||||
s = fmtfunc(k)
|
|
||||||
elseif tn == "table" then
|
|
||||||
s = format("{%p}", k)
|
|
||||||
elseif tn == "userdata" then
|
|
||||||
if t == 12 then
|
|
||||||
s = format("userdata:%p", k)
|
|
||||||
else
|
|
||||||
s = format("[%p]", k)
|
|
||||||
if s == "[0x00000000]" then s = "NULL" end
|
|
||||||
end
|
|
||||||
elseif t == 21 then -- int64_t
|
|
||||||
s = sub(tostring(k), 1, -3)
|
|
||||||
if sub(s, 1, 1) ~= "-" then s = "+"..s end
|
|
||||||
else
|
|
||||||
s = tostring(k) -- For primitives.
|
|
||||||
end
|
|
||||||
s = colorize(format("%-4s", s), t)
|
|
||||||
if slot then
|
|
||||||
s = format("%s @%d", s, slot)
|
|
||||||
end
|
|
||||||
return s
|
|
||||||
end
|
|
||||||
|
|
||||||
local function printsnap(tr, snap)
|
|
||||||
local n = 2
|
|
||||||
for s=0,snap[1]-1 do
|
|
||||||
local sn = snap[n]
|
|
||||||
if shr(sn, 24) == s then
|
|
||||||
n = n + 1
|
|
||||||
local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
|
|
||||||
if ref < 0 then
|
|
||||||
out:write(formatk(tr, ref))
|
|
||||||
elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
|
|
||||||
out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
|
|
||||||
else
|
|
||||||
local m, ot, op1, op2 = traceir(tr, ref)
|
|
||||||
out:write(colorize(format("%04d", ref), band(ot, 31)))
|
|
||||||
end
|
|
||||||
out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
|
|
||||||
else
|
|
||||||
out:write("---- ")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
out:write("]\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Dump snapshots (not interleaved with IR).
|
|
||||||
local function dump_snap(tr)
|
|
||||||
out:write("---- TRACE ", tr, " snapshots\n")
|
|
||||||
for i=0,1000000000 do
|
|
||||||
local snap = tracesnap(tr, i)
|
|
||||||
if not snap then break end
|
|
||||||
out:write(format("#%-3d %04d [ ", i, snap[0]))
|
|
||||||
printsnap(tr, snap)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Return a register name or stack slot for a rid/sp location.
|
|
||||||
local function ridsp_name(ridsp, ins)
|
|
||||||
if not disass then disass = require("jit.dis_"..jit.arch) end
|
|
||||||
local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
|
|
||||||
if rid == 253 or rid == 254 then
|
|
||||||
return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
|
|
||||||
end
|
|
||||||
if ridsp > 255 then return format("[%x]", slot*4) end
|
|
||||||
if rid < 128 then return disass.regname(rid) end
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Dump CALL* function ref and return optional ctype.
|
|
||||||
local function dumpcallfunc(tr, ins)
|
|
||||||
local ctype
|
|
||||||
if ins > 0 then
|
|
||||||
local m, ot, op1, op2 = traceir(tr, ins)
|
|
||||||
if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
|
|
||||||
ins = op1
|
|
||||||
ctype = formatk(tr, op2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if ins < 0 then
|
|
||||||
out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
|
|
||||||
else
|
|
||||||
out:write(format("%04d (", ins))
|
|
||||||
end
|
|
||||||
return ctype
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Recursively gather CALL* args and dump them.
|
|
||||||
local function dumpcallargs(tr, ins)
|
|
||||||
if ins < 0 then
|
|
||||||
out:write(formatk(tr, ins))
|
|
||||||
else
|
|
||||||
local m, ot, op1, op2 = traceir(tr, ins)
|
|
||||||
local oidx = 6*shr(ot, 8)
|
|
||||||
local op = sub(vmdef.irnames, oidx+1, oidx+6)
|
|
||||||
if op == "CARG " then
|
|
||||||
dumpcallargs(tr, op1)
|
|
||||||
if op2 < 0 then
|
|
||||||
out:write(" ", formatk(tr, op2))
|
|
||||||
else
|
|
||||||
out:write(" ", format("%04d", op2))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
out:write(format("%04d", ins))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Dump IR and interleaved snapshots.
|
|
||||||
local function dump_ir(tr, dumpsnap, dumpreg)
|
|
||||||
local info = traceinfo(tr)
|
|
||||||
if not info then return end
|
|
||||||
local nins = info.nins
|
|
||||||
out:write("---- TRACE ", tr, " IR\n")
|
|
||||||
local irnames = vmdef.irnames
|
|
||||||
local snapref = 65536
|
|
||||||
local snap, snapno
|
|
||||||
if dumpsnap then
|
|
||||||
snap = tracesnap(tr, 0)
|
|
||||||
snapref = snap[0]
|
|
||||||
snapno = 0
|
|
||||||
end
|
|
||||||
for ins=1,nins do
|
|
||||||
if ins >= snapref then
|
|
||||||
if dumpreg then
|
|
||||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
|
||||||
else
|
|
||||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
|
||||||
end
|
|
||||||
printsnap(tr, snap)
|
|
||||||
snapno = snapno + 1
|
|
||||||
snap = tracesnap(tr, snapno)
|
|
||||||
snapref = snap and snap[0] or 65536
|
|
||||||
end
|
|
||||||
local m, ot, op1, op2, ridsp = traceir(tr, ins)
|
|
||||||
local oidx, t = 6*shr(ot, 8), band(ot, 31)
|
|
||||||
local op = sub(irnames, oidx+1, oidx+6)
|
|
||||||
if op == "LOOP " then
|
|
||||||
if dumpreg then
|
|
||||||
out:write(format("%04d ------------ LOOP ------------\n", ins))
|
|
||||||
else
|
|
||||||
out:write(format("%04d ------ LOOP ------------\n", ins))
|
|
||||||
end
|
|
||||||
elseif op ~= "NOP " and op ~= "CARG " and
|
|
||||||
(dumpreg or op ~= "RENAME") then
|
|
||||||
local rid = band(ridsp, 255)
|
|
||||||
if dumpreg then
|
|
||||||
out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
|
|
||||||
else
|
|
||||||
out:write(format("%04d ", ins))
|
|
||||||
end
|
|
||||||
out:write(format("%s%s %s %s ",
|
|
||||||
(rid == 254 or rid == 253) and "}" or
|
|
||||||
(band(ot, 128) == 0 and " " or ">"),
|
|
||||||
band(ot, 64) == 0 and " " or "+",
|
|
||||||
irtype[t], op))
|
|
||||||
local m1, m2 = band(m, 3), band(m, 3*4)
|
|
||||||
if sub(op, 1, 4) == "CALL" then
|
|
||||||
local ctype
|
|
||||||
if m2 == 1*4 then -- op2 == IRMlit
|
|
||||||
out:write(format("%-10s (", vmdef.ircall[op2]))
|
|
||||||
else
|
|
||||||
ctype = dumpcallfunc(tr, op2)
|
|
||||||
end
|
|
||||||
if op1 ~= -1 then dumpcallargs(tr, op1) end
|
|
||||||
out:write(")")
|
|
||||||
if ctype then out:write(" ctype ", ctype) end
|
|
||||||
elseif op == "CNEW " and op2 == -1 then
|
|
||||||
out:write(formatk(tr, op1))
|
|
||||||
elseif m1 ~= 3 then -- op1 != IRMnone
|
|
||||||
if op1 < 0 then
|
|
||||||
out:write(formatk(tr, op1))
|
|
||||||
else
|
|
||||||
out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
|
|
||||||
end
|
|
||||||
if m2 ~= 3*4 then -- op2 != IRMnone
|
|
||||||
if m2 == 1*4 then -- op2 == IRMlit
|
|
||||||
local litn = litname[op]
|
|
||||||
if litn and litn[op2] then
|
|
||||||
out:write(" ", litn[op2])
|
|
||||||
elseif op == "UREFO " or op == "UREFC " then
|
|
||||||
out:write(format(" #%-3d", shr(op2, 8)))
|
|
||||||
else
|
|
||||||
out:write(format(" #%-3d", op2))
|
|
||||||
end
|
|
||||||
elseif op2 < 0 then
|
|
||||||
out:write(" ", formatk(tr, op2))
|
|
||||||
else
|
|
||||||
out:write(format(" %04d", op2))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
out:write("\n")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if snap then
|
|
||||||
if dumpreg then
|
|
||||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
|
||||||
else
|
|
||||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
|
||||||
end
|
|
||||||
printsnap(tr, snap)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local recprefix = ""
|
|
||||||
local recdepth = 0
|
|
||||||
|
|
||||||
-- Format trace error message.
|
|
||||||
local function fmterr(err, info)
|
|
||||||
if type(err) == "number" then
|
|
||||||
if type(info) == "function" then info = fmtfunc(info) end
|
|
||||||
err = format(vmdef.traceerr[err], info)
|
|
||||||
end
|
|
||||||
return err
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Dump trace states.
|
|
||||||
local function dump_trace(what, tr, func, pc, otr, oex)
|
|
||||||
if what == "stop" or (what == "abort" and dumpmode.a) then
|
|
||||||
if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
|
|
||||||
elseif dumpmode.s then dump_snap(tr) end
|
|
||||||
if dumpmode.m then dump_mcode(tr) end
|
|
||||||
end
|
|
||||||
if what == "start" then
|
|
||||||
if dumpmode.H then out:write('<pre class="ljdump">\n') end
|
|
||||||
out:write("---- TRACE ", tr, " ", what)
|
|
||||||
if otr then out:write(" ", otr, "/", oex) end
|
|
||||||
out:write(" ", fmtfunc(func, pc), "\n")
|
|
||||||
elseif what == "stop" or what == "abort" then
|
|
||||||
out:write("---- TRACE ", tr, " ", what)
|
|
||||||
if what == "abort" then
|
|
||||||
out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
|
|
||||||
else
|
|
||||||
local info = traceinfo(tr)
|
|
||||||
local link, ltype = info.link, info.linktype
|
|
||||||
if link == tr or link == 0 then
|
|
||||||
out:write(" -> ", ltype, "\n")
|
|
||||||
elseif ltype == "root" then
|
|
||||||
out:write(" -> ", link, "\n")
|
|
||||||
else
|
|
||||||
out:write(" -> ", link, " ", ltype, "\n")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
|
|
||||||
else
|
|
||||||
out:write("---- TRACE ", what, "\n\n")
|
|
||||||
end
|
|
||||||
out:flush()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Dump recorded bytecode.
|
|
||||||
local function dump_record(tr, func, pc, depth, callee)
|
|
||||||
if depth ~= recdepth then
|
|
||||||
recdepth = depth
|
|
||||||
recprefix = rep(" .", depth)
|
|
||||||
end
|
|
||||||
local line
|
|
||||||
if pc >= 0 then
|
|
||||||
line = bcline(func, pc, recprefix)
|
|
||||||
if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
|
|
||||||
else
|
|
||||||
line = "0000 "..recprefix.." FUNCC \n"
|
|
||||||
callee = func
|
|
||||||
end
|
|
||||||
if pc <= 0 then
|
|
||||||
out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
|
|
||||||
else
|
|
||||||
out:write(line)
|
|
||||||
end
|
|
||||||
if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
|
|
||||||
out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Dump taken trace exits.
|
|
||||||
local function dump_texit(tr, ex, ngpr, nfpr, ...)
|
|
||||||
out:write("---- TRACE ", tr, " exit ", ex, "\n")
|
|
||||||
if dumpmode.X then
|
|
||||||
local regs = {...}
|
|
||||||
if jit.arch == "x64" then
|
|
||||||
for i=1,ngpr do
|
|
||||||
out:write(format(" %016x", regs[i]))
|
|
||||||
if i % 4 == 0 then out:write("\n") end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for i=1,ngpr do
|
|
||||||
out:write(format(" %08x", regs[i]))
|
|
||||||
if i % 8 == 0 then out:write("\n") end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if jit.arch == "mips" or jit.arch == "mipsel" then
|
|
||||||
for i=1,nfpr,2 do
|
|
||||||
out:write(format(" %+17.14g", regs[ngpr+i]))
|
|
||||||
if i % 8 == 7 then out:write("\n") end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for i=1,nfpr do
|
|
||||||
out:write(format(" %+17.14g", regs[ngpr+i]))
|
|
||||||
if i % 4 == 0 then out:write("\n") end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Detach dump handlers.
|
|
||||||
local function dumpoff()
|
|
||||||
if active then
|
|
||||||
active = false
|
|
||||||
jit.attach(dump_texit)
|
|
||||||
jit.attach(dump_record)
|
|
||||||
jit.attach(dump_trace)
|
|
||||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
|
||||||
out = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Open the output file and attach dump handlers.
|
|
||||||
local function dumpon(opt, outfile)
|
|
||||||
if active then dumpoff() end
|
|
||||||
|
|
||||||
local colormode = os.getenv("COLORTERM") and "A" or "T"
|
|
||||||
if opt then
|
|
||||||
opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local m = { t=true, b=true, i=true, m=true, }
|
|
||||||
if opt and opt ~= "" then
|
|
||||||
local o = sub(opt, 1, 1)
|
|
||||||
if o ~= "+" and o ~= "-" then m = {} end
|
|
||||||
for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
|
|
||||||
end
|
|
||||||
dumpmode = m
|
|
||||||
|
|
||||||
if m.t or m.b or m.i or m.s or m.m then
|
|
||||||
jit.attach(dump_trace, "trace")
|
|
||||||
end
|
|
||||||
if m.b then
|
|
||||||
jit.attach(dump_record, "record")
|
|
||||||
if not bcline then bcline = require("jit.bc").line end
|
|
||||||
end
|
|
||||||
if m.x or m.X then
|
|
||||||
jit.attach(dump_texit, "texit")
|
|
||||||
end
|
|
||||||
|
|
||||||
if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
|
|
||||||
if outfile then
|
|
||||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
|
||||||
else
|
|
||||||
out = stdout
|
|
||||||
end
|
|
||||||
|
|
||||||
m[colormode] = true
|
|
||||||
if colormode == "A" then
|
|
||||||
colorize = colorize_ansi
|
|
||||||
irtype = irtype_ansi
|
|
||||||
elseif colormode == "H" then
|
|
||||||
colorize = colorize_html
|
|
||||||
irtype = irtype_html
|
|
||||||
out:write(header_html)
|
|
||||||
else
|
|
||||||
colorize = colorize_text
|
|
||||||
irtype = irtype_text
|
|
||||||
end
|
|
||||||
|
|
||||||
active = true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Public module functions.
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
on = dumpon
|
|
||||||
off = dumpoff
|
|
||||||
start = dumpon -- For -j command line option.
|
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
{
|
|
||||||
engine_main_arrays_are_static;
|
|
||||||
engine_v8;
|
|
||||||
|
|
||||||
saveboard_maptext;
|
|
||||||
loadboard_maptext;
|
|
||||||
|
|
||||||
sector;
|
|
||||||
wall;
|
|
||||||
sprite;
|
|
||||||
tsprite;
|
|
||||||
spriteext;
|
|
||||||
|
|
||||||
Numsprites;
|
|
||||||
numsectors;
|
|
||||||
numwalls;
|
|
||||||
numyaxbunches;
|
|
||||||
spritesortcnt;
|
|
||||||
guniqhudid;
|
|
||||||
|
|
||||||
qsetmode;
|
|
||||||
rendmode;
|
|
||||||
totalclock;
|
|
||||||
randomseed;
|
|
||||||
xdim;
|
|
||||||
ydim;
|
|
||||||
windowxy1;
|
|
||||||
windowxy2;
|
|
||||||
yxaspect;
|
|
||||||
viewingrange;
|
|
||||||
|
|
||||||
sectorofwall_noquick;
|
|
||||||
getceilzofslopeptr;
|
|
||||||
getflorzofslopeptr;
|
|
||||||
getzsofslopeptr;
|
|
||||||
spriteheightofsptr;
|
|
||||||
|
|
||||||
changespritesect;
|
|
||||||
changespritestat;
|
|
||||||
|
|
||||||
headspritesect;
|
|
||||||
headspritestat;
|
|
||||||
prevspritesect;
|
|
||||||
prevspritestat;
|
|
||||||
nextspritesect;
|
|
||||||
nextspritestat;
|
|
||||||
tilesiz;
|
|
||||||
picanm;
|
|
||||||
|
|
||||||
show2dsector;
|
|
||||||
|
|
||||||
headsectbunch;
|
|
||||||
nextsectbunch;
|
|
||||||
|
|
||||||
krand;
|
|
||||||
ksqrt;
|
|
||||||
inside;
|
|
||||||
getangle;
|
|
||||||
Mulscale;
|
|
||||||
cansee;
|
|
||||||
hitscan;
|
|
||||||
dragpoint;
|
|
||||||
neartag;
|
|
||||||
getzrange;
|
|
||||||
clipmovex;
|
|
||||||
rotatesprite_;
|
|
||||||
setaspect;
|
|
||||||
|
|
||||||
getclosestcol_lim;
|
|
||||||
palookup;
|
|
||||||
palette;
|
|
||||||
getblendtab;
|
|
||||||
setblendtab;
|
|
||||||
setpalookup;
|
|
||||||
|
|
||||||
kopen4load;
|
|
||||||
kfilelength;
|
|
||||||
kclose;
|
|
||||||
kread;
|
|
||||||
klseek;
|
|
||||||
|
|
||||||
dist;
|
|
||||||
ldist;
|
|
||||||
|
|
||||||
updatesector;
|
|
||||||
updatesectorz;
|
|
||||||
updatesectorbreadth;
|
|
||||||
|
|
||||||
getticks;
|
|
||||||
gethiticks;
|
|
||||||
OSD_Printf;
|
|
||||||
|
|
||||||
Bcrc32;
|
|
||||||
|
|
||||||
luaJIT_BC__defs_editor;
|
|
||||||
luaJIT_BC_defs_common;
|
|
||||||
luaJIT_BC_engine_maptext;
|
|
||||||
luaJIT_BC_engine;
|
|
||||||
luaJIT_BC_bcarray;
|
|
||||||
luaJIT_BC_bcheck;
|
|
||||||
luaJIT_BC_bitar;
|
|
||||||
luaJIT_BC_xmath;
|
|
||||||
luaJIT_BC_v;
|
|
||||||
luaJIT_BC_dump;
|
|
||||||
luaJIT_BC_dis_x86;
|
|
||||||
luaJIT_BC_dis_x64;
|
|
||||||
|
|
||||||
g_argv;
|
|
||||||
|
|
||||||
|
|
||||||
_getnumber16;
|
|
||||||
getstring_simple;
|
|
||||||
listsearchpath;
|
|
||||||
LM_Register;
|
|
||||||
LM_Clear;
|
|
||||||
|
|
||||||
basepaltable;
|
|
||||||
};
|
|
|
@ -1,252 +0,0 @@
|
||||||
{
|
|
||||||
engine_main_arrays_are_static;
|
|
||||||
engine_v8;
|
|
||||||
|
|
||||||
saveboard_maptext;
|
|
||||||
loadboard_maptext;
|
|
||||||
|
|
||||||
sector;
|
|
||||||
wall;
|
|
||||||
sprite;
|
|
||||||
tsprite;
|
|
||||||
spriteext;
|
|
||||||
|
|
||||||
Numsprites;
|
|
||||||
numsectors;
|
|
||||||
numwalls;
|
|
||||||
numyaxbunches;
|
|
||||||
spritesortcnt;
|
|
||||||
guniqhudid;
|
|
||||||
|
|
||||||
qsetmode;
|
|
||||||
rendmode;
|
|
||||||
totalclock;
|
|
||||||
randomseed;
|
|
||||||
xdim;
|
|
||||||
ydim;
|
|
||||||
windowxy1;
|
|
||||||
windowxy2;
|
|
||||||
yxaspect;
|
|
||||||
viewingrange;
|
|
||||||
|
|
||||||
sectorofwall_noquick;
|
|
||||||
getceilzofslopeptr;
|
|
||||||
getflorzofslopeptr;
|
|
||||||
getzsofslopeptr;
|
|
||||||
spriteheightofsptr;
|
|
||||||
|
|
||||||
changespritesect;
|
|
||||||
changespritestat;
|
|
||||||
|
|
||||||
headspritesect;
|
|
||||||
headspritestat;
|
|
||||||
prevspritesect;
|
|
||||||
prevspritestat;
|
|
||||||
nextspritesect;
|
|
||||||
nextspritestat;
|
|
||||||
tilesiz;
|
|
||||||
picanm;
|
|
||||||
|
|
||||||
show2dsector;
|
|
||||||
|
|
||||||
headsectbunch;
|
|
||||||
nextsectbunch;
|
|
||||||
|
|
||||||
krand;
|
|
||||||
ksqrt;
|
|
||||||
inside;
|
|
||||||
getangle;
|
|
||||||
Mulscale;
|
|
||||||
cansee;
|
|
||||||
hitscan;
|
|
||||||
dragpoint;
|
|
||||||
neartag;
|
|
||||||
getzrange;
|
|
||||||
clipmovex;
|
|
||||||
rotatesprite_;
|
|
||||||
setaspect;
|
|
||||||
|
|
||||||
getclosestcol_lim;
|
|
||||||
palookup;
|
|
||||||
palette;
|
|
||||||
getblendtab;
|
|
||||||
setblendtab;
|
|
||||||
setpalookup;
|
|
||||||
|
|
||||||
kopen4load;
|
|
||||||
kfilelength;
|
|
||||||
kclose;
|
|
||||||
kread;
|
|
||||||
klseek;
|
|
||||||
|
|
||||||
pathsearchmode;
|
|
||||||
klistfree;
|
|
||||||
klistpath;
|
|
||||||
|
|
||||||
dist;
|
|
||||||
ldist;
|
|
||||||
|
|
||||||
updatesector;
|
|
||||||
updatesectorz;
|
|
||||||
updatesectorbreadth;
|
|
||||||
|
|
||||||
getticks;
|
|
||||||
gethiticks;
|
|
||||||
OSD_Printf;
|
|
||||||
|
|
||||||
Bcrc32;
|
|
||||||
|
|
||||||
luaJIT_BC_defs_common;
|
|
||||||
luaJIT_BC_engine_maptext;
|
|
||||||
luaJIT_BC_engine;
|
|
||||||
|
|
||||||
g_argv;
|
|
||||||
|
|
||||||
|
|
||||||
g_elCONSize;
|
|
||||||
g_elCON;
|
|
||||||
El_SetCON;
|
|
||||||
El_OnError;
|
|
||||||
|
|
||||||
basepaltable;
|
|
||||||
|
|
||||||
g_elSavecode;
|
|
||||||
El_FreeSaveCode;
|
|
||||||
El_SerializeGamevars;
|
|
||||||
El_RestoreGamevars;
|
|
||||||
El_GetLabelValue;
|
|
||||||
|
|
||||||
s_buildRev;
|
|
||||||
g_sizes_of_what;
|
|
||||||
g_sizes_of;
|
|
||||||
g_elFirstTime;
|
|
||||||
g_elCallDepth;
|
|
||||||
block_deletesprite;
|
|
||||||
myconnectindex;
|
|
||||||
g_RETURN;
|
|
||||||
g_elModules;
|
|
||||||
g_modDir;
|
|
||||||
g_elSessionVar;
|
|
||||||
|
|
||||||
g_mapInfo;
|
|
||||||
g_volumeNames;
|
|
||||||
|
|
||||||
kopen4loadfrommod;
|
|
||||||
|
|
||||||
g_scriptModules;
|
|
||||||
g_scriptModulesNum;
|
|
||||||
|
|
||||||
G_ConFile;
|
|
||||||
G_DoGameStartup;
|
|
||||||
C_DefineSound;
|
|
||||||
C_DefineMusic;
|
|
||||||
C_DefineQuote;
|
|
||||||
C_DefineVolumeName;
|
|
||||||
C_DefineVolumeFlags;
|
|
||||||
C_UndefineVolume;
|
|
||||||
C_DefineSkillName;
|
|
||||||
C_UndefineSkill;
|
|
||||||
C_DefineLevelName;
|
|
||||||
C_UndefineLevel;
|
|
||||||
C_DefineProjectile;
|
|
||||||
C_DefineGameFuncName;
|
|
||||||
C_DefineGameType;
|
|
||||||
C_SetDefName;
|
|
||||||
C_SetCfgName;
|
|
||||||
|
|
||||||
SCRIPT_GetNumber;
|
|
||||||
SCRIPT_PutNumber;
|
|
||||||
|
|
||||||
actor;
|
|
||||||
g_camera;
|
|
||||||
ud;
|
|
||||||
g_player;
|
|
||||||
g_player_ps;
|
|
||||||
g_playerWeapon;
|
|
||||||
g_weaponOverridden;
|
|
||||||
WeaponPickupSprites;
|
|
||||||
g_tile;
|
|
||||||
SpriteProjectile;
|
|
||||||
|
|
||||||
g_noResetVars;
|
|
||||||
A_ResetVars;
|
|
||||||
|
|
||||||
g_dynTileList;
|
|
||||||
g_dynSoundList;
|
|
||||||
|
|
||||||
apStrings;
|
|
||||||
|
|
||||||
screenpeek;
|
|
||||||
hudweap;
|
|
||||||
g_mostConcurrentPlayers;
|
|
||||||
g_deleteQueueSize;
|
|
||||||
g_blimpSpawnItems;
|
|
||||||
g_scriptVersion;
|
|
||||||
g_frameRate;
|
|
||||||
g_currentMenu;
|
|
||||||
g_earthquakeTime;
|
|
||||||
g_moveThingsCount;
|
|
||||||
CheatKeys;
|
|
||||||
g_logoFlags;
|
|
||||||
|
|
||||||
luaJIT_BC_lunacon;
|
|
||||||
luaJIT_BC_con_lang;
|
|
||||||
luaJIT_BC_randgen;
|
|
||||||
luaJIT_BC_stat;
|
|
||||||
luaJIT_BC_bitar;
|
|
||||||
luaJIT_BC_control;
|
|
||||||
luaJIT_BC_bcarray;
|
|
||||||
luaJIT_BC_bcheck;
|
|
||||||
luaJIT_BC_xmath;
|
|
||||||
luaJIT_BC__defs_game;
|
|
||||||
luaJIT_BC_v;
|
|
||||||
luaJIT_BC_dump;
|
|
||||||
luaJIT_BC_dis_x86;
|
|
||||||
luaJIT_BC_dis_x64;
|
|
||||||
luaJIT_BC_lunasave;
|
|
||||||
luaJIT_BC_fs;
|
|
||||||
|
|
||||||
rand_jkiss_u32;
|
|
||||||
rand_jkiss_dbl;
|
|
||||||
|
|
||||||
A_IncurDamage;
|
|
||||||
G_CheckActivatorMotion;
|
|
||||||
A_Dodge;
|
|
||||||
A_MoveSpriteClipdist;
|
|
||||||
P_DoQuote;
|
|
||||||
P_SetGamePalette;
|
|
||||||
G_AddUserQuote;
|
|
||||||
G_ClearCameraView;
|
|
||||||
VM_DrawTileGeneric;
|
|
||||||
G_InitTimer;
|
|
||||||
G_GetTimeDate;
|
|
||||||
G_ToggleWallInterpolation;
|
|
||||||
G_StartTrack;
|
|
||||||
VM_CheckSquished2;
|
|
||||||
Menu_Change;
|
|
||||||
|
|
||||||
KB_ScanCodeToString;
|
|
||||||
|
|
||||||
A_CheckAnySoundPlaying;
|
|
||||||
S_CheckSoundPlaying;
|
|
||||||
S_StopEnvSound;
|
|
||||||
S_StopAllSounds;
|
|
||||||
S_ChangeSoundPitch;
|
|
||||||
S_GetMusicPosition;
|
|
||||||
S_SetMusicPosition;
|
|
||||||
|
|
||||||
minitext_;
|
|
||||||
G_DrawTXDigiNumZ;
|
|
||||||
G_PrintGameText;
|
|
||||||
G_ScreenText;
|
|
||||||
G_ScreenTextSize;
|
|
||||||
G_PrintYourTime;
|
|
||||||
G_PrintParTime;
|
|
||||||
G_PrintDesignerTime;
|
|
||||||
G_PrintBestTime;
|
|
||||||
|
|
||||||
G_UpdateScreenArea;
|
|
||||||
G_SaveMapState;
|
|
||||||
G_RestoreMapState;
|
|
||||||
G_FreeMapState;
|
|
||||||
};
|
|
|
@ -1,522 +0,0 @@
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local C = ffi.C
|
|
||||||
|
|
||||||
local bcarray = require("bcarray")
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local error = error
|
|
||||||
local ipairs = ipairs
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
local decl = assert(decl) -- comes from above (_defs_game.lua or defs_m32.lua)
|
|
||||||
|
|
||||||
local ismapster32 = (C.LUNATIC_CLIENT == C.LUNATIC_CLIENT_MAPSTER32)
|
|
||||||
|
|
||||||
----------
|
|
||||||
|
|
||||||
decl[[
|
|
||||||
const int32_t qsetmode;
|
|
||||||
int32_t getclosestcol_lim(int32_t r, int32_t g, int32_t b, int32_t lastokcol);
|
|
||||||
char *palookup[256]; // MAXPALOOKUPS
|
|
||||||
uint8_t palette[768];
|
|
||||||
uint8_t *basepaltable[];
|
|
||||||
|
|
||||||
const char *getblendtab(int32_t blend);
|
|
||||||
void setblendtab(int32_t blend, const char *tab);
|
|
||||||
|
|
||||||
int32_t setpalookup(int32_t palnum, const uint8_t *shtab);
|
|
||||||
]]
|
|
||||||
|
|
||||||
if (ismapster32) then
|
|
||||||
ffi.cdef[[
|
|
||||||
int32_t _getnumber16(const char *namestart, int32_t num, int32_t maxnumber, char sign, const char *(func)(int32_t));
|
|
||||||
const char *getstring_simple(const char *querystr, const char *defaultstr, int32_t maxlen, int32_t completion);
|
|
||||||
|
|
||||||
typedef const char *(*luamenufunc_t)(void);
|
|
||||||
void LM_Register(const char *name, luamenufunc_t funcptr, const char *description);
|
|
||||||
void LM_Clear(void);
|
|
||||||
]]
|
|
||||||
end
|
|
||||||
|
|
||||||
----------
|
|
||||||
|
|
||||||
|
|
||||||
-- The API table
|
|
||||||
local engine = {}
|
|
||||||
|
|
||||||
|
|
||||||
local shtab_t -- forward-decl
|
|
||||||
|
|
||||||
local function cast_u8ptr(sth)
|
|
||||||
return ffi.cast("uint8_t *", sth)
|
|
||||||
end
|
|
||||||
|
|
||||||
local shtab_methods = {
|
|
||||||
-- Remap consecutive blocks of 16 color indices and return this new shade
|
|
||||||
-- table.
|
|
||||||
--
|
|
||||||
-- <idxs16>: table with idxs16[0] .. idxs16[15] >= 0 and <= 15
|
|
||||||
-- (i.e. 0-based indices of such 16-tuples)
|
|
||||||
--
|
|
||||||
-- For example, the table
|
|
||||||
-- { [0]=0,1, 2,3, 5,4, 6,7, 8,13, 10,11, 12,9, 14,15 }
|
|
||||||
-- TODO (...)
|
|
||||||
remap16 = function(sht, idxs16)
|
|
||||||
if (type(idxs16) ~= "table") then
|
|
||||||
error("invalid argument #2: must be a table", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=0,15 do
|
|
||||||
local idx = idxs16[i]
|
|
||||||
if (not (idx==nil or type(idx)=="number" and idx >= 0 and idx <= 15)) then
|
|
||||||
error("invalid reordering table: elements must be numbers in [0 .. 15], or nil", 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local newsht = shtab_t()
|
|
||||||
for sh=0,31 do
|
|
||||||
for i=0,15 do
|
|
||||||
ffi.copy(cast_u8ptr(newsht[sh]) + 16*i,
|
|
||||||
cast_u8ptr(sht[sh]) + 16*(idxs16[i] or i), 16)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return newsht
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
local function shtab_mt__index(sht, idx)
|
|
||||||
local method = shtab_methods[idx]
|
|
||||||
if (method) then
|
|
||||||
return method
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local pal256_t = bcarray.new("uint8_t", 256, "color index 256-tuple")
|
|
||||||
local SIZEOF_PAL256 = ffi.sizeof(pal256_t)
|
|
||||||
|
|
||||||
-- The shade table type, effectively a bound-checked uint8_t [32][256]:
|
|
||||||
shtab_t = bcarray.new(pal256_t, 32, "shade table", nil, nil, { __index = shtab_mt__index })
|
|
||||||
local SIZEOF_SHTAB = ffi.sizeof(shtab_t)
|
|
||||||
|
|
||||||
local blendtab_t = bcarray.new(pal256_t, 256, "blending table")
|
|
||||||
local SIZEOF_BLENDTAB = ffi.sizeof(blendtab_t)
|
|
||||||
|
|
||||||
local RESERVEDPALS = 8 -- KEEPINSYNC build.h: assure that ours is >= theirs
|
|
||||||
engine.RESERVEDPALS = RESERVEDPALS
|
|
||||||
|
|
||||||
local MAXBLENDTABS = 256 -- KEEPINSYNC build.h
|
|
||||||
|
|
||||||
local function check_palidx(i)
|
|
||||||
if (type(i) ~= "number" or not (i >= 0 and i <= 255-RESERVEDPALS)) then
|
|
||||||
error("invalid argument #1: palette swap index must be in the range [0 .. "..255-RESERVEDPALS.."]", 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function check_blendidx(i)
|
|
||||||
if (type(i) ~= "number" or not (i >= 0 and i <= MAXBLENDTABS-1)) then
|
|
||||||
error("invalid argument #1: blending table index must be in the range [0 .. ".. MAXBLENDTABS-1 .."]", 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function err_uncommon_shade_table(ret)
|
|
||||||
if (ret == -1) then
|
|
||||||
error("loaded engine shade tables don't have 32 gradients of shade", 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function palookup_isdefault(palnum) -- KEEPINSYNC engine.c
|
|
||||||
return (C.palookup[palnum] == nil or (palnum ~= 0 and C.palookup[palnum] == C.palookup[0]))
|
|
||||||
end
|
|
||||||
|
|
||||||
function engine.shadetab()
|
|
||||||
return shtab_t()
|
|
||||||
end
|
|
||||||
|
|
||||||
function engine.blendtab()
|
|
||||||
return blendtab_t()
|
|
||||||
end
|
|
||||||
|
|
||||||
function engine.getshadetab(palidx)
|
|
||||||
check_palidx(palidx)
|
|
||||||
if (palookup_isdefault(palidx)) then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local ret = C.setpalookup(palidx, nil)
|
|
||||||
err_uncommon_shade_table(ret)
|
|
||||||
|
|
||||||
local sht = shtab_t()
|
|
||||||
ffi.copy(sht, C.palookup[palidx], SIZEOF_SHTAB)
|
|
||||||
return sht
|
|
||||||
end
|
|
||||||
|
|
||||||
function engine.getblendtab(blendidx)
|
|
||||||
check_blendidx(blendidx)
|
|
||||||
|
|
||||||
local ptr = C.getblendtab(blendidx)
|
|
||||||
if (ptr == nil) then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local tab = blendtab_t()
|
|
||||||
ffi.copy(tab, ptr, SIZEOF_BLENDTAB)
|
|
||||||
return tab
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function check_first_time()
|
|
||||||
if (not ismapster32 and C.g_elFirstTime == 0) then
|
|
||||||
error("may be called only while LUNATIC_FIRST_TIME is true", 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function engine.setshadetab(palidx, shtab)
|
|
||||||
check_first_time()
|
|
||||||
check_palidx(palidx)
|
|
||||||
|
|
||||||
if (not ffi.istype(shtab_t, shtab)) then
|
|
||||||
error("invalid argument #2: must be a shade table obtained by shadetab()", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not ismapster32 and not palookup_isdefault(palidx)) then
|
|
||||||
error("attempt to override already defined shade table", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ret = C.setpalookup(palidx, cast_u8ptr(shtab))
|
|
||||||
err_uncommon_shade_table(ret)
|
|
||||||
end
|
|
||||||
|
|
||||||
function engine.setblendtab(blendidx, tab)
|
|
||||||
check_first_time()
|
|
||||||
check_blendidx(blendidx)
|
|
||||||
|
|
||||||
if (not ffi.istype(blendtab_t, tab)) then
|
|
||||||
error("invalid argument #2: must be a blending table obtained by blendtab()", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not ismapster32 and C.getblendtab(blendidx) ~= nil) then
|
|
||||||
error("attempt to override already defined blending table", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
C.setblendtab(blendidx, cast_u8ptr(tab))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function check_colcomp(a)
|
|
||||||
if (type(a) ~= "number" or not (a >= 0 and a < 256)) then
|
|
||||||
error("color component must be in the range [0 .. 256)", 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- TODO: other base palettes?
|
|
||||||
function engine.getrgb(colidx)
|
|
||||||
if (type(colidx) ~= "number" or not (colidx >= 0 and colidx <= 255)) then
|
|
||||||
error("color index must be in the range [0 .. 255]", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- NOTE: In the game, palette[255*{0..2}] is set to 0 in
|
|
||||||
-- G_LoadExtraPalettes() via G_Startup(). However, that's after Lua state
|
|
||||||
-- initialization (i.e. when LUNATIC_FIRST_TIME would be true), and in the
|
|
||||||
-- editor, it's never changed from the purple color. Therefore, I think
|
|
||||||
-- it's more useful to always return the fully black color here.
|
|
||||||
if (colidx == 255) then
|
|
||||||
return 0, 0, 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local rgbptr = C.palette + 3*colidx
|
|
||||||
return rgbptr[0], rgbptr[1], rgbptr[2]
|
|
||||||
end
|
|
||||||
|
|
||||||
function engine.nearcolor(r, g, b, lastokcol)
|
|
||||||
check_colcomp(r)
|
|
||||||
check_colcomp(g)
|
|
||||||
check_colcomp(b)
|
|
||||||
|
|
||||||
if (lastokcol == nil) then
|
|
||||||
lastokcol = 255
|
|
||||||
elseif (type(lastokcol)~="number" or not (lastokcol >= 0 and lastokcol <= 255)) then
|
|
||||||
error("invalid argument #4 <lastokcol>: must be in the range [0 .. 255]", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
return C.getclosestcol_lim(r, g, b, lastokcol)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---------- Mapster32-only functions ----------
|
|
||||||
|
|
||||||
if (ismapster32) then
|
|
||||||
local io = require("io")
|
|
||||||
local math = require("math")
|
|
||||||
local string = require("string")
|
|
||||||
|
|
||||||
ffi.cdef[[size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, void * restrict stream);]]
|
|
||||||
|
|
||||||
local function validate_more_blendtabs(moreblends, kindname, gettabfunc)
|
|
||||||
if (moreblends == nil) then
|
|
||||||
return nil, nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Additional blending tables: validate <moreblends> table.
|
|
||||||
if (type(moreblends) ~= "table") then
|
|
||||||
error("invalid argument #4: must be a table", 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
local haveblend = { [0]=true }
|
|
||||||
local blendnumtab, blendptrtab = {}, {}
|
|
||||||
|
|
||||||
for i=1,#moreblends do
|
|
||||||
local tmp = moreblends[i]
|
|
||||||
local blendspec = (type(tmp) == "number") and { tmp, tmp } or tmp
|
|
||||||
|
|
||||||
if (not (type(blendspec) == "table" and #blendspec == 2)) then
|
|
||||||
error("invalid argument #4: must contain numbers or 2-tables", 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
local blend1, blend2 = math.floor(blendspec[1]), math.floor(blendspec[2])
|
|
||||||
|
|
||||||
if (not (type(blend1)=="number" and blend1 >= 1 and blend1 <= 255 and
|
|
||||||
type(blend2)=="number" and blend2 >= 1 and blend2 <= 255)) then
|
|
||||||
error("invalid argument #4: "..kindname.." table numbers must be in [1 .. 255]", 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
for bi=blend1,blend2 do
|
|
||||||
if (haveblend[bi]) then
|
|
||||||
error("invalid argument #4: duplicate "..kindname.." table number "..bi, 3)
|
|
||||||
end
|
|
||||||
haveblend[bi] = true
|
|
||||||
|
|
||||||
local ptr = gettabfunc(bi)
|
|
||||||
if (ptr == nil) then
|
|
||||||
error("invalid argument #4: "..kindname.." table for number "..bi.." is void", 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
blendnumtab[#blendnumtab+1] = bi
|
|
||||||
blendptrtab[#blendptrtab+1] = ptr
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(#blendnumtab <= 255)
|
|
||||||
return blendnumtab, blendptrtab
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ok, errmsg, nummoreblends = engine.savePaletteDat(
|
|
||||||
-- filename [, palnum [, blendnum [, moreblends [, lognumalphatabs]]]])
|
|
||||||
function engine.savePaletteDat(filename, palnum, blendnum, moreblends, lognumalphatabs)
|
|
||||||
local sht = engine.getshadetab(palnum or 0)
|
|
||||||
local tab = engine.getblendtab(blendnum or 0)
|
|
||||||
|
|
||||||
if (sht == nil) then
|
|
||||||
return nil, "no shade table with number "..palnum
|
|
||||||
elseif (tab == nil) then
|
|
||||||
return nil, "no blending table with number "..blendnum
|
|
||||||
end
|
|
||||||
|
|
||||||
local blendnumtab, blendptrtab = validate_more_blendtabs(
|
|
||||||
moreblends, "blending", C.getblendtab)
|
|
||||||
|
|
||||||
if (lognumalphatabs ~= nil) then
|
|
||||||
if (not (type(lognumalphatabs)=="number" and lognumalphatabs >= 1 and lognumalphatabs <= 7)) then
|
|
||||||
error("invalid argument #5: must be a number in [1 .. 7]", 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local f, errmsg = io.open(filename, "wb+")
|
|
||||||
if (f == nil) then
|
|
||||||
return nil, errmsg
|
|
||||||
end
|
|
||||||
|
|
||||||
local truncpal = pal256_t()
|
|
||||||
ffi.copy(truncpal, C.palette, SIZEOF_PAL256)
|
|
||||||
for i=0,255 do
|
|
||||||
truncpal[i] = bit.rshift(truncpal[i], 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local n1 = C.fwrite(truncpal, 3, 256, f)
|
|
||||||
f:write("\032\000") -- int16_t numshades
|
|
||||||
local n3 = C.fwrite(sht, 256, 32, f)
|
|
||||||
local n4 = C.fwrite(tab, 256, 256, f)
|
|
||||||
|
|
||||||
if (n1 ~= 256 or n3 ~= 32 or n4 ~= 256) then
|
|
||||||
return nil, "failed writing classic PALETTE.DAT data"
|
|
||||||
end
|
|
||||||
|
|
||||||
if (blendnumtab ~= nil) then
|
|
||||||
f:write("MoreBlendTab")
|
|
||||||
f:write(string.char(#blendnumtab))
|
|
||||||
|
|
||||||
for i=1,#blendnumtab do
|
|
||||||
f:write(string.char(blendnumtab[i]))
|
|
||||||
if (C.fwrite(blendptrtab[i], 256, 256, f) ~= 256) then
|
|
||||||
return nil, "failed writing additional blending table"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (lognumalphatabs ~= nil) then
|
|
||||||
-- XXX: no checking whether these blending tables 1 to
|
|
||||||
-- 1<<lognumalphatabs have been written.
|
|
||||||
f:write(string.char(lognumalphatabs))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
f:close()
|
|
||||||
|
|
||||||
return true, nil, (blendnumtab ~= nil) and #blendnumtab or 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ok, errmsg, numlookups = engine.saveLookupDat(filename, lookups)
|
|
||||||
function engine.saveLookupDat(filename, lookups)
|
|
||||||
if (lookups == nil) then
|
|
||||||
-- set to an invalid value, validate_more_blendtabs will error
|
|
||||||
lookups = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local lookupnumtab, lookupptrtab = validate_more_blendtabs(
|
|
||||||
lookups, "lookup", engine.getshadetab)
|
|
||||||
|
|
||||||
local f, errmsg = io.open(filename, "wb+")
|
|
||||||
if (f == nil) then
|
|
||||||
return nil, errmsg
|
|
||||||
end
|
|
||||||
|
|
||||||
f:write(string.char(#lookupnumtab))
|
|
||||||
|
|
||||||
for i=1,#lookupnumtab do
|
|
||||||
f:write(string.char(lookupnumtab[i]))
|
|
||||||
if (C.fwrite(lookupptrtab[i], 1, 256, f) ~= 256) then
|
|
||||||
return nil, "failed writing lookup table"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Write five base palettes
|
|
||||||
for i=1,5 do
|
|
||||||
local bpi = (i==3 or i==4) and 4+3-i or i
|
|
||||||
|
|
||||||
local truncbasepal = pal256_t()
|
|
||||||
ffi.copy(truncbasepal, C.basepaltable[bpi], SIZEOF_PAL256)
|
|
||||||
for j=0,255 do
|
|
||||||
truncbasepal[j] = bit.rshift(truncbasepal[j], 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (C.fwrite(truncbasepal, 1, 768, f) ~= 768) then
|
|
||||||
return nil, "failed writing base palette"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
f:close()
|
|
||||||
|
|
||||||
return true, nil, #lookupnumtab
|
|
||||||
end
|
|
||||||
|
|
||||||
local hexmap = {
|
|
||||||
[0] = 0, -14, -- 0, 1: gray ramp
|
|
||||||
14, 0, -- 2, 3: skin color ramp
|
|
||||||
0, 14, -- 4, 5: blue ramp (second part first)
|
|
||||||
14, 0, -- 6, 7: nightvision yellow/green
|
|
||||||
14, -- 8: red first part...
|
|
||||||
8, -- 9: yellow (slightly more red than green)
|
|
||||||
14, 0, -- 10, 11: almost gray ramp, but with a slight red hue
|
|
||||||
8, -- 12: "dirty" orange
|
|
||||||
0, -- 13: ...red second part
|
|
||||||
8, -- 14: blue-purple-red
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Setup base palette 1 (water) to contain one color for each consecutive
|
|
||||||
-- 16-tuple (which I'm calling a 'hex' for brevity), except for the last
|
|
||||||
-- one with the fullbrights.
|
|
||||||
function engine.setupDebugBasePal()
|
|
||||||
for i=0,14 do
|
|
||||||
local ptr = C.basepaltable[1] + 3*(16*i)
|
|
||||||
local src = C.basepaltable[0] + 3*(16*i) + 3*hexmap[i]
|
|
||||||
local r, g, b = src[0], src[1], src[2]
|
|
||||||
|
|
||||||
for j=0,15 do
|
|
||||||
local dst = ptr + 3*j
|
|
||||||
dst[0], dst[1], dst[2] = r, g, b
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function engine.linearizeBasePal()
|
|
||||||
for _, begi in ipairs{0, 32, 96, 160} do
|
|
||||||
local ptr = C.basepaltable[0] + 3*begi
|
|
||||||
local refcol = ptr + 3*31
|
|
||||||
|
|
||||||
for i=0,30 do
|
|
||||||
for c=0,2 do
|
|
||||||
ptr[3*i + c] = i*refcol[c]/31
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, begi in ipairs{128, 144, 192, 208, 224} do
|
|
||||||
local ptr = C.basepaltable[0] + 3*begi
|
|
||||||
|
|
||||||
for i=0,3*15+2 do
|
|
||||||
ptr[i] = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Interfaces to Mapster32's status bar menu
|
|
||||||
|
|
||||||
local pcall = pcall
|
|
||||||
|
|
||||||
function engine.clearMenu()
|
|
||||||
C.LM_Clear()
|
|
||||||
end
|
|
||||||
|
|
||||||
function engine.registerMenuFunc(name, func, description)
|
|
||||||
if (type(name) ~= "string") then
|
|
||||||
error("invalid argument #1: must be a string", 2)
|
|
||||||
end
|
|
||||||
if (type(func) ~= "function") then
|
|
||||||
error("invalid argument #2: must be a function", 2)
|
|
||||||
end
|
|
||||||
if (description~=nil and type(description)~="string") then
|
|
||||||
error("invalid argument #3: must be nil or a string", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local safefunc = function()
|
|
||||||
local ok, errmsg = pcall(func)
|
|
||||||
if (not ok) then
|
|
||||||
return errmsg
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
C.LM_Register(name, safefunc, description)
|
|
||||||
end
|
|
||||||
|
|
||||||
engine.GETNUMFLAG = {
|
|
||||||
NEG_ALLOWED = 1,
|
|
||||||
AUTOCOMPL_NAMES = 2,
|
|
||||||
AUTOCOMPL_TAGLAB = 4,
|
|
||||||
RET_M1_ON_CANCEL = 8,
|
|
||||||
|
|
||||||
NEXTFREE = 16,
|
|
||||||
}
|
|
||||||
|
|
||||||
function engine.getnumber16(namestart, num, maxnumber, flags)
|
|
||||||
if (C.qsetmode == 200) then
|
|
||||||
error("getnumber16 must be called from 2D mode", 2)
|
|
||||||
end
|
|
||||||
if (type(namestart)~="string") then
|
|
||||||
error("invalid argument #1: must be a string", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
return C._getnumber16(namestart, num, maxnumber, flags or 8, nil) -- RET_M1_ON_CANCEL
|
|
||||||
end
|
|
||||||
|
|
||||||
function engine.getstring(querystr)
|
|
||||||
if (type(querystr) ~= "string") then
|
|
||||||
error("invalid argument #2: must be a string", 2)
|
|
||||||
end
|
|
||||||
local cstr = C.getstring_simple(querystr, nil, 0, 0)
|
|
||||||
return cstr~=nil and ffi.string(cstr) or nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Done!
|
|
||||||
return engine
|
|
|
@ -1,538 +0,0 @@
|
||||||
-- MAPTEXT
|
|
||||||
-- Lunatic: routines for reading and writing map-text.
|
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local ffiC = ffi.C
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local loadstring = loadstring
|
|
||||||
local pairs = pairs
|
|
||||||
local pcall = pcall
|
|
||||||
local print = print
|
|
||||||
local setfenv = setfenv
|
|
||||||
local tonumber = tonumber
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
local readintostr = assert(string.readintostr)
|
|
||||||
|
|
||||||
local io = require("io")
|
|
||||||
local string = require("string")
|
|
||||||
|
|
||||||
|
|
||||||
ffi.cdef[[
|
|
||||||
int32_t (*saveboard_maptext)(const char *filename, const vec3_t *dapos, int16_t daang, int16_t dacursectnum);
|
|
||||||
int32_t (*loadboard_maptext)(int32_t fil, vec3_t *dapos, int16_t *daang, int16_t *dacursectnum);
|
|
||||||
]]
|
|
||||||
|
|
||||||
|
|
||||||
--== COMMON ==--
|
|
||||||
|
|
||||||
local sector_members = {
|
|
||||||
-- Mandatory positional members first, [pos]=<name>.
|
|
||||||
"ceilingz", "floorz",
|
|
||||||
"ceilingpicnum", "floorpicnum",
|
|
||||||
"ceilingshade", "floorshade";
|
|
||||||
|
|
||||||
-- If other positional members are to be added, they must be optional
|
|
||||||
-- for backwards compatibility.
|
|
||||||
|
|
||||||
-- Optional key/value members next.
|
|
||||||
B = { "ceilingbunch", -1 }, b = { "floorbunch", -1 }, -- default: -1
|
|
||||||
F = "ceilingstat", f = "floorstat", -- default: 0
|
|
||||||
H = "ceilingheinum", h = "floorheinum",
|
|
||||||
P = "ceilingpal", p = "floorpal",
|
|
||||||
X = "ceilingxpanning", x = "floorxpanning",
|
|
||||||
Y = "ceilingypanning", y = "floorypanning",
|
|
||||||
|
|
||||||
v = "visibility",
|
|
||||||
_ = "fogpal",
|
|
||||||
o = "lotag", i = "hitag", e = { "extra", -1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Defines the order in which the members are written out. A space denotes that
|
|
||||||
-- a newline should appear in the output. KEEPINSYNC with sector_members.
|
|
||||||
local sector_ord = { mand="12 34 56 ", opt="Bb Ff Hh Pp Xx Yy v _ oie" }
|
|
||||||
|
|
||||||
-- KEEPINSYNC with sector_members.
|
|
||||||
local sector_default = ffi.new("const sectortype", { ceilingbunch=-1, floorbunch=-1, extra=-1 })
|
|
||||||
|
|
||||||
|
|
||||||
local wall_members = {
|
|
||||||
-- mandatory
|
|
||||||
"point2", -- special: 0, 1 or 2 in map-text
|
|
||||||
"x", "y",
|
|
||||||
"nextwall",
|
|
||||||
"picnum",
|
|
||||||
"shade",
|
|
||||||
"xrepeat", "yrepeat",
|
|
||||||
"xpanning", "ypanning";
|
|
||||||
|
|
||||||
-- optional
|
|
||||||
f = "cstat",
|
|
||||||
m = "overpicnum", b = "blend",
|
|
||||||
p = "pal",
|
|
||||||
w = { "upwall", -1 }, W = { "dnwall", -1 },
|
|
||||||
o = "lotag", i = "hitag", e = { "extra", -1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
local wall_ord = { mand="1 23 4 5 6 78 90 ", opt="f mb p wW oie" }
|
|
||||||
local wall_default = ffi.new("const walltype", { extra = -1, upwall=-1, dnwall=-1 })
|
|
||||||
|
|
||||||
|
|
||||||
local sprite_members = {
|
|
||||||
-- mandatory
|
|
||||||
"x", "y", "z",
|
|
||||||
"ang",
|
|
||||||
"sectnum",
|
|
||||||
"picnum",
|
|
||||||
"cstat",
|
|
||||||
"shade",
|
|
||||||
"xrepeat", "yrepeat",
|
|
||||||
|
|
||||||
-- optional
|
|
||||||
p = "pal",
|
|
||||||
c = { "clipdist", 32 },
|
|
||||||
b = "blend",
|
|
||||||
x = "xoffset", y = "yoffset",
|
|
||||||
s = "statnum",
|
|
||||||
w = { "owner", -1 },
|
|
||||||
X = "xvel", Y = "yvel", Z = "zvel",
|
|
||||||
o = "lotag", i = "hitag", e = { "extra", -1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
local sprite_ord = { mand="123 4 5 6 7 8 90 ", opt="p c b xy s w XYZ oie" }
|
|
||||||
local sprite_default = ffi.new("const spritetype", { clipdist=32, owner=-1, extra=-1 })
|
|
||||||
|
|
||||||
|
|
||||||
--== SAVING ==--
|
|
||||||
|
|
||||||
local function write_struct(f, struct, members, ord)
|
|
||||||
-- Write mandatory members first.
|
|
||||||
local str = ord.mand:gsub(".",
|
|
||||||
function(s)
|
|
||||||
local num = (s=="0") and 10 or tonumber(s)
|
|
||||||
return (s==" ") and "\n" or (struct[members[num]]..",")
|
|
||||||
end)
|
|
||||||
f:write("{"..str)
|
|
||||||
|
|
||||||
local havesth = false
|
|
||||||
|
|
||||||
-- Write optional members next.
|
|
||||||
str = ord.opt:gsub(".",
|
|
||||||
function(s)
|
|
||||||
if (s==" ") then
|
|
||||||
local ohavesth = havesth
|
|
||||||
havesth = false
|
|
||||||
return ohavesth and "\n" or ""
|
|
||||||
end
|
|
||||||
|
|
||||||
local memb = members[s]
|
|
||||||
local mname = (type(memb)=="table") and memb[1] or memb
|
|
||||||
local mdefault = (type(memb)=="table") and memb[2] or 0
|
|
||||||
|
|
||||||
local val = struct[mname]
|
|
||||||
|
|
||||||
if (val~=mdefault) then
|
|
||||||
havesth = true
|
|
||||||
return s.."="..val..","
|
|
||||||
else
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
local neednl = (#str>0 and str:sub(-1)~="\n")
|
|
||||||
f:write(str..(neednl and "\n" or "").."},\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- common
|
|
||||||
local function check_bad_point2()
|
|
||||||
local lastloopstart = 0
|
|
||||||
|
|
||||||
for i=0,ffiC.numwalls-1 do
|
|
||||||
local p2 = ffiC.wall[i].point2
|
|
||||||
|
|
||||||
if (not (p2 == i+1 or (p2 ~= i and p2 == lastloopstart))) then
|
|
||||||
-- If we hit this, the map is seriously corrupted!
|
|
||||||
print(string.format("INTERNAL ERROR: wall[%d].point2=%d invalid", i, p2))
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
if (p2 ~= i+1) then
|
|
||||||
lastloopstart = i+1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function lastwallofsect(s)
|
|
||||||
return ffiC.sector[s].wallptr + ffiC.sector[s].wallnum - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- In map-text, instead of saving wall[].point2, we store whether a particular
|
|
||||||
-- wall is the last one in its loop instead: the on-disk wall[i].point2 is
|
|
||||||
-- * 2 if wall i is last of its sector (no need to save sector's .wallnum),
|
|
||||||
-- * 1 if wall i is last of its loop,
|
|
||||||
-- * 0 otherwise.
|
|
||||||
-- This function prepares saving to map-text by tweaking the wall[].point2
|
|
||||||
-- members in-place.
|
|
||||||
local function save_tweak_point2()
|
|
||||||
-- Check first.
|
|
||||||
if (check_bad_point2()) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Do it for real.
|
|
||||||
local lastloopstart = 0
|
|
||||||
local cursect, curlastwall = 0, lastwallofsect(0)
|
|
||||||
|
|
||||||
for i=0,ffiC.numwalls-1 do
|
|
||||||
local wal = ffiC.wall[i]
|
|
||||||
|
|
||||||
if (wal.point2 == i+1) then
|
|
||||||
wal.point2 = 0
|
|
||||||
else
|
|
||||||
-- Wall i is last point in loop.
|
|
||||||
|
|
||||||
if (i==curlastwall) then
|
|
||||||
-- ... and also last wall of sector.
|
|
||||||
cursect = cursect+1
|
|
||||||
curlastwall = lastwallofsect(cursect)
|
|
||||||
|
|
||||||
wal.point2 = 2
|
|
||||||
else
|
|
||||||
wal.point2 = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
lastloopstart = i+1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Common: restore tweaked point2 members to actual wall indices.
|
|
||||||
-- If <alsosectorp> is true, also set sector's .wallptr and .wallnum members.
|
|
||||||
local function restore_point2(alsosectorp)
|
|
||||||
local lastloopstart = 0
|
|
||||||
local cursect, curfirstwall = 0, 0
|
|
||||||
|
|
||||||
for i=0,ffiC.numwalls-1 do
|
|
||||||
local wal = ffiC.wall[i]
|
|
||||||
local islast = (wal.point2~=0)
|
|
||||||
|
|
||||||
if (not islast) then
|
|
||||||
wal.point2 = i+1
|
|
||||||
else
|
|
||||||
-- Wall i is last point in loop.
|
|
||||||
|
|
||||||
if (alsosectorp and wal.point2 == 2) then
|
|
||||||
-- ... and also last wall of sector.
|
|
||||||
|
|
||||||
if (cursect==ffiC.MAXSECTORS) then
|
|
||||||
return true -- Too many sectors.
|
|
||||||
end
|
|
||||||
|
|
||||||
ffiC.sector[cursect].wallptr = curfirstwall
|
|
||||||
ffiC.sector[cursect].wallnum = i-curfirstwall+1
|
|
||||||
cursect = cursect+1
|
|
||||||
curfirstwall = i+1
|
|
||||||
end
|
|
||||||
|
|
||||||
wal.point2 = lastloopstart
|
|
||||||
lastloopstart = i+1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function saveboard_maptext(filename, pos, ang, cursectnum)
|
|
||||||
assert(ffiC.numsectors > 0)
|
|
||||||
|
|
||||||
-- First, temporarily tweak wall[].point2.
|
|
||||||
if (save_tweak_point2()) then
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- We open in binary mode so that newlines get written out as one byte even
|
|
||||||
-- on Windows.
|
|
||||||
local f, msg = io.open(ffi.string(filename), "wb")
|
|
||||||
|
|
||||||
if (f == nil) then
|
|
||||||
print(string.format("Couldn't open \"%s\" for writing: %s\n", filename, msg))
|
|
||||||
restore_point2(false)
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Write header.
|
|
||||||
f:write(string.format("--EDuke32 map\n"..
|
|
||||||
"return {\n"..
|
|
||||||
"version=10,\n\n"..
|
|
||||||
|
|
||||||
"pos={%d,%d,%d},\n"..
|
|
||||||
"sectnum=%d,\n"..
|
|
||||||
"ang=%d,\n\n",
|
|
||||||
|
|
||||||
pos.x, pos.y, pos.z,
|
|
||||||
cursectnum,
|
|
||||||
ang))
|
|
||||||
|
|
||||||
-- Sectors.
|
|
||||||
f:write("sector={\n")
|
|
||||||
for i=0,ffiC.numsectors-1 do
|
|
||||||
write_struct(f, ffiC.sector[i], sector_members, sector_ord)
|
|
||||||
end
|
|
||||||
f:write("},\n\n")
|
|
||||||
|
|
||||||
-- Walls.
|
|
||||||
f:write("wall={\n")
|
|
||||||
for i=0,ffiC.numwalls-1 do
|
|
||||||
write_struct(f, ffiC.wall[i], wall_members, wall_ord)
|
|
||||||
end
|
|
||||||
f:write("},\n\n")
|
|
||||||
|
|
||||||
-- Sprites.
|
|
||||||
f:write("sprite={\n")
|
|
||||||
for i=0,ffiC.MAXSPRITES-1 do
|
|
||||||
if (ffiC.sprite[i].statnum ~= ffiC.MAXSTATUS) then
|
|
||||||
write_struct(f, ffiC.sprite[i], sprite_members, sprite_ord)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
f:write("},\n\n")
|
|
||||||
|
|
||||||
f:write("}\n");
|
|
||||||
|
|
||||||
-- Done.
|
|
||||||
f:close()
|
|
||||||
restore_point2(false)
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--== LOADING ==--
|
|
||||||
|
|
||||||
local function isnum(v)
|
|
||||||
return (type(v)=="number")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function istab(v)
|
|
||||||
return (type(v)=="table")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks whether <tab> is a table all values of <tab> are of type <extype>.
|
|
||||||
local function allxtab(tab, extype)
|
|
||||||
if (not istab(tab)) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
for _,val in pairs(tab) do
|
|
||||||
if (type(val) ~= extype) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Is table of all numbers?
|
|
||||||
local function allnumtab(tab) return allxtab(tab, "number") end
|
|
||||||
-- Is table of all tables?
|
|
||||||
local function alltabtab(tab) return allxtab(tab, "table") end
|
|
||||||
|
|
||||||
-- Is table of tables of all numbers? Additionally, each must contain exactly
|
|
||||||
-- as many mandatory positional entries as given by the <members> table.
|
|
||||||
local function tabofnumtabs(tab, members)
|
|
||||||
for i=1,#tab do
|
|
||||||
if (not allnumtab(tab[i])) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local nummand = #members -- number of mandatory entries
|
|
||||||
if (#tab[i] ~= nummand) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Read data from Lua table <stab> into C struct <cs>, using the struct
|
|
||||||
-- description <members>.
|
|
||||||
-- Returns true on error.
|
|
||||||
local function read_struct(cs, stab, members, defaults)
|
|
||||||
-- Clear struct to default values.
|
|
||||||
ffi.copy(cs, defaults, ffi.sizeof(defaults))
|
|
||||||
|
|
||||||
-- Read mandatory positional members.
|
|
||||||
for i=1,#members do
|
|
||||||
cs[members[i]] = stab[i]
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Read optional key/value members.
|
|
||||||
for k,val in pairs(stab) do
|
|
||||||
if (members[k]==nil) then
|
|
||||||
-- No such member abbreviation for the given struct.
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local memb = istab(members[k]) and members[k][1] or members[k]
|
|
||||||
cs[memb] = val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local RETERR = -4
|
|
||||||
|
|
||||||
local function loadboard_maptext(fil, posptr, angptr, cursectnumptr)
|
|
||||||
-- Read the whole map-text as string.
|
|
||||||
local str = readintostr(fil)
|
|
||||||
|
|
||||||
if (str == nil) then
|
|
||||||
return RETERR
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Strip all one-line comments (currently, only the header).
|
|
||||||
str = str:gsub("%-%-.-\n", "")
|
|
||||||
|
|
||||||
--- Preliminary (pseudo-syntactical) validation ---
|
|
||||||
|
|
||||||
-- Whitelist approach: map-text may only contain certain characters. This
|
|
||||||
-- excludes various potentially 'bad' operations (such as calling a
|
|
||||||
-- function) in one blow. Also, this assures (by exclusion) that the Lua
|
|
||||||
-- code contains no long comments, strings, or function calls.
|
|
||||||
if (not str:find("^[ A-Za-z_0-9{},%-\n=]+$")) then
|
|
||||||
return RETERR-1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- The map-text code must return a single table.
|
|
||||||
if (not str:find("^return %b{}\n$")) then
|
|
||||||
return RETERR-2
|
|
||||||
end
|
|
||||||
|
|
||||||
local func, errmsg = loadstring(str, "maptext")
|
|
||||||
if (func == nil) then
|
|
||||||
print("Error preloading map-text Lua code: "..errmsg)
|
|
||||||
return RETERR-3
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Completely empty the function's environment as an additional safety
|
|
||||||
-- measure, then run the chunk protected! (XXX: currently a bit pointless
|
|
||||||
-- because of the asserts below.)
|
|
||||||
local ok, map = pcall(setfenv(func, {}))
|
|
||||||
if (not ok) then
|
|
||||||
print("Error executing map-text Lua code: "..map)
|
|
||||||
return RETERR-4
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(istab(map))
|
|
||||||
-- OK, now 'map' contains the map data.
|
|
||||||
|
|
||||||
--- Structural validation ---
|
|
||||||
|
|
||||||
-- Check types.
|
|
||||||
if (not isnum(map.version) or not allnumtab(map.pos) or #map.pos~=3 or
|
|
||||||
not isnum(map.sectnum) or not isnum(map.ang))
|
|
||||||
then
|
|
||||||
return RETERR-5
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not (map.version <= 10)) then
|
|
||||||
return RETERR-6
|
|
||||||
end
|
|
||||||
|
|
||||||
local msector, mwall, msprite = map.sector, map.wall, map.sprite
|
|
||||||
|
|
||||||
if (not alltabtab(msector) or not alltabtab(mwall) or not alltabtab(msprite)) then
|
|
||||||
return RETERR-7
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not tabofnumtabs(msector, sector_members) or
|
|
||||||
not tabofnumtabs(mwall, wall_members) or
|
|
||||||
not tabofnumtabs(msprite, sprite_members))
|
|
||||||
then
|
|
||||||
return RETERR-8
|
|
||||||
end
|
|
||||||
|
|
||||||
local numsectors, numwalls, numsprites = #msector, #mwall, #msprite
|
|
||||||
local sector, wall, sprite = ffiC.sector, ffiC.wall, ffiC.sprite
|
|
||||||
|
|
||||||
if (numsectors == 0 or numsectors > ffiC.MAXSECTORS or
|
|
||||||
numwalls > ffiC.MAXWALLS or numsprites > ffiC.MAXSPRITES)
|
|
||||||
then
|
|
||||||
return RETERR-9
|
|
||||||
end
|
|
||||||
|
|
||||||
--- From here on, start filling out C structures. ---
|
|
||||||
|
|
||||||
ffiC.numsectors = numsectors
|
|
||||||
ffiC.numwalls = numwalls
|
|
||||||
|
|
||||||
-- Header.
|
|
||||||
posptr.x = map.pos[1]
|
|
||||||
posptr.y = map.pos[2]
|
|
||||||
posptr.z = map.pos[3]
|
|
||||||
|
|
||||||
angptr[0] = map.ang
|
|
||||||
cursectnumptr[0] = map.sectnum
|
|
||||||
|
|
||||||
-- Sectors.
|
|
||||||
for i=0,numsectors-1 do
|
|
||||||
if (read_struct(sector[i], msector[i+1], sector_members, sector_default)) then
|
|
||||||
return RETERR-10
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Walls.
|
|
||||||
for i=0,numwalls-1 do
|
|
||||||
if (read_struct(wall[i], mwall[i+1], wall_members, wall_default)) then
|
|
||||||
return RETERR-11
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Sprites.
|
|
||||||
for i=0,numsprites-1 do
|
|
||||||
if (read_struct(sprite[i], msprite[i+1], sprite_members, sprite_default)) then
|
|
||||||
return RETERR-12
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- XXX: need to consistency-check much more here! Basically, all of
|
|
||||||
-- astub.c's CheckMapCorruption() for corruption level >=4?
|
|
||||||
-- See NOTNICE below.
|
|
||||||
|
|
||||||
--- Tweakery: mostly setting dependent members. ---
|
|
||||||
|
|
||||||
-- sector[]: .wallptr calculated from .wallnum.
|
|
||||||
local numw = 0
|
|
||||||
for i=0,numsectors-1 do
|
|
||||||
assert(numw >= 0 and numw < numwalls) -- NOTNICE, cheap check instead of real one.
|
|
||||||
sector[i].wallptr = numw
|
|
||||||
numw = numw + sector[i].wallnum
|
|
||||||
end
|
|
||||||
|
|
||||||
-- .point2 in {0, 1} --> wall index, sector[].wallptr/.wallnum
|
|
||||||
if (restore_point2(true)) then
|
|
||||||
return RETERR-13
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check .point2 at least.
|
|
||||||
if (check_bad_point2()) then
|
|
||||||
return RETERR-14
|
|
||||||
end
|
|
||||||
|
|
||||||
-- wall[]: .nextsector calculated by using engine's sectorofwall_noquick()
|
|
||||||
for i=0,numwalls-1 do
|
|
||||||
local nw = wall[i].nextwall
|
|
||||||
|
|
||||||
if (nw >= 0) then
|
|
||||||
assert(nw >= 0 and nw < numwalls) -- NOTNICE
|
|
||||||
wall[i].nextsector = ffiC.sectorofwall_noquick(nw)
|
|
||||||
else
|
|
||||||
wall[i].nextsector = -1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- All OK, return the number of sprites for further engine loading code.
|
|
||||||
return numsprites
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Register our Lua functions as callbacks from C.
|
|
||||||
ffiC.saveboard_maptext = saveboard_maptext
|
|
||||||
ffiC.loadboard_maptext = loadboard_maptext
|
|
|
@ -1,96 +0,0 @@
|
||||||
-- Virtual file system facilities for Lunatic
|
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local C = ffi.C
|
|
||||||
|
|
||||||
local decl = require("lprivate").decl
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local error = error
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
----------
|
|
||||||
|
|
||||||
decl[[
|
|
||||||
int32_t pathsearchmode;
|
|
||||||
|
|
||||||
typedef struct _CACHE1D_FIND_REC {
|
|
||||||
char *name;
|
|
||||||
int32_t type, source;
|
|
||||||
struct _CACHE1D_FIND_REC *next, *prev, *usera, *userb;
|
|
||||||
} CACHE1D_FIND_REC;
|
|
||||||
|
|
||||||
void klistfree(CACHE1D_FIND_REC *rec);
|
|
||||||
CACHE1D_FIND_REC *klistpath(const char *path, const char *mask, int32_t type);
|
|
||||||
]]
|
|
||||||
|
|
||||||
|
|
||||||
-- The API table
|
|
||||||
local fs = {}
|
|
||||||
|
|
||||||
local CACHE1D = ffi.new[[
|
|
||||||
struct {
|
|
||||||
static const int FIND_FILE = 1;
|
|
||||||
static const int FIND_DIR = 2;
|
|
||||||
static const int FIND_DRIVE = 4;
|
|
||||||
static const int FIND_NOCURDIR = 8;
|
|
||||||
|
|
||||||
static const int OPT_NOSTACK = 0x100;
|
|
||||||
|
|
||||||
// the lower the number, the higher the priority
|
|
||||||
static const int SOURCE_DRIVE = 0;
|
|
||||||
static const int SOURCE_CURDIR = 1;
|
|
||||||
static const int SOURCE_PATH = 2; // + path stack depth
|
|
||||||
static const int SOURCE_ZIP = 0x7ffffffe;
|
|
||||||
static const int SOURCE_GRP = 0x7fffffff;
|
|
||||||
}
|
|
||||||
]]
|
|
||||||
|
|
||||||
-- files = fs.listpath(path, mask)
|
|
||||||
-- TODO: filter by 'source' (path, zip and/or grp)
|
|
||||||
-- TODO: directories too, so that one can list them recursively, for example
|
|
||||||
function fs.listpath(path, mask)
|
|
||||||
if (type(path)~="string") then
|
|
||||||
-- TODO: maybe also allow nil
|
|
||||||
error("Invalid argument #1: must be a string", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (type(mask) ~= "string") then
|
|
||||||
error("Invalid argument #2: must be a string", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Normalization, for portability's sake
|
|
||||||
|
|
||||||
if (path:find("\\")) then
|
|
||||||
error("Invalid argument #1: must not contain backslashes", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (mask:find("[\\/]")) then
|
|
||||||
error("Invalid argument #2: must not contain back or forward slashes", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local opsm = C.pathsearchmode
|
|
||||||
C.pathsearchmode = 0
|
|
||||||
local rootrec = C.klistpath(path, mask, CACHE1D.FIND_FILE + CACHE1D.FIND_NOCURDIR)
|
|
||||||
C.pathsearchmode = opsm
|
|
||||||
|
|
||||||
if (rootrec == nil) then
|
|
||||||
-- XXX: may have failed hard or just no matching files
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
|
|
||||||
local files = {}
|
|
||||||
|
|
||||||
local rec = rootrec
|
|
||||||
while (rec ~= nil) do
|
|
||||||
assert(rec.name ~= nil)
|
|
||||||
files[#files+1] = ffi.string(rec.name)
|
|
||||||
rec = rec.next
|
|
||||||
end
|
|
||||||
|
|
||||||
C.klistfree(rootrec)
|
|
||||||
|
|
||||||
return files
|
|
||||||
end
|
|
||||||
|
|
||||||
return fs
|
|
|
@ -1,35 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
# S or G can be used to display SETGLOBALs or GETGLOBALs, respectively
|
|
||||||
echo "Usage: $0 [S|G] <files.lua...>"
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
|
|
||||||
GS=
|
|
||||||
|
|
||||||
if [ "$1" = G ]; then
|
|
||||||
GS=G
|
|
||||||
shift
|
|
||||||
elif [ "$1" = S ]; then
|
|
||||||
GS=S
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Lua 5.2 required to parse ::label:: / goto generated by LunaCON.
|
|
||||||
LUAC=luac
|
|
||||||
|
|
||||||
for f in "$@"; do
|
|
||||||
if [ ! -f "$f" ]; then
|
|
||||||
echo "$f: No such file"
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
echo "[$f]"
|
|
||||||
# Strip LuaJIT specific syntax first. Run luac and extract interesting lines.
|
|
||||||
sed -r -e "s/[0-9]+U?LL/0/g" "$f" | $LUAC -p -l - | grep "${GS}ETTABUP.*; _ENV " |
|
|
||||||
# Reformat them.
|
|
||||||
sed -e 's/.*\[\(.*\)\].*\([SG]ET\)TABUP.*"\(.*\)".*/\1: \2 \3/' |
|
|
||||||
# Mark where the new module environment starts.
|
|
||||||
sed -e 's/GET module/& ____________________/'
|
|
||||||
echo
|
|
||||||
done
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,210 +0,0 @@
|
||||||
|
|
||||||
-- Savegame facilities for Lunatic.
|
|
||||||
|
|
||||||
local string = require("string")
|
|
||||||
local table = require("table")
|
|
||||||
|
|
||||||
local format = string.format
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local getmetatable = getmetatable
|
|
||||||
local pairs = pairs
|
|
||||||
local setmetatable = setmetatable
|
|
||||||
local tostring = tostring
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
local cansave_cdata = require("lprivate").cansave_cdata
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
---=== Serialization, based on the idea from PiL ===---
|
|
||||||
|
|
||||||
assert(tostring(0/0)=="nan")
|
|
||||||
assert(tostring(1/0)=="inf")
|
|
||||||
|
|
||||||
-- Serialize a 'primitive' Lua value.
|
|
||||||
local function basicSerialize(o)
|
|
||||||
-- Compare with sb_get_initial_strbuf() below.
|
|
||||||
-- XXX: nil?
|
|
||||||
if (type(o) == "number") then
|
|
||||||
-- NOTE: NaN and infinity handled.
|
|
||||||
return tostring(o)
|
|
||||||
elseif (type(o) == "boolean") then
|
|
||||||
return o and "t" or "f"
|
|
||||||
elseif (type(o) == "string") then
|
|
||||||
-- TODO: return refname if it's shorter
|
|
||||||
return format("%q", o)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function isSerializeable(obj)
|
|
||||||
return (getmetatable(obj)=="serializeable" or cansave_cdata(obj))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- 'Save buffer' class. Objects of this class keep track of a string buffer
|
|
||||||
-- contiaining Lua code to recreate the values of particular variables from a
|
|
||||||
-- user Lunatic environment.
|
|
||||||
local savebuffer_mt = {
|
|
||||||
__index = {
|
|
||||||
-- Called on starting processing one module.
|
|
||||||
startmod = function(self, modname)
|
|
||||||
self:addrawf("if (N==%q) then", modname)
|
|
||||||
-- Will contain all tables and cdata refs of this module, indexed
|
|
||||||
-- by number.
|
|
||||||
self:addraw("local T={}")
|
|
||||||
self:resetRefs()
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Called on finishing processing one module.
|
|
||||||
endmod = function(self)
|
|
||||||
self:addraw("end")
|
|
||||||
end,
|
|
||||||
|
|
||||||
--== vvv INTERNAL vvv ===---
|
|
||||||
resetRefs = function(self)
|
|
||||||
self.numrefs = 0
|
|
||||||
self.recursionDepth = 0
|
|
||||||
self.val2ref = {}
|
|
||||||
self.havereq = {}
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Get the code necessary to create this object, usually 'require'ing a
|
|
||||||
-- module into a local variable.
|
|
||||||
getRequire = function(self, value)
|
|
||||||
local reqcode = value:_get_require()
|
|
||||||
if (self.havereq[reqcode] == nil) then
|
|
||||||
self.havereq[reqcode] = true
|
|
||||||
self.strbuf[#self.strbuf+1] = reqcode
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
emitT = function(self, refcode, valcode, obj)
|
|
||||||
self:addrawf("%s=%s", refcode, valcode)
|
|
||||||
self.val2ref[obj] = refcode
|
|
||||||
end,
|
|
||||||
|
|
||||||
emitAssign = function(self, lhscode, refcode)
|
|
||||||
self:addrawf("%s=%s", lhscode, refcode)
|
|
||||||
end,
|
|
||||||
--== ^^^ INTERNAL ^^^ ===---
|
|
||||||
|
|
||||||
-- Add an entry of Lua object <value> that can be referenced by
|
|
||||||
-- <lhscode> on the left hand side of an assignment.
|
|
||||||
-- <excludedKeys>: if non-nil, should be table
|
|
||||||
-- [<key_string>] = true. See EXCLUDE_KEYS below.
|
|
||||||
-- Returns 'true' if <value> cannot be serialized.
|
|
||||||
add = function(self, lhscode, value, excludedKeys)
|
|
||||||
local valcode = basicSerialize(value)
|
|
||||||
|
|
||||||
if (valcode == nil) then
|
|
||||||
-- <value> is a not a 'basic' Lua object, but one passed by
|
|
||||||
-- reference.
|
|
||||||
if (not self.val2ref[value]) then
|
|
||||||
-- Object is being serialized for the first time.
|
|
||||||
|
|
||||||
-- Create a slot in 'T' for this object by which we can
|
|
||||||
-- refer to it in the following.
|
|
||||||
self.numrefs = self.numrefs+1
|
|
||||||
local refcode = format("T[%d]", self.numrefs)
|
|
||||||
|
|
||||||
if (isSerializeable(value)) then
|
|
||||||
-- We have a serializeable object from Lunatic
|
|
||||||
-- (e.g. actorvar).
|
|
||||||
self:getRequire(value)
|
|
||||||
local restoreCode = value:_serialize()
|
|
||||||
if (restoreCode == nil) then -- XXX: check UNUSED?
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
self:emitT(refcode, restoreCode, value)
|
|
||||||
valcode = refcode
|
|
||||||
|
|
||||||
elseif (type(value)=="table") then
|
|
||||||
-- Create a new table for this gamevar.
|
|
||||||
-- TODO: emit table initializations where possible.
|
|
||||||
self:emitT(refcode, "{}", value)
|
|
||||||
|
|
||||||
for k,v in pairs(value) do
|
|
||||||
local keystr = basicSerialize(k)
|
|
||||||
if (keystr == nil) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- EXCLUDE_KEYS
|
|
||||||
local isNORESET = (self.recursionDepth == 0 and
|
|
||||||
excludedKeys and excludedKeys[k])
|
|
||||||
|
|
||||||
-- Generate the name under which the table element
|
|
||||||
-- is referenced.
|
|
||||||
local refcode2 = format("%s[%s]", refcode, keystr)
|
|
||||||
|
|
||||||
if (isNORESET) then
|
|
||||||
self:emitAssign(refcode2, format("M._V[%s]", keystr))
|
|
||||||
else
|
|
||||||
-- Recurse!
|
|
||||||
self.recursionDepth = self.recursionDepth+1
|
|
||||||
local ret = self:add(refcode2, v)
|
|
||||||
self.recursionDepth = self.recursionDepth-1
|
|
||||||
|
|
||||||
if (ret) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
valcode = refcode
|
|
||||||
else
|
|
||||||
-- We have anything else: can't serialize.
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Object was previously serialized, get Lua expression it
|
|
||||||
-- can be referenced with.
|
|
||||||
valcode = self.val2ref[value]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
self:emitAssign(lhscode, valcode)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Add a single string to the buffer.
|
|
||||||
addraw = function(self, str)
|
|
||||||
self.strbuf[#self.strbuf+1] = str
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Add a single formatted string to the buffer.
|
|
||||||
addrawf = function(self, fmt, ...)
|
|
||||||
self:addraw(format(fmt, ...))
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Get the Lua code recreating the values as a string.
|
|
||||||
getcode = function(self)
|
|
||||||
self.strbuf[#self.strbuf+1] = "" -- add newline at EOS
|
|
||||||
return table.concat(self.strbuf, "\n")
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
local function sb_get_initial_strbuf()
|
|
||||||
return {
|
|
||||||
"local nan, inf = 0/0, 1/0",
|
|
||||||
"local t, f = true, false",
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create a new savebuffer object.
|
|
||||||
function savebuffer()
|
|
||||||
-- .numrefs: how many table or cdata objects we have serialized
|
|
||||||
-- .val2ref: [<Lua object>] = <Lua code string>
|
|
||||||
-- .havereq = [<string>] = true
|
|
||||||
-- .strbuf: array of Lua code pieces
|
|
||||||
local sb = {
|
|
||||||
numrefs=0, val2ref={}, havereq={},
|
|
||||||
recursionDepth = 0,
|
|
||||||
strbuf=sb_get_initial_strbuf()
|
|
||||||
}
|
|
||||||
|
|
||||||
return setmetatable(sb, savebuffer_mt)
|
|
||||||
end
|
|
|
@ -1,20 +0,0 @@
|
||||||
/* The Lunatic Interpreter, part of EDuke32. Editor stuff. */
|
|
||||||
|
|
||||||
#ifdef USE_LUAJIT_2_1
|
|
||||||
# include <luajit-2.1/lualib.h>
|
|
||||||
#else
|
|
||||||
# include <luajit-2.0/lualib.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "lunatic_editor.h"
|
|
||||||
|
|
||||||
|
|
||||||
int Em_CreateState(L_State *estate)
|
|
||||||
{
|
|
||||||
return L_CreateState(estate, "m32", NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Em_DestroyState(L_State *estate)
|
|
||||||
{
|
|
||||||
L_DestroyState(estate);
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
/* The Lunatic Interpreter, part of EDuke32. Editor stuff. */
|
|
||||||
|
|
||||||
#ifndef EDUKE32_LUNATIC_M32_H_
|
|
||||||
#define EDUKE32_LUNATIC_M32_H_
|
|
||||||
|
|
||||||
#include "lunatic.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int Em_CreateState(L_State *estate);
|
|
||||||
void Em_DestroyState(L_State *estate);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,661 +0,0 @@
|
||||||
/* The Lunatic Interpreter, part of EDuke32. Game-side stuff. */
|
|
||||||
|
|
||||||
#include "compat.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_LUAJIT_2_1
|
|
||||||
# include <luajit-2.1/lualib.h>
|
|
||||||
# include <luajit-2.1/lauxlib.h>
|
|
||||||
#else
|
|
||||||
# include <luajit-2.0/lualib.h>
|
|
||||||
# include <luajit-2.0/lauxlib.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "build.h" // printext256
|
|
||||||
|
|
||||||
#include "lunatic_game.h"
|
|
||||||
|
|
||||||
#include "osd.h"
|
|
||||||
#include "gamedef.h" // EventNames[]
|
|
||||||
|
|
||||||
|
|
||||||
L_State g_ElState;
|
|
||||||
|
|
||||||
|
|
||||||
// this serves two purposes:
|
|
||||||
// the values as booleans and the addresses as keys to the Lua registry
|
|
||||||
uint8_t g_elEvents[MAXEVENTS];
|
|
||||||
|
|
||||||
// same thing for actors:
|
|
||||||
el_actor_t g_elActors[MAXTILES];
|
|
||||||
|
|
||||||
// Session variable. Never restored except by 'readgamevar'.
|
|
||||||
int32_t g_elSessionVar[8]; // MAXSESSIONVARS, KEEPINSYNC con_lang.lua
|
|
||||||
|
|
||||||
// Set to 1 on error in event.
|
|
||||||
int32_t g_elEventError;
|
|
||||||
|
|
||||||
// Will be set to 0 after the first time that user Lua modules are run.
|
|
||||||
int32_t g_elFirstTime = 1;
|
|
||||||
|
|
||||||
int32_t g_elCallDepth = 0;
|
|
||||||
int32_t g_RETURN;
|
|
||||||
|
|
||||||
// for timing events and actors
|
|
||||||
static int32_t g_timingInited = 0;
|
|
||||||
|
|
||||||
// Used as Lua registry key to the tweak_traceback_msg() function, set to 1 if
|
|
||||||
// such a function has been registered.
|
|
||||||
static uint8_t g_tweakTracebackMsg = 0;
|
|
||||||
|
|
||||||
// forward-decls...
|
|
||||||
static int32_t SetEvent_CF(lua_State *L);
|
|
||||||
static int32_t SetActor_CF(lua_State *L);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// in lpeg.o
|
|
||||||
extern int luaopen_lpeg(lua_State *L);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// See: Good Practice in (Pseudo) Random Number Generation for
|
|
||||||
// Bioinformatics Applications, by David Jones
|
|
||||||
ATTRIBUTE_OPTIMIZE("O2")
|
|
||||||
LUNATIC_EXTERN uint32_t rand_jkiss_u32(rng_jkiss_t *s)
|
|
||||||
{
|
|
||||||
uint64_t t;
|
|
||||||
s->x = 314527869 * s->x + 1234567;
|
|
||||||
s->y ^= s->y << 5; s->y ^= s->y >> 7; s->y ^= s->y << 22;
|
|
||||||
t = 4294584393ULL * s->z + s->c; s->c = t >> 32; s->z = t;
|
|
||||||
return s->x + s->y + s->z;
|
|
||||||
}
|
|
||||||
|
|
||||||
ATTRIBUTE_OPTIMIZE("O2")
|
|
||||||
LUNATIC_EXTERN double rand_jkiss_dbl(rng_jkiss_t *s)
|
|
||||||
{
|
|
||||||
double x;
|
|
||||||
unsigned int a, b;
|
|
||||||
a = rand_jkiss_u32(s) >> 6; /* Upper 26 bits */
|
|
||||||
b = rand_jkiss_u32(s) >> 5; /* Upper 27 bits */
|
|
||||||
x = (a * 134217728.0 + b) / 9007199254740992.0;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void El_PrintTimes(void)
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
const char nn = Bstrlen("EVENT_");
|
|
||||||
|
|
||||||
// Try environment variable specifying the base name (sans ".actors.csv" or
|
|
||||||
// ".events.csv") for a CSV file to output, for further processing in e.g.
|
|
||||||
// GSL shell: http://www.nongnu.org/gsl-shell/
|
|
||||||
const char *basefn = getenv("LUNATIC_TIMING_BASEFN");
|
|
||||||
|
|
||||||
if (basefn != NULL)
|
|
||||||
{
|
|
||||||
const int32_t baselen = Bstrlen(basefn);
|
|
||||||
const int32_t addnlen = Bstrlen(".actors.csv"); // MUST equal that of ".events.csv"
|
|
||||||
|
|
||||||
char *fullfn = (char *)Xmalloc(baselen + addnlen + 1);
|
|
||||||
BFILE *outf;
|
|
||||||
|
|
||||||
if (fullfn == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Bmemcpy(fullfn, basefn, baselen);
|
|
||||||
|
|
||||||
// EVENTS
|
|
||||||
Bmemcpy(fullfn+baselen, ".events.csv", addnlen+1);
|
|
||||||
outf = Bfopen(fullfn, "w");
|
|
||||||
if (outf == NULL)
|
|
||||||
{
|
|
||||||
OSD_Printf("Couldn't open \"%s\" for writing timing data: %s", fullfn, strerror(errno));
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bfprintf(outf, "evtname,numcalls,total_ms,mean_us\n"); // times in usecs are per-call
|
|
||||||
for (i=0; i<MAXEVENTS; i++)
|
|
||||||
if (g_eventCalls[i])
|
|
||||||
Bfprintf(outf, "%s,%d,%f,%f\n", EventNames[i]+nn, g_eventCalls[i], g_eventTotalMs[i],
|
|
||||||
1000*g_eventTotalMs[i]/g_eventCalls[i]);
|
|
||||||
Bfclose(outf);
|
|
||||||
|
|
||||||
// ACTORS
|
|
||||||
Bmemcpy(fullfn+baselen, ".actors.csv", addnlen+1);
|
|
||||||
outf = Bfopen(fullfn, "w");
|
|
||||||
if (outf == NULL)
|
|
||||||
{
|
|
||||||
OSD_Printf("Couldn't open \"%s\" for writing timing data: %s", fullfn, strerror(errno));
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bfprintf(outf, "tilenum,numcalls,total_ms,min_us,mean_us,max_us\n");
|
|
||||||
for (i=0; i<MAXTILES; i++)
|
|
||||||
if (g_actorCalls[i])
|
|
||||||
Bfprintf(outf, "%d,%d,%f,%f,%f,%f\n", i, g_actorCalls[i], g_actorTotalMs[i],
|
|
||||||
1000*g_actorMinMs[i],
|
|
||||||
1000*g_actorTotalMs[i]/g_actorCalls[i],
|
|
||||||
1000*g_actorMaxMs[i]);
|
|
||||||
Bfclose(outf);
|
|
||||||
|
|
||||||
OSD_Printf("Wrote timing data to \"%s.*.csv\"\n", basefn);
|
|
||||||
finish:
|
|
||||||
Bfree(fullfn);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If not writing out CSV files, print timing data to log instead.
|
|
||||||
|
|
||||||
char buf[32];
|
|
||||||
int32_t maxlen = 0;
|
|
||||||
int32_t haveev=0, haveac=0;
|
|
||||||
|
|
||||||
for (i=0; i<MAXEVENTS; i++)
|
|
||||||
{
|
|
||||||
int32_t len = Bstrlen(EventNames[i]+nn);
|
|
||||||
Bassert(len < (int32_t)sizeof(buf));
|
|
||||||
maxlen = max(len, maxlen);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0; i<MAXEVENTS; i++)
|
|
||||||
if (g_eventCalls[i])
|
|
||||||
{
|
|
||||||
int32_t n=Bsprintf(buf, "%s", EventNames[i]+nn);
|
|
||||||
|
|
||||||
if (!haveev)
|
|
||||||
{
|
|
||||||
haveev = 1;
|
|
||||||
OSD_Printf("\n -- event times: [event]={ total calls, total time [ms], mean time/call [us] }\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; n<maxlen; n++)
|
|
||||||
buf[n] = ' ';
|
|
||||||
buf[maxlen] = 0;
|
|
||||||
|
|
||||||
OSD_Printf(" [%s]={ %8d, %10.3f, %10.3f },\n",
|
|
||||||
buf, g_eventCalls[i], g_eventTotalMs[i],
|
|
||||||
1000*g_eventTotalMs[i]/g_eventCalls[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i=0; i<MAXTILES; i++)
|
|
||||||
if (g_actorCalls[i])
|
|
||||||
{
|
|
||||||
if (!haveac)
|
|
||||||
{
|
|
||||||
haveac = 1;
|
|
||||||
OSD_Printf("\n -- actor times: [tile]={ total calls, total time [ms], {min,mean,max} time/call [us] }\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
OSD_Printf(" [%5d]={ %8d, %9.3f, %9.3f, %9.3f, %9.3f },\n",
|
|
||||||
i, g_actorCalls[i], g_actorTotalMs[i],
|
|
||||||
1000*g_actorMinMs[i],
|
|
||||||
1000*g_actorTotalMs[i]/g_actorCalls[i],
|
|
||||||
1000*g_actorMaxMs[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////// ERROR REPORTING //////////
|
|
||||||
#define EL_MAXERRORS 20
|
|
||||||
static int32_t el_numErrors=0, el_tooMuchErrors;
|
|
||||||
static char *el_errorMsgs[EL_MAXERRORS];
|
|
||||||
int8_t el_addNewErrors = 1; // add new errors to display?
|
|
||||||
|
|
||||||
// Compare against all other error messages.
|
|
||||||
// Strictly seen, this is quadratic-time, but EL_MAXERRORS is small and
|
|
||||||
// errors should be fixed anyway.
|
|
||||||
static int32_t cmp_against_others(const char *str, int32_t slen)
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
for (i=0; i<el_numErrors; i++)
|
|
||||||
if (!Bstrncmp(str, el_errorMsgs[i], slen))
|
|
||||||
return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
LUNATIC_EXTERN void El_OnError(const char *str)
|
|
||||||
{
|
|
||||||
if (el_addNewErrors && !el_tooMuchErrors)
|
|
||||||
{
|
|
||||||
char *errstr = NULL;
|
|
||||||
const char *nl = Bstrchr(str, '\n');
|
|
||||||
|
|
||||||
// First, check whether the error message matches an already saved one
|
|
||||||
if (nl)
|
|
||||||
{
|
|
||||||
// cut off string after the newline
|
|
||||||
if (cmp_against_others(str, nl-str))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// save string fully
|
|
||||||
if (cmp_against_others(str, Bstrlen(str)))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the (EL_MAXERRORS+1)'th distinct error appeared, we have too many.
|
|
||||||
if (el_numErrors==EL_MAXERRORS)
|
|
||||||
{
|
|
||||||
el_tooMuchErrors = 1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, allocate storage for the potentially clipped error string...
|
|
||||||
if (nl)
|
|
||||||
{
|
|
||||||
errstr = (char *)Xmalloc(nl-str+1);
|
|
||||||
Bmemcpy(errstr, str, nl-str);
|
|
||||||
errstr[nl-str] = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
errstr = Xstrdup(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ...and save it:
|
|
||||||
el_errorMsgs[el_numErrors++] = errstr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void El_ClearErrors(void)
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
for (i=0; i<EL_MAXERRORS; i++)
|
|
||||||
DO_FREE_AND_NULL(el_errorMsgs[i]);
|
|
||||||
el_numErrors = el_tooMuchErrors = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void El_DisplayErrors(void)
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
for (i=0; i<el_numErrors; i++)
|
|
||||||
printext256(8, 8+8*i, 242, 0, el_errorMsgs[i], 0);
|
|
||||||
if (el_tooMuchErrors)
|
|
||||||
printext256(8, 8+8*EL_MAXERRORS, 242, 0, "(more distinct errors ...)", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////// STATE CREATION/DESTRUCTIION //////////
|
|
||||||
|
|
||||||
static int our_traceback_CF(lua_State *L)
|
|
||||||
{
|
|
||||||
Bassert(lua_gettop(L)==1);
|
|
||||||
|
|
||||||
if (lua_type(L, 1)==LUA_TBOOLEAN)
|
|
||||||
{
|
|
||||||
lua_pushvalue(L, 1); // duplicate it
|
|
||||||
return 1; // and tell Lua to return it
|
|
||||||
}
|
|
||||||
|
|
||||||
Bassert(lua_type(L, 1)==LUA_TSTRING);
|
|
||||||
|
|
||||||
// call debug.traceback with the string
|
|
||||||
L_PushDebugTraceback(L);
|
|
||||||
lua_pushvalue(L, 1);
|
|
||||||
lua_call(L, 1, 1);
|
|
||||||
Bassert(lua_gettop(L)==2); // Lua will pop off args
|
|
||||||
|
|
||||||
if (g_tweakTracebackMsg)
|
|
||||||
{
|
|
||||||
// Get tweak_traceback_msg() onto the stack.
|
|
||||||
lua_pushlightuserdata(L, &g_tweakTracebackMsg);
|
|
||||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
|
||||||
|
|
||||||
lua_pushvalue(L, -2); // push copy of error message string
|
|
||||||
Bassert(lua_type(L, -1)==LUA_TSTRING);
|
|
||||||
|
|
||||||
// Call tweak_traceback_msg(). CAREFUL, it's unprotected!
|
|
||||||
lua_call(L, 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Registers a function: str = tweak_traceback_msg(str)
|
|
||||||
static int32_t SetTweakTracebackMsg_CF(lua_State *L)
|
|
||||||
{
|
|
||||||
Bassert(lua_gettop(L)==1);
|
|
||||||
L_CheckAndRegisterFunction(L, &g_tweakTracebackMsg);
|
|
||||||
g_tweakTracebackMsg = 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////// Lua C-API interfaces for C game functions that may call events.
|
|
||||||
// http://www.freelists.org/post/luajit/intermitten-lua-pcall-crash-on-x86-64-linux,1
|
|
||||||
|
|
||||||
// Some of these are duplicate declarations:
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
extern void P_AddWeaponMaybeSwitchI(int32_t snum, int32_t weap);
|
|
||||||
void P_CheckWeaponI(int playerNum);
|
|
||||||
extern int32_t A_ShootWithZvel(int32_t i, int32_t atwith, int32_t override_zvel);
|
|
||||||
extern int A_Spawn(int j, int pn);
|
|
||||||
extern void VM_FallSprite(int32_t i);
|
|
||||||
extern int32_t VM_ResetPlayer2(int32_t snum, int32_t flags);
|
|
||||||
extern void A_RadiusDamage(int32_t i, int32_t r, int32_t, int32_t, int32_t, int32_t);
|
|
||||||
extern void G_OperateSectors(int sectNum, int spriteNum);
|
|
||||||
extern void G_OperateActivators(int32_t low,int32_t snum);
|
|
||||||
int32_t A_InsertSprite(int16_t whatsect,int32_t s_x,int32_t s_y,int32_t s_z,int16_t s_pn,int8_t s_s,
|
|
||||||
uint8_t s_xr,uint8_t s_yr,int16_t s_a,int16_t s_ve,int16_t s_zv,int16_t s_ow,int16_t s_ss);
|
|
||||||
extern void A_AddToDeleteQueue(int32_t i);
|
|
||||||
extern int32_t A_PlaySound(uint32_t num, int32_t i);
|
|
||||||
void A_DeleteSprite(int spriteNum);
|
|
||||||
extern void G_ShowView(vec3_t vec, int32_t a, int32_t horiz, int32_t sect,
|
|
||||||
int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t unbiasedp);
|
|
||||||
extern void G_GameExit(const char *msg);
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void G_ShowViewXYZ(int32_t x, int32_t y, int32_t z, int32_t a, int32_t horiz, int32_t sect,
|
|
||||||
int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t unbiasedp)
|
|
||||||
{
|
|
||||||
vec3_t vec = { x, y, z };
|
|
||||||
G_ShowView(vec, a, horiz, sect, x1, y1, x2, y2, unbiasedp);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LARG(index) lua_tointeger(L, index)
|
|
||||||
|
|
||||||
#define ONE_ARG LARG(1)
|
|
||||||
#define TWO_ARGS LARG(1), LARG(2)
|
|
||||||
#define THREE_ARGS LARG(1), LARG(2), LARG(3)
|
|
||||||
|
|
||||||
#define CALL_WITH_RET(Name, ...) \
|
|
||||||
int32_t ret = Name(__VA_ARGS__); \
|
|
||||||
lua_pushinteger(L, ret); \
|
|
||||||
return 1
|
|
||||||
|
|
||||||
#define CALL_WITHOUT_RET(Name, ...) \
|
|
||||||
Name(__VA_ARGS__); \
|
|
||||||
return 0
|
|
||||||
|
|
||||||
#define DEFINE_RET_CFUNC(Name, ...) \
|
|
||||||
static int32_t Name##_CF(lua_State *L) \
|
|
||||||
{ \
|
|
||||||
CALL_WITH_RET(Name, __VA_ARGS__); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define DEFINE_VOID_CFUNC(Name, ...) \
|
|
||||||
static int32_t Name##_CF(lua_State *L) \
|
|
||||||
{ \
|
|
||||||
CALL_WITHOUT_RET(Name, __VA_ARGS__); \
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: player struct -> player index -> player struct ugliness because
|
|
||||||
// pointers to FFI cdata apparently can't be reliably passed via lua_getpointer().
|
|
||||||
// Not to mention that lua_getpointer() returns _const_ void*.
|
|
||||||
DEFINE_VOID_CFUNC(P_AddWeaponMaybeSwitchI, TWO_ARGS)
|
|
||||||
DEFINE_VOID_CFUNC(P_CheckWeaponI, ONE_ARG)
|
|
||||||
DEFINE_RET_CFUNC(A_ShootWithZvel, THREE_ARGS)
|
|
||||||
DEFINE_RET_CFUNC(A_Spawn, TWO_ARGS)
|
|
||||||
DEFINE_VOID_CFUNC(VM_FallSprite, ONE_ARG)
|
|
||||||
DEFINE_RET_CFUNC(VM_ResetPlayer2, TWO_ARGS)
|
|
||||||
DEFINE_VOID_CFUNC(A_RadiusDamage, LARG(1), LARG(2), LARG(3), LARG(4), LARG(5), LARG(6))
|
|
||||||
DEFINE_VOID_CFUNC(G_OperateSectors, TWO_ARGS)
|
|
||||||
DEFINE_VOID_CFUNC(G_OperateActivators, TWO_ARGS)
|
|
||||||
DEFINE_RET_CFUNC(A_InsertSprite, LARG(1), LARG(2), LARG(3), LARG(4), LARG(5), LARG(6),
|
|
||||||
LARG(7), LARG(8), LARG(9), LARG(10), LARG(11), LARG(12), LARG(13))
|
|
||||||
DEFINE_VOID_CFUNC(A_AddToDeleteQueue, ONE_ARG)
|
|
||||||
DEFINE_RET_CFUNC(A_PlaySound, TWO_ARGS)
|
|
||||||
DEFINE_VOID_CFUNC(A_DeleteSprite, ONE_ARG)
|
|
||||||
DEFINE_VOID_CFUNC(G_ShowViewXYZ, LARG(1), LARG(2), LARG(3), LARG(4), LARG(5), LARG(6),
|
|
||||||
LARG(7), LARG(8), LARG(9), LARG(10), LARG(11))
|
|
||||||
|
|
||||||
#define CFUNC_REG(Name) { #Name, Name##_CF }
|
|
||||||
|
|
||||||
static struct { const char *name; lua_CFunction func; } cfuncs[] =
|
|
||||||
{
|
|
||||||
CFUNC_REG(P_AddWeaponMaybeSwitchI),
|
|
||||||
CFUNC_REG(P_CheckWeaponI),
|
|
||||||
CFUNC_REG(A_ShootWithZvel),
|
|
||||||
CFUNC_REG(A_Spawn),
|
|
||||||
CFUNC_REG(VM_FallSprite),
|
|
||||||
CFUNC_REG(VM_ResetPlayer2),
|
|
||||||
CFUNC_REG(A_RadiusDamage),
|
|
||||||
CFUNC_REG(G_OperateSectors),
|
|
||||||
CFUNC_REG(G_OperateActivators),
|
|
||||||
CFUNC_REG(A_InsertSprite),
|
|
||||||
CFUNC_REG(A_Spawn),
|
|
||||||
CFUNC_REG(A_AddToDeleteQueue),
|
|
||||||
CFUNC_REG(A_PlaySound),
|
|
||||||
CFUNC_REG(A_DeleteSprite),
|
|
||||||
CFUNC_REG(G_ShowViewXYZ),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Creates a global table "CF" containing the functions from cfuncs[].
|
|
||||||
static void El_PushCFunctions(lua_State *L)
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
|
|
||||||
lua_newtable(L);
|
|
||||||
|
|
||||||
for (i=0; i<(signed)sizeof(cfuncs)/(signed)sizeof(cfuncs[0]); i++)
|
|
||||||
{
|
|
||||||
lua_pushstring(L, cfuncs[i].name);
|
|
||||||
lua_pushcfunction(L, cfuncs[i].func);
|
|
||||||
lua_settable(L, -3);
|
|
||||||
}
|
|
||||||
|
|
||||||
lua_setglobal(L, "CF");
|
|
||||||
}
|
|
||||||
|
|
||||||
//////
|
|
||||||
|
|
||||||
LUNATIC_CB int32_t (*El_RestoreGamevars)(const char *savecode);
|
|
||||||
|
|
||||||
static void El_StateSetup(lua_State *L)
|
|
||||||
{
|
|
||||||
luaopen_lpeg(L);
|
|
||||||
lua_pop(L, lua_gettop(L)); // pop off whatever lpeg leaves on the stack
|
|
||||||
|
|
||||||
// create misc. global functions in the Lua state
|
|
||||||
lua_pushcfunction(L, SetEvent_CF);
|
|
||||||
lua_setglobal(L, "gameevent_internal");
|
|
||||||
lua_pushcfunction(L, SetActor_CF);
|
|
||||||
lua_setglobal(L, "gameactor_internal");
|
|
||||||
lua_pushcfunction(L, SetTweakTracebackMsg_CF);
|
|
||||||
lua_setglobal(L, "set_tweak_traceback_internal");
|
|
||||||
|
|
||||||
El_PushCFunctions(L);
|
|
||||||
|
|
||||||
Bassert(lua_gettop(L)==0);
|
|
||||||
|
|
||||||
// This is for engine-side Lua:
|
|
||||||
lua_pushcfunction(L, &our_traceback_CF);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void El_OnOutOfMem(void)
|
|
||||||
{
|
|
||||||
G_GameExit("Out of memory in Lunatic.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0: success, <0: failure
|
|
||||||
int32_t El_CreateState(L_State *estate, const char *name)
|
|
||||||
{
|
|
||||||
int32_t i;
|
|
||||||
|
|
||||||
if (!g_timingInited)
|
|
||||||
{
|
|
||||||
g_timingInited = 1;
|
|
||||||
for (i=0; i<MAXTILES; i++)
|
|
||||||
g_actorMinMs[i] = 1e308;
|
|
||||||
}
|
|
||||||
|
|
||||||
L_ErrorFunc = El_OnError;
|
|
||||||
L_OutOfMemFunc = El_OnOutOfMem;
|
|
||||||
|
|
||||||
return L_CreateState(estate, name, &El_StateSetup);
|
|
||||||
}
|
|
||||||
|
|
||||||
void El_DestroyState(L_State *estate)
|
|
||||||
{
|
|
||||||
L_DestroyState(estate);
|
|
||||||
|
|
||||||
g_tweakTracebackMsg = 0;
|
|
||||||
|
|
||||||
// XXX: It would be cleaner to also clear stuff like g_elEvents[], but
|
|
||||||
// currently, when the game Lua state is recreated, the array should have
|
|
||||||
// the same values as before, so we're skipping that for now.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////// Lua_CFunctions //////////
|
|
||||||
|
|
||||||
// gameevent(EVENT_..., lua_function)
|
|
||||||
static int32_t SetEvent_CF(lua_State *L)
|
|
||||||
{
|
|
||||||
int32_t eventidx;
|
|
||||||
|
|
||||||
Bassert(lua_gettop(L) == 2);
|
|
||||||
eventidx = luaL_checkint(L, 1);
|
|
||||||
Bassert((unsigned)eventidx < MAXEVENTS);
|
|
||||||
|
|
||||||
L_CheckAndRegisterFunction(L, &g_elEvents[eventidx]);
|
|
||||||
g_elEvents[eventidx] = 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// gameactor(actortile, strength, act, mov, movflags, lua_function)
|
|
||||||
static int32_t SetActor_CF(lua_State *L)
|
|
||||||
{
|
|
||||||
int32_t actortile;
|
|
||||||
el_actor_t *a;
|
|
||||||
|
|
||||||
Bassert(lua_gettop(L) == 6);
|
|
||||||
|
|
||||||
actortile = luaL_checkint(L, 1);
|
|
||||||
Bassert((unsigned)actortile < MAXTILES);
|
|
||||||
|
|
||||||
a = &g_elActors[actortile];
|
|
||||||
L_CheckAndRegisterFunction(L, a);
|
|
||||||
|
|
||||||
// Set actor properties. They can only be nil if we're chaining actor code.
|
|
||||||
|
|
||||||
if (!lua_isnil(L, 2))
|
|
||||||
a->strength = luaL_checkint(L, 2);
|
|
||||||
if (!lua_isnil(L, 5))
|
|
||||||
a->movflags = luaL_checkint(L, 5);
|
|
||||||
|
|
||||||
if (!lua_isnil(L, 3))
|
|
||||||
Bmemcpy(&a->act, lua_topointer(L, 3), sizeof(con_action_t));
|
|
||||||
if (!lua_isnil(L, 4))
|
|
||||||
Bmemcpy(&a->mov, lua_topointer(L, 4), sizeof(con_move_t));
|
|
||||||
|
|
||||||
a->haveit = 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////
|
|
||||||
|
|
||||||
static int32_t call_regd_function3(lua_State *L, void *keyaddr,
|
|
||||||
int32_t spriteNum, int32_t playerNum, int32_t lDist)
|
|
||||||
{
|
|
||||||
#if !defined NDEBUG
|
|
||||||
const int32_t top = lua_gettop(L);
|
|
||||||
#endif
|
|
||||||
lua_pushcfunction(L, &our_traceback_CF);
|
|
||||||
|
|
||||||
// get the Lua function from the registry
|
|
||||||
lua_pushlightuserdata(L, keyaddr);
|
|
||||||
lua_gettable(L, LUA_REGISTRYINDEX);
|
|
||||||
|
|
||||||
lua_pushinteger(L, spriteNum);
|
|
||||||
lua_pushinteger(L, playerNum);
|
|
||||||
lua_pushinteger(L, lDist);
|
|
||||||
|
|
||||||
// -- call it! --
|
|
||||||
{
|
|
||||||
const int32_t i = lua_pcall(L, 3, 0, -5);
|
|
||||||
const int32_t haveerr = (i != 0);
|
|
||||||
|
|
||||||
Bassert(lua_iscfunction(L, -1-haveerr));
|
|
||||||
lua_remove(L, -1-haveerr);
|
|
||||||
|
|
||||||
Bassert(lua_gettop(L) == top+haveerr);
|
|
||||||
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t g_eventIdx = 0;
|
|
||||||
static void El_EventErrorPrint(const char *errmsg)
|
|
||||||
{
|
|
||||||
OSD_Printf(OSD_ERROR "event \"%s\" runtime error: %s\n",
|
|
||||||
EventNames[g_eventIdx], errmsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t El_CallEvent(L_State *estate, int32_t eventidx, int32_t spriteNum, int32_t playerNum, int32_t lDist, int32_t *iReturn)
|
|
||||||
{
|
|
||||||
// XXX: estate must be the one where the events were registered...
|
|
||||||
// make a global?
|
|
||||||
|
|
||||||
lua_State *const L = estate->L;
|
|
||||||
int32_t i;
|
|
||||||
|
|
||||||
const int32_t o_RETURN = g_RETURN;
|
|
||||||
g_RETURN = *iReturn;
|
|
||||||
|
|
||||||
g_elCallDepth++;
|
|
||||||
i = call_regd_function3(L, &g_elEvents[eventidx], spriteNum, playerNum, lDist);
|
|
||||||
g_elCallDepth--;
|
|
||||||
|
|
||||||
*iReturn = g_RETURN;
|
|
||||||
g_RETURN = o_RETURN;
|
|
||||||
|
|
||||||
if (i != 0)
|
|
||||||
{
|
|
||||||
g_elEventError = 1;
|
|
||||||
g_eventIdx = eventidx;
|
|
||||||
return L_HandleError(L, i, &El_EventErrorPrint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t g_actorTile, g_iActor;
|
|
||||||
static void El_ActorErrorPrint(const char *errmsg)
|
|
||||||
{
|
|
||||||
OSD_Printf(OSD_ERROR "actor %d (sprite %d) runtime error: %s\n",
|
|
||||||
g_actorTile, g_iActor, errmsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t El_CallActor(L_State *estate, int32_t actortile, int32_t spriteNum, int32_t playerNum, int32_t lDist)
|
|
||||||
{
|
|
||||||
lua_State *const L = estate->L;
|
|
||||||
int32_t i;
|
|
||||||
|
|
||||||
g_elCallDepth++;
|
|
||||||
i = call_regd_function3(L, &g_elActors[actortile], spriteNum, playerNum, lDist);
|
|
||||||
g_elCallDepth--;
|
|
||||||
|
|
||||||
if (i != 0)
|
|
||||||
{
|
|
||||||
g_actorTile = actortile;
|
|
||||||
g_iActor = spriteNum;
|
|
||||||
return L_HandleError(L, i, &El_ActorErrorPrint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
/* The Lunatic Interpreter, part of EDuke32. Game-side stuff. */
|
|
||||||
|
|
||||||
#ifndef EDUKE32_LUNATIC_H_
|
|
||||||
#define EDUKE32_LUNATIC_H_
|
|
||||||
|
|
||||||
#include "lunatic.h"
|
|
||||||
#include "events_defs.h" // MAXEVENTS
|
|
||||||
#include "actors.h" // con_move_t, con_action_t
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern L_State g_ElState;
|
|
||||||
|
|
||||||
// actor initialization data
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
con_move_t mov;
|
|
||||||
int16_t movflags;
|
|
||||||
int16_t strength;
|
|
||||||
con_action_t act;
|
|
||||||
uint8_t haveit; // shouldn't be used directly
|
|
||||||
} el_actor_t;
|
|
||||||
|
|
||||||
extern uint8_t g_elEvents[MAXEVENTS]; // shouldn't be used directly
|
|
||||||
extern el_actor_t g_elActors[MAXTILES];
|
|
||||||
|
|
||||||
extern int32_t g_elEventError;
|
|
||||||
|
|
||||||
// -- functions --
|
|
||||||
void El_PrintTimes(void);
|
|
||||||
|
|
||||||
void El_ClearErrors(void);
|
|
||||||
void El_DisplayErrors(void);
|
|
||||||
|
|
||||||
int32_t El_CreateState(L_State *estate, const char *name);
|
|
||||||
void El_DestroyState(L_State *estate);
|
|
||||||
|
|
||||||
int32_t El_CallEvent(L_State *estate, int32_t eventidx, int32_t spriteNum, int32_t playerNum, int32_t lDist, int32_t *iReturn);
|
|
||||||
int32_t El_CallActor(L_State *estate, int32_t actortile, int32_t spriteNum, int32_t playerNum, int32_t lDist);
|
|
||||||
|
|
||||||
extern int8_t el_addNewErrors; // add new errors to display?
|
|
||||||
void El_OnError(const char *str);
|
|
||||||
|
|
||||||
extern int32_t (*El_RestoreGamevars)(const char *savecode);
|
|
||||||
|
|
||||||
static inline int32_t El_HaveEvent(int32_t eventidx) { return g_elEvents[eventidx]!=0; }
|
|
||||||
static inline int32_t El_HaveActor(int32_t actortile) { return g_elActors[actortile].haveit!=0; }
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
uint32_t x, y, z, c;
|
|
||||||
} rng_jkiss_t;
|
|
||||||
|
|
||||||
uint32_t rand_jkiss_u32(rng_jkiss_t *s);
|
|
||||||
double rand_jkiss_dbl(rng_jkiss_t *s);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,85 +0,0 @@
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local ffiC = ffi.C
|
|
||||||
|
|
||||||
local sector = sector
|
|
||||||
local inside = inside
|
|
||||||
|
|
||||||
local math = require("math")
|
|
||||||
local xmath = require("xmath")
|
|
||||||
local stat = require("stat")
|
|
||||||
|
|
||||||
local function resetseed()
|
|
||||||
math.randomseed(834572183572)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getmapbounds()
|
|
||||||
local inf = 1/0
|
|
||||||
local min = { x=inf, y=inf }
|
|
||||||
local max = { x=-inf, y=-inf }
|
|
||||||
|
|
||||||
for i=0,ffiC.numsectors-1 do
|
|
||||||
for w in wallsofsect(i) do
|
|
||||||
local wal = wall[w]
|
|
||||||
|
|
||||||
min.x = math.min(wal.x, min.x)
|
|
||||||
max.x = math.max(wal.x, max.x)
|
|
||||||
min.y = math.min(wal.y, min.y)
|
|
||||||
max.y = math.max(wal.y, max.y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return min, max
|
|
||||||
end
|
|
||||||
|
|
||||||
local function getpoints(n, min, max)
|
|
||||||
local posns, sects = {}, {}
|
|
||||||
|
|
||||||
resetseed()
|
|
||||||
for i=1,n do
|
|
||||||
local x = math.random(min.x, max.x)
|
|
||||||
local y= math.random(min.y, max.y)
|
|
||||||
posns[i] = xmath.vec2(x, y)
|
|
||||||
sects[i] = math.random(0, ffiC.numsectors-1)
|
|
||||||
end
|
|
||||||
|
|
||||||
return posns, sects
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Compare 'inside' implementations
|
|
||||||
--
|
|
||||||
-- N: number of calls
|
|
||||||
function compinside(n)
|
|
||||||
if (type(n) ~= "number") then
|
|
||||||
error("N must be a number")
|
|
||||||
end
|
|
||||||
|
|
||||||
local sti = stat.new()
|
|
||||||
local sts = stat.new()
|
|
||||||
|
|
||||||
local min, max = getmapbounds()
|
|
||||||
local posns, sects = getpoints(n, min, max)
|
|
||||||
|
|
||||||
local isi, iss = {}, {}
|
|
||||||
|
|
||||||
for i=1,n do
|
|
||||||
local t = ffiC.gethiticks()
|
|
||||||
isi[i] = inside(posns[i], sects[i])
|
|
||||||
t = ffiC.gethiticks()-t
|
|
||||||
sti:add(t)
|
|
||||||
|
|
||||||
local t = ffiC.gethiticks()
|
|
||||||
iss[i] = sector[sects[i]]:contains(posns[i])
|
|
||||||
t = ffiC.gethiticks()-t
|
|
||||||
sts:add(t)
|
|
||||||
|
|
||||||
-- if (isi[i]~=iss[i]) then
|
|
||||||
-- print("unequal: "..i.." "..sects[i].." "..posns[i].x.." "..posns[i].y.." ("..tostring(isi[i])..","..tostring(iss[i])..")")
|
|
||||||
-- end
|
|
||||||
assert(isi[i]==iss[i])
|
|
||||||
end
|
|
||||||
|
|
||||||
print("====================")
|
|
||||||
print("inside(): " .. sti:getstatstr())
|
|
||||||
print("contains(): " .. sts:getstatstr())
|
|
||||||
end
|
|
|
@ -1,119 +0,0 @@
|
||||||
-- Random walker as updatesector() timing test, for Lunatic-m32
|
|
||||||
-- Load with: lua "require 'randwalk'"
|
|
||||||
|
|
||||||
local ffi = require "ffi"
|
|
||||||
local ffiC = ffi.C
|
|
||||||
|
|
||||||
local xmath = require "xmath"
|
|
||||||
local stat = require "stat"
|
|
||||||
|
|
||||||
|
|
||||||
-- set to nil to disable saving positions
|
|
||||||
g_posns = {}
|
|
||||||
|
|
||||||
local USF_BREADTH = sector.UPDATE_FLAGS.BREADTH
|
|
||||||
|
|
||||||
local function updatesectorbreadth(pos, sectnum)
|
|
||||||
return updatesector(pos, sectnum, USF_BREADTH)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- [STATS, TIMES] = RANDWALK(N, SPRITENUM, MINLEN, MAXLEN [, RANDOFS [, FUNCI [, LOGFN]]])
|
|
||||||
function randwalk(N, spritenum, minlen, maxlen, randofs, funci, logfn)
|
|
||||||
-- Set the random seed to an arbitrary but fixed value so that the "random" walk
|
|
||||||
-- is deterministically reproducible for particular arguments.
|
|
||||||
math.randomseed(834572183572)
|
|
||||||
|
|
||||||
randofs = randofs or 0
|
|
||||||
|
|
||||||
for i=1,randofs do
|
|
||||||
math.random()
|
|
||||||
end
|
|
||||||
|
|
||||||
local funcnames = { "updatesector", "updatesectorbreadth", "updatesectorz" }
|
|
||||||
local funcs = { updatesector, updatesectorbreadth, updatesectorz }
|
|
||||||
local updatesectorfunc = funcs[funci]
|
|
||||||
|
|
||||||
-- stats
|
|
||||||
local st = { all=stat.new(), succ=stat.new(), fail=stat.new() }
|
|
||||||
|
|
||||||
local times = {}
|
|
||||||
local successp = {}
|
|
||||||
|
|
||||||
local pos = xmath.vec3(sprite[spritenum])
|
|
||||||
local sectnum = sprite[spritenum].sectnum
|
|
||||||
|
|
||||||
for i=1,N do
|
|
||||||
--[[
|
|
||||||
-- get random integers for the displacement
|
|
||||||
local ax,ay,az = math.random(minlen,maxlen), math.random(minlen,maxlen), math.random(minlen,maxlen)
|
|
||||||
|
|
||||||
-- random signs
|
|
||||||
local sx,sy,sz = math.random(0,1), math.random(0,1), math.random(0,1)
|
|
||||||
sx, sy, sz = 2*sx-1, 2*sy-1, 2*sz-1 -- {0,1} -> {-1,1}
|
|
||||||
ax, ay, az = sx*ax, sy*ay, sz*az
|
|
||||||
--]]
|
|
||||||
---[[
|
|
||||||
local ang = 2*math.pi*math.random()
|
|
||||||
local len = math.random(minlen, maxlen)
|
|
||||||
local ax, ay, az = len*math.cos(ang), len*math.sin(ang), 0
|
|
||||||
--]]
|
|
||||||
local newpos = pos + xmath.ivec3(ax,ay,az)
|
|
||||||
|
|
||||||
local t = ffiC.gethiticks()
|
|
||||||
local newsect = updatesectorfunc(newpos, sectnum)
|
|
||||||
t = ffiC.gethiticks()-t
|
|
||||||
|
|
||||||
st.all:add(t)
|
|
||||||
times[i] = t
|
|
||||||
|
|
||||||
successp[i] = (newsect >= 0)
|
|
||||||
|
|
||||||
if (newsect >= 0) then
|
|
||||||
st.succ:add(t)
|
|
||||||
|
|
||||||
pos = newpos
|
|
||||||
sectnum = newsect
|
|
||||||
else
|
|
||||||
st.fail:add(t)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (g_posns ~= nil) then
|
|
||||||
g_posns[i] = pos
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local stallres = st.all:getstats()
|
|
||||||
|
|
||||||
print("====================")
|
|
||||||
printf("Random %s walk starting with sprite %d",
|
|
||||||
funcnames[funci], spritenum)
|
|
||||||
printf("Hop length: [%d .. %d], RNG offset: %d, %s",
|
|
||||||
minlen, maxlen, randofs, logfn)
|
|
||||||
printf("-- Times [ms] (total=%f)", N*stallres.mean)
|
|
||||||
print("All: ".. tostring(stallres))
|
|
||||||
print("Succeeded: ".. st.succ:getstatstr())
|
|
||||||
print("Failed: ".. st.fail:getstatstr())
|
|
||||||
|
|
||||||
if (logfn ~= nil) then
|
|
||||||
local logfile, errmsg = io.open(logfn, "w")
|
|
||||||
if (logfile == nil) then
|
|
||||||
printf("Couldn't open log file \"%s\" for writing: %s",
|
|
||||||
logfn, errmsg)
|
|
||||||
else
|
|
||||||
logfile:write(" \t \t \t\t\n")
|
|
||||||
for i=1,N do
|
|
||||||
if (g_posns ~= nil) then
|
|
||||||
logfile:write(string.format("%s %d %d %d %d\n",
|
|
||||||
tostring(times[i]), successp[i] and 1 or 0,
|
|
||||||
g_posns[i].x, g_posns[i].y, g_posns[i].z))
|
|
||||||
else
|
|
||||||
logfile:write(string.format("%s %d\n", tostring(times[i]), successp[i] and 1 or 0))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
logfile:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return st, ts
|
|
||||||
end
|
|
|
@ -1,63 +0,0 @@
|
||||||
#!/usr/bin/env luajit
|
|
||||||
|
|
||||||
local stat = require "stat"
|
|
||||||
|
|
||||||
if (#arg ~= 2) then
|
|
||||||
print("Usage: profdemo.lua <eduke32> <demo_arg>")
|
|
||||||
print("Example: ./profdemo.lua ../../eduke32 -d3:1,8")
|
|
||||||
print(" (for 8 repetitions of demo 3 at 1 frame per gametic)")
|
|
||||||
print("")
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local eduke32 = arg[1]
|
|
||||||
local demoarg = arg[2]
|
|
||||||
|
|
||||||
local numrepstr = demoarg:match(",[0-9]+$")
|
|
||||||
local numreps = tonumber(numrepstr and numrepstr:sub(2)) or 1
|
|
||||||
local st = { game=stat.new(), drawrooms=stat.new(), drawrest=stat.new() }
|
|
||||||
local stperx = { game=stat.new(), drawrooms=stat.new(), drawrest=stat.new() }
|
|
||||||
|
|
||||||
local unit, unitperx = {}, {}
|
|
||||||
|
|
||||||
for i=1,numreps do
|
|
||||||
local fh = io.popen(eduke32.." "..demoarg)
|
|
||||||
|
|
||||||
while (true) do
|
|
||||||
local str = fh:read("*l")
|
|
||||||
if (str == nil) then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local NUMRE = "([0-9]+%.[0-9]+)"
|
|
||||||
local UNITRE = "[mu]?s"
|
|
||||||
|
|
||||||
local RE = "(== demo [0-9]+ ([a-z]+) times: "..NUMRE.." ("..UNITRE..") %("..NUMRE.." ("..UNITRE.."/[a-z]+)%))"
|
|
||||||
local wholematch, whatstr, time1, unit1, time2, unit2 = str:match(RE)
|
|
||||||
|
|
||||||
if (wholematch ~= nil) then
|
|
||||||
if (numreps==1) then
|
|
||||||
print(wholematch)
|
|
||||||
else
|
|
||||||
st[whatstr]:add(tonumber(time1))
|
|
||||||
stperx[whatstr]:add(tonumber(time2))
|
|
||||||
|
|
||||||
unit[whatstr] = unit1
|
|
||||||
unitperx[whatstr] = unit2
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (numreps > 1) then
|
|
||||||
local keys = { "game", "drawrooms", "drawrest" }
|
|
||||||
|
|
||||||
for i=1,#keys do
|
|
||||||
local key = keys[i]
|
|
||||||
if (unit[key] ~= nil) then
|
|
||||||
print("== "..key.." times:")
|
|
||||||
print(" "..st[key]:getstatstr().." ["..unit[key].."]")
|
|
||||||
print(" "..stperx[key]:getstatstr().." ["..unitperx[key].."]")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,91 +0,0 @@
|
||||||
-- Pseudo random number generation module for Lunatic
|
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local ffiC = ffi.C
|
|
||||||
|
|
||||||
local rawset = rawset
|
|
||||||
|
|
||||||
local type = type
|
|
||||||
local decl = decl -- comes from above (_defs_game.lua)
|
|
||||||
|
|
||||||
local print = print -- for commented out debug block in new() below
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
-- PRNG state struct
|
|
||||||
ffi.cdef[[
|
|
||||||
typedef struct {
|
|
||||||
uint32_t x, y, z, c;
|
|
||||||
} rng_jkiss_t;
|
|
||||||
|
|
||||||
typedef union { unsigned char u[16]; double d[2]; } uchar_double_u_t;
|
|
||||||
]]
|
|
||||||
|
|
||||||
-- PRNG functions
|
|
||||||
decl[[
|
|
||||||
uint32_t rand_jkiss_u32(rng_jkiss_t *s);
|
|
||||||
double rand_jkiss_dbl(rng_jkiss_t *s);
|
|
||||||
|
|
||||||
uint32_t Bcrc32(const void* data, size_t length, uint32_t crc);
|
|
||||||
]]
|
|
||||||
|
|
||||||
local function get_rand_u32(tin)
|
|
||||||
tin.d[0] = ffiC.gethiticks() % 1
|
|
||||||
tin.d[1] = ffiC.gethiticks() % 1
|
|
||||||
return ffiC.Bcrc32(tin.u, 16, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
local mt = {
|
|
||||||
__tostring = function(s)
|
|
||||||
return "rand.new("..s.x..","..s.y..","..s.z..","..s.c..")"
|
|
||||||
end,
|
|
||||||
|
|
||||||
__index = {
|
|
||||||
getu32 = ffiC.rand_jkiss_u32,
|
|
||||||
getdbl = ffiC.rand_jkiss_dbl,
|
|
||||||
|
|
||||||
-- Initialize the JKISS PRNG using the CRC32 of the result of several
|
|
||||||
-- profiling timer calls interpreted as byte sequence.
|
|
||||||
init_time_md4 = function(s)
|
|
||||||
local tin = ffi.new("uchar_double_u_t")
|
|
||||||
local tout = ffi.new("uint32_t [4]")
|
|
||||||
|
|
||||||
repeat
|
|
||||||
s.y = get_rand_u32(tin)
|
|
||||||
until (s.y ~= 0) -- y must not be zero!
|
|
||||||
|
|
||||||
s.x = get_rand_u32(tin)
|
|
||||||
s.z = get_rand_u32(tin)
|
|
||||||
s.c = get_rand_u32(tin) % 698769068 + 1 -- Should be less than 698769069
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
local jkiss = ffi.metatype("rng_jkiss_t", mt)
|
|
||||||
|
|
||||||
function new(x,y,z,c)
|
|
||||||
local s
|
|
||||||
if (x == nil or type(x)=="boolean") then
|
|
||||||
-- initialize with arbitrary but fixed state
|
|
||||||
s = jkiss(123456789, 987654321, 43219876, 6543217)
|
|
||||||
if (x) then
|
|
||||||
s:init_time_md4()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
s = jkiss(x,y,z,c)
|
|
||||||
end
|
|
||||||
--[[
|
|
||||||
local r=ffi.new("rng_jkiss_t")
|
|
||||||
r.x = 123456789; r.y = 987654321; r.z = 43219876; r.c = 6543217;
|
|
||||||
|
|
||||||
local N = 1e7
|
|
||||||
local t=ffiC.gethiticks()
|
|
||||||
for i=1,N do
|
|
||||||
ffiC.rand_jkiss_dbl(r)
|
|
||||||
end
|
|
||||||
print("RANDGEN TIME: "..ffiC.gethiticks()-t) -- x86_64: approx. 100 ms
|
|
||||||
--]]
|
|
||||||
|
|
||||||
return s
|
|
||||||
end
|
|
|
@ -1,103 +0,0 @@
|
||||||
-- Statistics module for Lunatic.
|
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
|
|
||||||
local math = require("math")
|
|
||||||
local string = require("string")
|
|
||||||
|
|
||||||
local tostring = tostring
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
ffi.cdef[[
|
|
||||||
typedef struct {
|
|
||||||
double n;
|
|
||||||
// vvv internal vvv
|
|
||||||
double m, s;
|
|
||||||
double min, max;
|
|
||||||
} runningstat_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const double n;
|
|
||||||
const double mean, var, std;
|
|
||||||
const double min, max;
|
|
||||||
} runningstat_res_t;
|
|
||||||
]]
|
|
||||||
|
|
||||||
|
|
||||||
local NaN = 0/0
|
|
||||||
|
|
||||||
local res_mt = {
|
|
||||||
__tostring = function(s)
|
|
||||||
return
|
|
||||||
string.format("N=%d; mean=%.5g, std=%.5g; min=%.5g, max=%.5g",
|
|
||||||
s.n, s.mean, s.std, s.min, s.max)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
local rstatres = ffi.metatype("runningstat_res_t", res_mt)
|
|
||||||
|
|
||||||
|
|
||||||
local mt = {
|
|
||||||
__tostring = function(s)
|
|
||||||
-- XXX: with this and the other serializing of ffi types, take care of
|
|
||||||
-- NaN and Infs when reading back (e.g. by "nan=0/0" in that context)
|
|
||||||
return "stat.new("..s.n..","..s.m..","..s.s..","..s.min..","..s.max..")"
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- See: Accurately computing running variance, by John D. Cook
|
|
||||||
-- http://www.johndcook.com/standard_deviation.html
|
|
||||||
__index = {
|
|
||||||
add = function(s, num)
|
|
||||||
if (s.n > 0) then
|
|
||||||
-- N>0, recurrence
|
|
||||||
s.n = s.n+1
|
|
||||||
|
|
||||||
local lastm = s.m
|
|
||||||
s.m = lastm + (num-lastm)/s.n
|
|
||||||
s.s = s.s + (num-lastm)*(num-s.m)
|
|
||||||
|
|
||||||
if (num < s.min) then s.min = num end
|
|
||||||
if (num > s.max) then s.max = num end
|
|
||||||
else
|
|
||||||
-- N==0, initialization
|
|
||||||
s.n = 1
|
|
||||||
|
|
||||||
s.m = num
|
|
||||||
s.s = 0
|
|
||||||
|
|
||||||
s.min = num
|
|
||||||
s.max = num
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
getstats = function(s)
|
|
||||||
local var = s.n > 1 and s.s/(s.n-1) or NaN
|
|
||||||
return rstatres(s.n, s.m, var, math.sqrt(var), s.min, s.max)
|
|
||||||
end,
|
|
||||||
|
|
||||||
getstatstr = function(s)
|
|
||||||
return tostring(s:getstats())
|
|
||||||
end,
|
|
||||||
|
|
||||||
reset = function(s)
|
|
||||||
s.n = 0
|
|
||||||
s.m, s.s, s.min, s.max = NaN, NaN, NaN, NaN
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
local rstat = ffi.metatype("runningstat_t", mt)
|
|
||||||
|
|
||||||
function new(n,m,s, min,max)
|
|
||||||
if (n == nil) then
|
|
||||||
-- initialization containing no elements
|
|
||||||
return rstat(0, NaN, NaN, NaN, NaN)
|
|
||||||
elseif (m == nil) then
|
|
||||||
-- same as initialization with N==0 above (one element)
|
|
||||||
return rstat(1, n, 0, n, n)
|
|
||||||
else
|
|
||||||
-- generic initialization (internal use only)
|
|
||||||
return rstat(n,m,s, min,max)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,44 +0,0 @@
|
||||||
--
|
|
||||||
-- strict.lua
|
|
||||||
-- checks uses of undeclared global variables
|
|
||||||
-- All global variables must be 'declared' through a regular assignment
|
|
||||||
-- (even assigning nil will do) in a main chunk before being used
|
|
||||||
-- anywhere or assigned to inside a function.
|
|
||||||
--
|
|
||||||
|
|
||||||
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
|
|
||||||
|
|
||||||
local mt = getmetatable(_G)
|
|
||||||
if mt == nil then
|
|
||||||
mt = {}
|
|
||||||
setmetatable(_G, mt)
|
|
||||||
end
|
|
||||||
|
|
||||||
mt.__declared = {}
|
|
||||||
|
|
||||||
local function what ()
|
|
||||||
local d = getinfo(3, "S")
|
|
||||||
return d and d.what or "C"
|
|
||||||
end
|
|
||||||
|
|
||||||
mt.__newindex = function (t, n, v)
|
|
||||||
if not mt.__declared[n] then
|
|
||||||
local w = what()
|
|
||||||
if w ~= "main" and w ~= "C" then
|
|
||||||
error("assign to undeclared variable '"..n.."'", 2)
|
|
||||||
end
|
|
||||||
mt.__declared[n] = true
|
|
||||||
end
|
|
||||||
rawset(t, n, v)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- PK: change from original (undeclared-refs in main allowed):
|
|
||||||
mt.__index = function (t, n)
|
|
||||||
if not mt.__declared[n] then
|
|
||||||
local w = what()
|
|
||||||
if w ~= "main" and w ~= "C" then
|
|
||||||
error("variable '"..n.."' is not declared", 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return rawget(t, n)
|
|
||||||
end
|
|
|
@ -1,906 +0,0 @@
|
||||||
-- test script for ELua/Lunatic Interpreter
|
|
||||||
|
|
||||||
-- error=nil -- must not affect "require"
|
|
||||||
local require = require
|
|
||||||
local string = require("string")
|
|
||||||
local bit = require("bit")
|
|
||||||
local math = require("math")
|
|
||||||
|
|
||||||
local pcall = pcall
|
|
||||||
local DBG_ = require("_LUNATIC_DBG")
|
|
||||||
|
|
||||||
local gv, sector, wall, sprite, spriteext = gv, sector, wall, sprite, spriteext
|
|
||||||
local actor, player, projectile = actor, player, projectile
|
|
||||||
local gameevent, gameactor = gameevent, gameactor
|
|
||||||
|
|
||||||
local spritesofsect = spritesofsect
|
|
||||||
local hitscan = hitscan
|
|
||||||
|
|
||||||
local assert, error, print, tostring = assert, error, print, tostring
|
|
||||||
|
|
||||||
local D = require("CON.DEFS")
|
|
||||||
|
|
||||||
print('---=== ELua Test script ===---')
|
|
||||||
|
|
||||||
local function printf(fmt, ...)
|
|
||||||
print(string.format(fmt, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function checkfail(funcstr, expectedmsg)
|
|
||||||
if (DBG_ == nil) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local status, errmsg = pcall(DBG_.loadstring(funcstr))
|
|
||||||
if (status) then
|
|
||||||
print('^21ERROR:^O '..funcstr.." DIDN'T fail")
|
|
||||||
else
|
|
||||||
if (expectedmsg==nil or string.find(errmsg, expectedmsg, 1, true)) then
|
|
||||||
print("^11SUCCESS:^O "..funcstr.." failed: "..errmsg)
|
|
||||||
else
|
|
||||||
-- XXX: beginning with "^10" is counted as error in OSD_Printf()
|
|
||||||
print("^10ERROR*:^O "..funcstr.." failed: "..errmsg..
|
|
||||||
", but expected error message was: "..expectedmsg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"ENTERLEVEL",
|
|
||||||
|
|
||||||
function()
|
|
||||||
local vol, lev = gv.currentEpisode(), gv.currentLevel()
|
|
||||||
printf('volume=%d, level=%d', vol, lev)
|
|
||||||
|
|
||||||
if (vol ~= 4) then
|
|
||||||
-- Tweak some sector pals.
|
|
||||||
print('tweaking sector pals')
|
|
||||||
print('numsectors: ' .. gv.numsectors .. ' of ' .. gv.MAXSECTORS)
|
|
||||||
|
|
||||||
local SF = sector.STAT
|
|
||||||
for i = 0, gv.numsectors/2 do
|
|
||||||
local sec = sector[i]
|
|
||||||
sec.floorpal = 1;
|
|
||||||
sector[i].floor.shade = sec.floor.shade + 4
|
|
||||||
sector[i].ceilingpal = 2;
|
|
||||||
local ceil = sec.ceiling
|
|
||||||
ceil.shade = sector[i].ceiling.shade + 8
|
|
||||||
ceil.statbits:flip(SF.SMOOSH)
|
|
||||||
sec.floorstatbits:flip(SF.SWAPXY)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (vol==1 and lev==1) then -- E1L1
|
|
||||||
print('tweaking some sprites 2')
|
|
||||||
local i = 562
|
|
||||||
spriteext[i].alpha = 0.5;
|
|
||||||
sprite[i].cstat = bit.bor(sprite[i].cstat, 2+512);
|
|
||||||
spriteext[i].pitch = 128;
|
|
||||||
spriteext[i].roll = 256;
|
|
||||||
|
|
||||||
i = 107 -- pistol ammo at rooftop
|
|
||||||
spriteext[i].pitch = 128;
|
|
||||||
spriteext[i].roll = 256;
|
|
||||||
|
|
||||||
for spr in spritesofsect(307) do -- some fence sprites in E1L1
|
|
||||||
printf('spr %d', spr)
|
|
||||||
sprite[spr].pal = 6
|
|
||||||
end
|
|
||||||
|
|
||||||
actor[562].flags = bit.bor(actor[562].flags, 2); -- pal 6 with goggles on front SEENINE
|
|
||||||
end
|
|
||||||
---[[
|
|
||||||
if (vol==1 and lev==8) then
|
|
||||||
local havebunch = false
|
|
||||||
for i=0,gv.numsectors-1 do
|
|
||||||
havebunch = havebunch or (sector[i].ceilingbunch >= 0)
|
|
||||||
end
|
|
||||||
if (havebunch) then
|
|
||||||
print('tweaking bunch 1');
|
|
||||||
-- trueror1.map
|
|
||||||
for i, what in sectorsofbunch(1, gv.BOTH_CF) do
|
|
||||||
sector[i][what].z = sector[i][what].z - 3*1024;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
--]]
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"JUMP",
|
|
||||||
|
|
||||||
function()
|
|
||||||
print("tweaking forcefield with lotag 34 (E2L1)")
|
|
||||||
|
|
||||||
for w=0,gv.numwalls-1 do
|
|
||||||
local wal = wall[w]
|
|
||||||
if (wal.overpicnum == D.W_FORCEFIELD or wal.overpicnum == D.W_FORCEFIELD+1) then
|
|
||||||
if (wal.lotag==34) then
|
|
||||||
wal.cstat = wal.cstatbits:test(85) and 0 or 85
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
local unsafe = pcall(function() string.UNSAFE=true; end)
|
|
||||||
|
|
||||||
checkfail("tostring = nil", "attempt to write into the global environment")
|
|
||||||
--DBG_.printkv('_G in test.elua', _G)
|
|
||||||
|
|
||||||
-- direct gv array access forbidden
|
|
||||||
checkfail('gv.sprite[0].yrepeat = 100', "access forbidden")
|
|
||||||
|
|
||||||
checkfail('print(sprite[100000].ceilingpal)', "out-of-bounds sprite[] read access")
|
|
||||||
|
|
||||||
checkfail('print(gv.sprite[0])', "access forbidden")
|
|
||||||
|
|
||||||
-- set metatable forbidden
|
|
||||||
checkfail('setmetatable(sprite, {})', "attempt to read undeclared variable 'setmetatable'")
|
|
||||||
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"ENTERLEVEL",
|
|
||||||
|
|
||||||
function()
|
|
||||||
-- OOB write access.
|
|
||||||
-- Note that indexing ("reading") sector fails, even if the user wants to
|
|
||||||
-- assign to a sector member. Potentially confusing error message.
|
|
||||||
checkfail('sector[-1].ceilingpal = 4', "out-of-bounds sector[] read access")
|
|
||||||
|
|
||||||
-- wallnum member is read-only
|
|
||||||
checkfail('sector[0].wallnum = 0', "attempt to write to constant location") -- this comes from LJ/FFI
|
|
||||||
|
|
||||||
-- direct sector write access forbidden
|
|
||||||
checkfail('sector[4] = sector[6]', "cannot write directly to sector[]")
|
|
||||||
|
|
||||||
-- creating new keys forbidden... handled by LuaJIT
|
|
||||||
checkfail('wall[4].QWE = 123', "has no member named 'QWE'")
|
|
||||||
|
|
||||||
-- no pointer arithmetic!
|
|
||||||
checkfail('local spr = sprite[0]; local x=spr+1', "attempt to perform arithmetic on")
|
|
||||||
|
|
||||||
-- actor[].t_data[] is not accessible for now
|
|
||||||
checkfail('local i = actor[0].t_data[15]', "has no member named 't_data'")
|
|
||||||
|
|
||||||
-- sprite.picnum may happen as a thinko/typo kind of error (spr.picnum was meant)
|
|
||||||
checkfail("local pic = sprite.picnum", "invalid access to static data")
|
|
||||||
|
|
||||||
checkfail("require('engine').setshadetab(200, nil)",
|
|
||||||
"setshadetab() may be run only while LUNATIC_FIRST_TIME is true")
|
|
||||||
|
|
||||||
checkfail("sprite[0]:set_picnum(-10)", "invalid tile number")
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
-- gv.numsectors is read-only
|
|
||||||
checkfail('gv.numsectors = 4', "attempt to write to constant location")
|
|
||||||
|
|
||||||
-- cannot create new fields in 'gv'
|
|
||||||
checkfail('gv.QWE = 4', "write access forbidden")
|
|
||||||
|
|
||||||
-- that would be horrible...
|
|
||||||
checkfail('sprite._nextspritesect[4] = -666', "cannot write directly to nextspritesect[]")
|
|
||||||
|
|
||||||
-- we're indexing a plain array!
|
|
||||||
checkfail('print(sprite._nextspritesect[4].whatfield)', "attempt to index a number value")
|
|
||||||
|
|
||||||
-- our 'require' has only safe stuff
|
|
||||||
--checkfail("require('os')")
|
|
||||||
|
|
||||||
-- gamevars are created using a special different mechanism
|
|
||||||
checkfail("new_global = 345", "attempt to write into the global environment")
|
|
||||||
-- Can't reassign to existing vars either
|
|
||||||
assert(actor ~= nil)
|
|
||||||
checkfail("actor = 345", "attempt to write into the global environment")
|
|
||||||
|
|
||||||
-- can't redefine constants in 'gv'
|
|
||||||
checkfail('gv.CEILING = 3', "attempt to write to constant location")
|
|
||||||
|
|
||||||
-- string.dump is unavailable
|
|
||||||
checkfail('local s=require[[string]]; local tmp=s.dump(gameevent)',
|
|
||||||
"attempt to call field 'dump' (a nil value)")
|
|
||||||
|
|
||||||
if (not unsafe) then
|
|
||||||
-- changing base module tables is disallowed
|
|
||||||
checkfail('local s=require[[string]]; s.format=nil', "modifying base module table forbidden")
|
|
||||||
else
|
|
||||||
print('WARNING: RUNNING WITH UNPROTECTED BASE MODULES')
|
|
||||||
end
|
|
||||||
|
|
||||||
print('')
|
|
||||||
-- This is problematic, even though pretty much every access will yield a
|
|
||||||
-- "missing declaration" error.
|
|
||||||
-- See http://luajit.org/ext_ffi_api.html#ffi_C about what stuff ffi.C contains.
|
|
||||||
checkfail('gv.luaJIT_setmode(nil, 0, 0)', "missing declaration for symbol 'luaJIT_setmode'")
|
|
||||||
|
|
||||||
checkfail('gv.luaJIT_BC_con_lang', "attempt to call a nil value")
|
|
||||||
checkfail('gv.gethiticks = nil', "attempt to write to constant location")
|
|
||||||
|
|
||||||
checkfail('gameactor{1680, 0}', "must provide a function with last numeric arg or .func")
|
|
||||||
|
|
||||||
checkfail("do local bt=require'test.test_bitar'; bt.QWE=1; end", "modifying module table forbidden")
|
|
||||||
-- the cdata returned by player[] can't be made into a pointer!
|
|
||||||
checkfail("do local pl=player[0]; i=pl[1]; end")
|
|
||||||
checkfail("do local ud=gv.ud.camerasprite; end", "access forbidden") -- test for proper decl()
|
|
||||||
checkfail("gv.g_sizes_of=nil; print(gv.g_sizes_of[0])", "write access forbidden")
|
|
||||||
checkfail("gv.cam.sect=-1", "invalid sector number")
|
|
||||||
checkfail("local flag=gv.SFLAG_NULL", "missing declaration")
|
|
||||||
|
|
||||||
-- NOTE: player[0] is forbidden at file scope, this is just for testing purposes.
|
|
||||||
player[0].wackedbyactor = -1 -- should succeed
|
|
||||||
checkfail("player[0].curr_weapon = -1", "Invalid weapon ID")
|
|
||||||
player[0].curr_weapon = 1
|
|
||||||
checkfail("local w = player[0].weapon[-1]", "out-of-bounds weapon read access")
|
|
||||||
|
|
||||||
-- XXX: This gives a very strange error message: "attempt to write to constant location". Why?
|
|
||||||
-- (note how I forgot to index weapon with a weapon index or name)
|
|
||||||
--player[0].weapon.firesound = 1e5
|
|
||||||
checkfail("player[0].weapon.SHOTGUN.firesound = 1e5", "invalid sound number")
|
|
||||||
checkfail("player[0].weapon.SHOTGUN.firesound = 0/0", "must be a non-NaN number")
|
|
||||||
checkfail("player[0].weapon.SHOTGUN.firesound = 1/0", "invalid sound number")
|
|
||||||
checkfail("gameactor{1680, action=require('con').action{numframes=-10}, function() end}",
|
|
||||||
"action has negative number of frames")
|
|
||||||
-- NOTE: It should only be relied on that setting e.g. .firesound to -1 sets it
|
|
||||||
-- to 0, not other negative values.
|
|
||||||
player[0].weapon.SHOTGUN.firesound = -1/0
|
|
||||||
assert(player[0].weapon.SHOTGUN.firesound == 0)
|
|
||||||
|
|
||||||
gameevent{gv.EVENT_JUMP,
|
|
||||||
function(actori, playeri, dist)
|
|
||||||
printf("jump i=%d p=%d d=%d", actori, playeri, dist)
|
|
||||||
error("greetings from EVENT_JUMP")
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
--[[
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"PROCESSINPUT",
|
|
||||||
|
|
||||||
-- Input test.
|
|
||||||
-- NOTE: I don't think that exposing g_player[].sync (aka "input") is a good idea...
|
|
||||||
func = function(actori, playeri, dist)
|
|
||||||
local IB = player._INPUT_BITS
|
|
||||||
local input = player[playeri]._input
|
|
||||||
if (bit.band(input.bits, IB.JUMP) ~= 0) then
|
|
||||||
print("JUMPED")
|
|
||||||
-- ... because for example this doesn't work
|
|
||||||
-- (P_HandleSharedKeys, where the JETPACK bit is tested, runs
|
|
||||||
-- before P_ProcessInput):
|
|
||||||
input.bits = bit.bor(input.bits, IB.JETPACK)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
--]]
|
|
||||||
|
|
||||||
local WEAPON = gv.WEAPON
|
|
||||||
|
|
||||||
-- Bad hack to test out pitch/roll: EVENT_GAME is discouraged from Lunatic.
|
|
||||||
local PITCH_PICNUM = { [D.SEENINE]=true, }
|
|
||||||
gameevent{ "GAME",
|
|
||||||
function(aci)
|
|
||||||
local spr = sprite[aci]
|
|
||||||
if (PITCH_PICNUM[spr.picnum]) then
|
|
||||||
local height = spr:getheightofs()
|
|
||||||
|
|
||||||
local sexy = spriteext[aci]
|
|
||||||
sexy.pitch = gv.totalclock
|
|
||||||
sexy.mdoff.x = -height/16 -- test xoff + pitch
|
|
||||||
sexy.mdoff.z = -height -- test zoff + pitch
|
|
||||||
|
|
||||||
-- Test roll + yoff
|
|
||||||
sexy.roll = gv.totalclock
|
|
||||||
sexy.mdoff.y = -height/16
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
-- test event chaining
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"JUMP",
|
|
||||||
|
|
||||||
flags = actor.FLAGS.chain_beg,
|
|
||||||
|
|
||||||
function(actori, playeri, dist)
|
|
||||||
local ps = player[playeri]
|
|
||||||
print("\n--- I'm first!")
|
|
||||||
-- DBG_.oom()
|
|
||||||
local pistol = ps.weapon.PISTOL
|
|
||||||
if (pistol.shoots ~= D.RPG) then
|
|
||||||
pistol.shoots = D.RPG
|
|
||||||
else
|
|
||||||
pistol.shoots = D.SHOTSPARK1
|
|
||||||
end
|
|
||||||
ps.weapon[WEAPON.PISTOL].firesound = D.LIGHTNING_SLAP
|
|
||||||
|
|
||||||
-- This succeeds, because sound2time is a time, not a sound.
|
|
||||||
ps.weapon.SHOTGUN.sound2time = 5000
|
|
||||||
printf("set shotgun's sound2time to %d", ps.weapon.SHOTGUN.sound2time)
|
|
||||||
|
|
||||||
-- Set pipebomb and tripbomb to timed mode.
|
|
||||||
-- XXX: Provide either named constants or methods?
|
|
||||||
-- XXX: These are probably reset to default on new game.
|
|
||||||
ps.pipebombControl = 2
|
|
||||||
ps.tripbombControl = 2
|
|
||||||
|
|
||||||
-- Test of INTERNAL member _pals.
|
|
||||||
-- NOTE: setting colors partially is bad! E.g. after an item is
|
|
||||||
-- picked up, col[0] and col[1] remain and tint everything greenish.
|
|
||||||
if (DBG_ ~= nil) then
|
|
||||||
ps._pals[2] = 20
|
|
||||||
ps._pals.f = 30
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
local xmath = require "xmath"
|
|
||||||
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
gv.EVENT_ENTERLEVEL,
|
|
||||||
|
|
||||||
function()
|
|
||||||
if (DBG_ ~= nil) then
|
|
||||||
DBG_.testmembread()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- NOTE: times are for helixhorned (Core2Duo 3GHz)
|
|
||||||
local i
|
|
||||||
local N = 1e6
|
|
||||||
local t = gv.gethiticks()
|
|
||||||
|
|
||||||
for i=3,N do
|
|
||||||
gv.gethiticks()
|
|
||||||
end
|
|
||||||
|
|
||||||
t = gv.gethiticks()-t
|
|
||||||
|
|
||||||
-- x86_64: 35ns/call, x86: 280 ns/call
|
|
||||||
-- Windows 32-bit: about 1 us/call?
|
|
||||||
printf("%d gethiticks() calls took %.03f ms (%.03f us/call)",
|
|
||||||
N, t, (t*1000)/N)
|
|
||||||
|
|
||||||
local sum=0
|
|
||||||
t = gv.gethiticks()
|
|
||||||
for i=1,N do sum = sum+gv.ksqrt(i) end
|
|
||||||
t = gv.gethiticks()-t
|
|
||||||
-- x86_64: 14ns/call
|
|
||||||
printf("%d ksqrt() calls took %.03f ms (%.03f us/call) [sum=%f]",
|
|
||||||
N, t, (t*1000)/N, sum)
|
|
||||||
|
|
||||||
sum=0
|
|
||||||
t = gv.gethiticks()
|
|
||||||
for i=1,N do sum = sum+math.sqrt(i) end
|
|
||||||
t = gv.gethiticks()-t
|
|
||||||
-- x86_64: 7ns/call
|
|
||||||
printf("%d math.sqrt() calls took %.03f ms (%.03f us/call) [sum=%f]",
|
|
||||||
N, t, (t*1000)/N, sum)
|
|
||||||
|
|
||||||
printf("sqrt(0xffffffff) = %f(ksqrt) %f(math.sqrt)",
|
|
||||||
gv.ksqrt(0xffffffff), math.sqrt(0xffffffff))
|
|
||||||
|
|
||||||
local pl = player[0]
|
|
||||||
-- MAX < current is "allowed"
|
|
||||||
pl.max_ammo_amount[WEAPON.RPG] = 17
|
|
||||||
pl:give_weapon(WEAPON.RPG)
|
|
||||||
pl.ammo_amount[WEAPON.RPG] = 54
|
|
||||||
|
|
||||||
pl:give_weapon(WEAPON.SHRINKER)
|
|
||||||
-- This looks much prettier:
|
|
||||||
pl.ammo_amount.SHRINKER = 2
|
|
||||||
|
|
||||||
-- MORTER2 from test/weaponvars.con
|
|
||||||
local PNUM = 1653
|
|
||||||
local proj = projectile[PNUM]
|
|
||||||
if (proj ~= nil) then
|
|
||||||
printf('Have projectile %d', PNUM)
|
|
||||||
player[0].weapon.SHOTGUN.shoots = PNUM
|
|
||||||
proj.drop = 0
|
|
||||||
proj:set_trail(D.SMALLSMOKE)
|
|
||||||
else
|
|
||||||
printf('^10Do NOT have projectile %d, test/weaponvars.con not loaded?', PNUM)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (gv._LUNATIC_STRICT == 0) then
|
|
||||||
t = gv.gethiticks()
|
|
||||||
local N=1
|
|
||||||
for n=1,N do
|
|
||||||
for i=0,gv.MAXSPRITES-1 do
|
|
||||||
sprite[i].blend = 1
|
|
||||||
end
|
|
||||||
for i=gv.MAXSPRITES-1,0,-1 do
|
|
||||||
sprite[i].shade = 1
|
|
||||||
end
|
|
||||||
for i=0,gv.MAXSPRITES-1 do
|
|
||||||
sprite[i].xoffset = 0
|
|
||||||
end
|
|
||||||
for i=gv.MAXSPRITES-1,0,-1 do
|
|
||||||
sprite[i].yoffset = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
t = gv.gethiticks()-t
|
|
||||||
printf("%d x four 0..MAXSPRITES-1 iterations took %.03f us per outer iteration", N, (1000*t)/N)
|
|
||||||
-- Results on x86:
|
|
||||||
-- N=1: 480-1000 us (too large variance)
|
|
||||||
-- N=10: 190-210 us * 10 = 1.9-2.1 ms
|
|
||||||
-- N=100: about 160 us * 100 = about 16 ms
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Make the DUKECAR in E1L1 into a zombie actor (temporarily)
|
|
||||||
-- NOTE: Use static value (not the one from 'D').
|
|
||||||
if (sprite[24].picnum==2491) then
|
|
||||||
sprite.changestat(24, actor.STAT.ZOMBIEACTOR)
|
|
||||||
end
|
|
||||||
|
|
||||||
checkfail("gameevent('GAME', function() print('qwe') end)",
|
|
||||||
"must be called from top level")
|
|
||||||
|
|
||||||
-- Test vec3 + wall. Pseudo wall member 'z' will be accessed.
|
|
||||||
local mpos = xmath.vec3()
|
|
||||||
for i=0,gv.numwalls-1 do
|
|
||||||
mpos = mpos + wall[i]
|
|
||||||
end
|
|
||||||
mpos = mpos/gv.numwalls
|
|
||||||
local impos = xmath.ivec3(mpos)^20 -- test ivec3 with dvec3 arg, test '^' op
|
|
||||||
assert(impos.z == -20)
|
|
||||||
printf("Map center point: (%d,%f)", mpos.x, impos.y)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
gameevent{"LOADACTOR", function(i)
|
|
||||||
local spr = sprite[i]
|
|
||||||
if (i==614 and spr.picnum==930) then
|
|
||||||
-- "police line" ribbon in E1L1
|
|
||||||
-- clear the hitag so that it doesn't spawn as FALLER from premap
|
|
||||||
-- Rather a HACK: relies on an implementation detail (A_Spawn()
|
|
||||||
-- "hard-wired" code).
|
|
||||||
spr.hitag = 0
|
|
||||||
end
|
|
||||||
end}
|
|
||||||
|
|
||||||
gameactor
|
|
||||||
{
|
|
||||||
-- "police line" ribbon
|
|
||||||
930, nil, 1,
|
|
||||||
|
|
||||||
func = function(i)
|
|
||||||
local spr = sprite[i]
|
|
||||||
local r = math.random
|
|
||||||
local d = 20
|
|
||||||
-- NOTE: __add metamethod is called because of the RHS:
|
|
||||||
local v = spr + xmath.vec3(r(-d,d), r(-d,d))
|
|
||||||
spr:setpos(v):updatesect()
|
|
||||||
|
|
||||||
-- Test vec3 constructor with cdata.
|
|
||||||
local tempvec = xmath.vec3(player[0].pos)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
local stat = require("stat")
|
|
||||||
local hs = stat.new()
|
|
||||||
|
|
||||||
local con = require("con")
|
|
||||||
local AC, MV = con.AC, con.MV
|
|
||||||
|
|
||||||
local CAC, CMV, CAI = require("CON.ACTION"), require("CON.MOVE"), require("CON.AI")
|
|
||||||
assert(CAC); assert(CMV); assert(CAI)
|
|
||||||
|
|
||||||
local AC, MV = {}, {}
|
|
||||||
|
|
||||||
AC.TROOPSTAND = assert(CAC.ATROOPSTAND) -- or con.action{0,1,5,1,1}
|
|
||||||
AC.TROOPFLINTCH = con.action{50, 1, 1, 1, 6}
|
|
||||||
MV.SHRUNKVELS = con.move{hvel=32}
|
|
||||||
con.ai(AC.TROOPFLINTCH, MV.SHRUNKVELS, 0) -- unused; TODO: test
|
|
||||||
|
|
||||||
local TROOPSTRENGTH = 30
|
|
||||||
|
|
||||||
local AF = actor.FLAGS
|
|
||||||
local CS = sprite.CSTAT
|
|
||||||
|
|
||||||
-- Crosshair sprite.
|
|
||||||
-- NOTE: This ought to be a gamevar -- if a savegame is restored, a new one
|
|
||||||
-- will be spawned.
|
|
||||||
local chair
|
|
||||||
|
|
||||||
gameactor{ D.APLAYER, AF.chain_end,
|
|
||||||
function(aci, pli)
|
|
||||||
if (chair == nil) then
|
|
||||||
chair = con.spawn(555, aci)
|
|
||||||
printf("Spawned our crosshair: sprite %d", chair)
|
|
||||||
local spr = sprite[chair]
|
|
||||||
-- Set to STAT_MISC because otherwise interpolation goes crazy (old
|
|
||||||
-- value never updated; dunno why...)
|
|
||||||
sprite.changestat(chair, actor.STAT.MISC)
|
|
||||||
spr.xrepeat, spr.yrepeat = 96, 96
|
|
||||||
spr.cstatbits:set(CS.CENTER)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ps = player[pli]
|
|
||||||
local ray = xmath.kangvec(ps.ang, -(ps.horiz-100)*2048)
|
|
||||||
|
|
||||||
local hit = hitscan(ps.pos, ps.cursectnum, ray, 0)
|
|
||||||
if (hit.sector >= 0) then
|
|
||||||
sprite[chair]:setpos(hit.pos)
|
|
||||||
sprite.changesect(chair, hit.sector)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Add NODAMAGEPUSH flag to NEWBEAST.
|
|
||||||
gameactor { D.NEWBEAST, AF.chain_end + AF.NODAMAGEPUSH, function() end }
|
|
||||||
|
|
||||||
-- Also test actor code chaining: strength is doubled.
|
|
||||||
gameactor
|
|
||||||
{
|
|
||||||
D.LIZTROOP, AF.chain_end+AF.enemy, 2*TROOPSTRENGTH,
|
|
||||||
|
|
||||||
action = AC.TROOPSTAND,
|
|
||||||
|
|
||||||
func = function(i, playeri, dist)
|
|
||||||
sprite[i].pal = math.random(32)
|
|
||||||
-- sprite[i].ang = bit.band(sprite[i].ang-20, 2047)
|
|
||||||
|
|
||||||
local spr = sprite[i]
|
|
||||||
|
|
||||||
local t = gv.gethiticks()
|
|
||||||
local hit = hitscan(spr, spr.sectnum, {x=10, y=10, z=0}, gv.CLIPMASK0)
|
|
||||||
|
|
||||||
hs:add(1000*(gv.gethiticks()-t))
|
|
||||||
|
|
||||||
if (hs.n == 300) then
|
|
||||||
printf("hitscan: %s", hs:getstatstr())
|
|
||||||
hs:reset()
|
|
||||||
error("greetings from LIZTROOP actor")
|
|
||||||
end
|
|
||||||
|
|
||||||
local actr = actor[i]
|
|
||||||
if (actr:get_count() % 30 == 0) then
|
|
||||||
spr.cstatbits:flip(CS.YFLIP)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Test of bitint's ":test()" for actor[].flags.
|
|
||||||
actr.flagsbits:test(AF.NVG)
|
|
||||||
|
|
||||||
if (dist < 4096) then
|
|
||||||
-- Duke Vader / Anakin Nukewalker?
|
|
||||||
actor[i]:set_action(AC.TROOPFLINTCH)
|
|
||||||
actor[i]:set_move(MV.SHRUNKVELS)
|
|
||||||
|
|
||||||
if (dist < 1024) then
|
|
||||||
con.killit()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (actr:has_action(CAC.ATROOPWALKING)) then
|
|
||||||
if (actr:get_count() % 50 == 0) then
|
|
||||||
actr.movflagsbits:flip(actor.MOVFLAGS.spin)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- NOTE: the animate callback is not yet documented and thus not official API!
|
|
||||||
animate = function(tspr)
|
|
||||||
local tspr2 = tspr:dup()
|
|
||||||
if (tspr2) then
|
|
||||||
tspr2.x = tspr2.x + 512*math.cos(gv.totalclock/60)
|
|
||||||
tspr2.y = tspr2.y + 512*math.sin(gv.totalclock/60)
|
|
||||||
tspr2.cstatbits:set(CS.TRANS_BITMASK)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- XXX: inserted tsprites have floor shadow in classic! (r_shadow)
|
|
||||||
-- G_DoSpriteAnimations() is passed as callback to the engine on occasion,
|
|
||||||
-- in other words, created tsprites may be fed back to G_DoSpriteAnimations()!
|
|
||||||
-- classic: shows shadow for both "ghost" liztroop and aim "reticle"
|
|
||||||
-- Polymost: only for "ghost"
|
|
||||||
-- Polymer: none
|
|
||||||
local aimv = 256*xmath.bangvec(tspr.ang)
|
|
||||||
local hit = hitscan(tspr^(16*256), tspr.sectnum, aimv, gv.CLIPMASK1)
|
|
||||||
|
|
||||||
if (hit.wall >= 0) then
|
|
||||||
local aimtspr = tspr:dup()
|
|
||||||
if (aimtspr) then
|
|
||||||
aimtspr.pal = 2
|
|
||||||
aimtspr:set_picnum(555)
|
|
||||||
aimtspr:setpos(hit.pos, hit.sector)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
gameactor
|
|
||||||
{
|
|
||||||
1275, -- newspaper, E4L6 sprite #513
|
|
||||||
|
|
||||||
action = con.action{0, 4, delay=20}, -- Same as {0, 4, 1, 1, 20}
|
|
||||||
move = 1, -- so that the ID is nonzero and it will move
|
|
||||||
|
|
||||||
func = function(aci)
|
|
||||||
local a = actor[aci]
|
|
||||||
local delay = math.sin(0.1 * 2*math.pi*gv.totalclock/120)
|
|
||||||
a:set_action_delay(20 + 10*delay)
|
|
||||||
if (sprite[aci].pal ~= 0) then
|
|
||||||
a:set_hvel(1024/30)
|
|
||||||
a:set_vvel(-1024/30)
|
|
||||||
else
|
|
||||||
a:set_hvel(50*delay)
|
|
||||||
end
|
|
||||||
a.movflags = actor.MOVFLAGS.geth + actor.MOVFLAGS.getv
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"DISPLAYROOMS",
|
|
||||||
|
|
||||||
function(aci, pli)
|
|
||||||
local ps = player[pli]
|
|
||||||
local cam = gv.cam
|
|
||||||
|
|
||||||
if (ps.newowner < 0) then
|
|
||||||
cam.pos.z = cam.pos.z + 64*16*math.sin(gv.totalclock/30)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (ps.cursectnum >= 0) then
|
|
||||||
local hit = sector[ps.cursectnum]:zrangeat(cam.pos)
|
|
||||||
if (gv.totalclock%200==0) then
|
|
||||||
printf("hit %s %d at z=%d, %s %d at z=%d",
|
|
||||||
hit.c.spritep and "sprite" or "sector", hit.c.num, hit.c.z,
|
|
||||||
hit.f.spritep and "sprite" or "sector", hit.f.num, hit.f.z)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"DISPLAYREST",
|
|
||||||
|
|
||||||
function()
|
|
||||||
for i=0,10 do
|
|
||||||
for j=1,100 do
|
|
||||||
-- XXX: This is slower in the Polymodes than with classic!
|
|
||||||
-- con._gametext(2822, j, i, 12, 0, 0, 16, 0,0,gv.xdim,gv.ydim,8192)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Clear showing every sector with the pavement floor tile. (Unless we're
|
|
||||||
-- in such a sector or an adjoining one.)
|
|
||||||
-- XXX: We need a better place to do this, maybe an event in
|
|
||||||
-- G_DisplayRest() where show2dsector[] is tweaked?
|
|
||||||
-- NOT YET OFFICIAL API.
|
|
||||||
local show2dsector = sector.showbitmap
|
|
||||||
for i=0,gv.numsectors-1 do
|
|
||||||
if (sector[i].floorpicnum==815) then
|
|
||||||
show2dsector:set0(i)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
gameactor
|
|
||||||
{
|
|
||||||
D.APLAYER, actor.FLAGS.chain_beg,
|
|
||||||
|
|
||||||
function(aci, pli)
|
|
||||||
if (con._squished(aci, pli)) then
|
|
||||||
printf("Squished LunaCON test")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
gameactor
|
|
||||||
{
|
|
||||||
-- Innocent sign, similar to test/weaponvars.con actor 909 (tree trunk)
|
|
||||||
1211, actor.FLAGS.replace,
|
|
||||||
|
|
||||||
function(aci, pli)
|
|
||||||
local a = actor[aci]
|
|
||||||
|
|
||||||
if (a:get_count() >= 120) then
|
|
||||||
local i = con.spawn(D.TRANSPORTERSTAR, aci)
|
|
||||||
sprite[i].z = sprite[i].z - 1024*16
|
|
||||||
con.shoot(D.MORTER, aci, -4096)
|
|
||||||
a:set_count(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
local function testbit(num, b)
|
|
||||||
return bit.band(num,b)~=0 and 1 or 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local FADE_SPEED = {
|
|
||||||
[WEAPON.KNEE] = 1/2.5,
|
|
||||||
|
|
||||||
1/128,
|
|
||||||
1/5,
|
|
||||||
1/3,
|
|
||||||
1/2,
|
|
||||||
1, -- pipebomb: ~1 sec
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
5,
|
|
||||||
127, -- freezer; such a fast fade is not visible, but it clears any
|
|
||||||
-- existing one (if of higher priority)
|
|
||||||
[WEAPON.GROW] = 9.9, -- test banker's rounding -- should be like 10
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Test player[]:fadecol(), a better palfrom.
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"CHANGEWEAPON",
|
|
||||||
|
|
||||||
function (aci, pli)
|
|
||||||
local ps = player[pli]
|
|
||||||
local neww, curw = gv.RETURN, ps.curr_weapon
|
|
||||||
|
|
||||||
local r, g, b = testbit(neww, 1), testbit(neww, 2), testbit(neww, 4)
|
|
||||||
local speed = FADE_SPEED[neww] or 1
|
|
||||||
player[pli]:fadecol(0.5, r, g, b, speed, neww-5)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Time the above p:fadecol() test for verification of the <speed> argument.
|
|
||||||
local last_f, last_t = 0, 0
|
|
||||||
local last_secs = nil
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"DISPLAYREST",
|
|
||||||
|
|
||||||
function(aci, pli)
|
|
||||||
local ps = player[pli]
|
|
||||||
-- WARNING: _pals in INTERNAL and off-limits to users!
|
|
||||||
local curf = ps._pals.f
|
|
||||||
if (curf > last_f) then
|
|
||||||
-- Starting a tint
|
|
||||||
last_secs = nil
|
|
||||||
last_f = curf
|
|
||||||
last_t = gv.getticks()
|
|
||||||
elseif (last_t > 0 and curf==0) then
|
|
||||||
-- Fade has ended
|
|
||||||
last_secs = (gv.getticks()-last_t)/1000
|
|
||||||
last_f, last_t = 0, 0
|
|
||||||
end
|
|
||||||
|
|
||||||
if (last_secs ~= nil) then
|
|
||||||
con.minitext(10, 10, string.format("Last fade time: %.03f s (%.03f gametics)",
|
|
||||||
last_secs, last_secs*30))
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("EVENT_INIT = %d", gv.EVENT_INIT) -- tests default defines
|
|
||||||
|
|
||||||
local bittest = require "test.test_bitar"
|
|
||||||
bittest.sieve()
|
|
||||||
|
|
||||||
require("test.test_geom", 123123)
|
|
||||||
require("test.test_rotspr")
|
|
||||||
|
|
||||||
do
|
|
||||||
-- Test ksin vs. sinb
|
|
||||||
local xmath = require "xmath"
|
|
||||||
local sum = 0
|
|
||||||
|
|
||||||
local N = 1000
|
|
||||||
local t = gv.gethiticks()
|
|
||||||
for i=0,N*2048-1 do
|
|
||||||
sum = sum+xmath.ksin(i)
|
|
||||||
end
|
|
||||||
t = gv.gethiticks()-t
|
|
||||||
sum = sum*1e12
|
|
||||||
printf("ksin: sum*1e12=%.03f, %.03fus per 0-2047 cycle", sum, t)
|
|
||||||
|
|
||||||
sum = 0
|
|
||||||
local t = gv.gethiticks()
|
|
||||||
for i=0,N*2048-1 do
|
|
||||||
sum = sum+xmath.sinb(i)
|
|
||||||
end
|
|
||||||
t = gv.gethiticks()-t
|
|
||||||
sum = sum*1e12
|
|
||||||
printf("sinb: sum*1e12=%.03f, %.03fus per 0-2047 cycle", sum, t)
|
|
||||||
|
|
||||||
-- Results (helixhorned x86):
|
|
||||||
-- ksin: sum*1e12=0.000, 3.801us per 0-2047 cycle
|
|
||||||
-- sinb: sum*1e12=0.052, 2.886us per 0-2047 cycle
|
|
||||||
end
|
|
||||||
|
|
||||||
do
|
|
||||||
-- Test getflorzofslopeptr()/sector[]:floorzat()
|
|
||||||
local N = 100
|
|
||||||
local t = gv.gethiticks()
|
|
||||||
|
|
||||||
for n=1,N do
|
|
||||||
for i=0,gv.numsectors-1 do
|
|
||||||
local sec = sector[i]
|
|
||||||
local w1 = sec.wallptr
|
|
||||||
sec:floorzat(wall[w1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
printf("%d x %d floorzat: %.03f us", N, gv.numsectors, (gv.gethiticks()-t)*1000)
|
|
||||||
|
|
||||||
-- Results for 100 x 325 floorzat (helixhorned x86):
|
|
||||||
-- cdecl getflorzofslope(): 572.165 us
|
|
||||||
-- __fastcall getflorzofslope(): 830.147 us (sic, but tested only once!)
|
|
||||||
end
|
|
||||||
|
|
||||||
print('---=== END TEST SCRIPT ===---')
|
|
||||||
|
|
||||||
--[[
|
|
||||||
function check_sector_idx()
|
|
||||||
error("bla")
|
|
||||||
end
|
|
||||||
spritesofsect(0)
|
|
||||||
--]]
|
|
||||||
|
|
||||||
--DBG_.oom()
|
|
||||||
|
|
||||||
-- This will complain about wrong usage of 'error'. In particular,
|
|
||||||
-- the nil must not propagate to C!
|
|
||||||
checkfail('error(nil)', "error using 'error': error message must be a string")
|
|
||||||
|
|
||||||
local D = require("CON.DEFS")
|
|
||||||
checkfail('require("CON.DEFS").APLAYER=123', "modifying base module table forbidden")
|
|
||||||
-- Test with lunatic/test/rotfixed_actor.con.
|
|
||||||
print("DUKECAR="..tostring(D.DUKECAR))
|
|
||||||
|
|
||||||
do
|
|
||||||
print('---------- getangle test')
|
|
||||||
|
|
||||||
local function CreateGetAngFunc(roundfunc)
|
|
||||||
return function(x, y)
|
|
||||||
local ang = (1024/math.pi)*math.atan2(y, x) -- note the swapped args
|
|
||||||
return bit.band(roundfunc(ang), 2047)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local ourgetang = CreateGetAngFunc(math.ceil)
|
|
||||||
local ourgetangf = CreateGetAngFunc(math.floor)
|
|
||||||
|
|
||||||
local function printang(x, y)
|
|
||||||
printf('%4d,%4d: %13d, %16d, %16d', x, y,
|
|
||||||
gv.getangle(x, y), ourgetang(x, y), ourgetangf(x, y))
|
|
||||||
end
|
|
||||||
|
|
||||||
print " x, y: getangle(x, y) | math.atan2/ceil | math.atan2/floor"
|
|
||||||
printang(10, 100)
|
|
||||||
printang(10, -100)
|
|
||||||
printang(-10, -100)
|
|
||||||
printang(-10, 100)
|
|
||||||
|
|
||||||
printang(0, 0)
|
|
||||||
|
|
||||||
printang(1, 0)
|
|
||||||
printang(0, 1)
|
|
||||||
printang(-1, 0)
|
|
||||||
printang(0, -1)
|
|
||||||
|
|
||||||
local N=1e5
|
|
||||||
local r = 0
|
|
||||||
|
|
||||||
local t = gv.gethiticks()
|
|
||||||
for i=1,N do
|
|
||||||
r = r + gv.getangle(10, i)
|
|
||||||
end
|
|
||||||
local t1 = gv.gethiticks()-t
|
|
||||||
|
|
||||||
r = 0
|
|
||||||
t = gv.gethiticks()
|
|
||||||
for i=1,N do
|
|
||||||
r = r + ourgetang(10, i)
|
|
||||||
end
|
|
||||||
local t2 = gv.gethiticks()-t
|
|
||||||
|
|
||||||
printf('Time for %d runs: getangle: %.03f ms, math.atan2: %.03f ms', N, t1, t2)
|
|
||||||
print('----------')
|
|
||||||
end
|
|
||||||
|
|
||||||
if (LUNATIC_FIRST_TIME) then
|
|
||||||
require("test.shadexfog").test_create0()
|
|
||||||
end
|
|
|
@ -1,109 +0,0 @@
|
||||||
gamevar tmp 0 0
|
|
||||||
/*
|
|
||||||
actor LIZTROOP
|
|
||||||
getactor[THISACTOR].mdflags tmp
|
|
||||||
orvar tmp 16
|
|
||||||
setactor[THISACTOR].mdflags tmp
|
|
||||||
enda
|
|
||||||
*/
|
|
||||||
|
|
||||||
gamevar ceilz 0 0
|
|
||||||
gamevar ceilhit 0 0
|
|
||||||
gamevar florz 0 0
|
|
||||||
gamevar florhit 0 0
|
|
||||||
gamevar pi 0 0
|
|
||||||
|
|
||||||
gamevar doxp 0 0
|
|
||||||
gamevar xp 0 0
|
|
||||||
|
|
||||||
define Q 500
|
|
||||||
|
|
||||||
gamevar pdist 0 2
|
|
||||||
|
|
||||||
onevent EVENT_GAME
|
|
||||||
ifactor LIZTROOP
|
|
||||||
findplayer pdist
|
|
||||||
|
|
||||||
// Test both view/wall/floor sprites for
|
|
||||||
// spriteext[].xpanning (Polymost only)
|
|
||||||
setvar doxp 0
|
|
||||||
ifactor 937 // --- vvv --- E2L1
|
|
||||||
setvar doxp 1
|
|
||||||
else ifactor FEMMAG1
|
|
||||||
setvar doxp 1
|
|
||||||
else ifactor PIGCOP
|
|
||||||
setvar doxp 1
|
|
||||||
else ifactor 849 // 64x24: in classic, better than nonpow2-x tile 937
|
|
||||||
setvar doxp 1
|
|
||||||
|
|
||||||
ifvarn doxp 0
|
|
||||||
{
|
|
||||||
getactor[THISACTOR].xpanning xp
|
|
||||||
addvar xp 1
|
|
||||||
setactor[THISACTOR].xpanning xp
|
|
||||||
}
|
|
||||||
|
|
||||||
getwall[0].blend xp // test wall[].blend
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_ANIMATESPRITES
|
|
||||||
ifactor LIZTROOP
|
|
||||||
{
|
|
||||||
setvarvar tmp totalclock
|
|
||||||
andvar tmp 255
|
|
||||||
settspr[THISACTOR].tsprshade tmp
|
|
||||||
}
|
|
||||||
endevent
|
|
||||||
|
|
||||||
gamevar lizi 0 0
|
|
||||||
|
|
||||||
onevent EVENT_DISPLAYREST
|
|
||||||
findnearactor LIZTROOP 4096 lizi
|
|
||||||
ifvarn lizi -1
|
|
||||||
{
|
|
||||||
redefinequote Q nearest LIZTROOP is %d units away (Manhattan dist.)
|
|
||||||
qsprintf Q Q /**/ actorvar[lizi].pdist
|
|
||||||
gametextz STARTALPHANUM 10 100 Q 2 0 16 /*bounds:*/ 0 0 xdim ydim 32768
|
|
||||||
}
|
|
||||||
|
|
||||||
getplayer[THISACTOR].i pi
|
|
||||||
// This blows up on C-CON:
|
|
||||||
getzrange sprite[pi].x sprite[pi].y sprite[pi].z player[THISACTOR].cursectnum
|
|
||||||
/*out:*/ ceilz ceilhit florz florhit
|
|
||||||
/*in:*/ 128 CLIPMASK0
|
|
||||||
|
|
||||||
redefinequote Q hit %d at %d, %d at %d
|
|
||||||
qsprintf Q Q /**/ ceilhit ceilz florhit florz
|
|
||||||
minitext 100 8 Q 0 0
|
|
||||||
gametext STARTALPHANUM 320 16 Q 0 0 16 /*bounds:*/ 0 0 xdim ydim
|
|
||||||
gametextz STARTALPHANUM 320 24 Q 0 0 16 /*bounds:*/ 0 0 xdim ydim 32768
|
|
||||||
endevent
|
|
||||||
|
|
||||||
|
|
||||||
// Following code by Fox, from
|
|
||||||
// http://forums.duke4.net/topic/955-eduke32-scripting/page__view__findpost__p__153176
|
|
||||||
|
|
||||||
gamevar SIN 0 0
|
|
||||||
gamevar COS 0 0
|
|
||||||
gamevar HORIZ 0 0
|
|
||||||
gamevar HITSECT 0 0
|
|
||||||
gamevar HITWALL 0 0
|
|
||||||
gamevar HITSPRITE 0 0
|
|
||||||
gamevar HITX 0 0
|
|
||||||
gamevar HITY 0 0
|
|
||||||
gamevar HITZ 0 0
|
|
||||||
|
|
||||||
onevent EVENT_DISPLAYREST
|
|
||||||
sin SIN player[THISACTOR].ang
|
|
||||||
cos COS player[THISACTOR].ang
|
|
||||||
setvar HORIZ 100
|
|
||||||
subvarvar HORIZ player[THISACTOR].horiz
|
|
||||||
subvarvar HORIZ player[THISACTOR].horizoff
|
|
||||||
mulvar HORIZ 32
|
|
||||||
hitscan player[THISACTOR].posx player[THISACTOR].posy player[THISACTOR].posz player[THISACTOR].cursectnum
|
|
||||||
COS SIN HORIZ HITSECT HITWALL HITSPRITE HITX HITY HITZ CLIPMASK1
|
|
||||||
|
|
||||||
redefinequote Q hitscan: hit sector %d, wall %d, sprite %d
|
|
||||||
qsprintf Q Q /**/ HITSECT HITWALL HITSPRITE
|
|
||||||
minitext 100 80 Q 0 0
|
|
||||||
endevent
|
|
|
@ -1,86 +0,0 @@
|
||||||
|
|
||||||
// Inputs to "state checknearwall".
|
|
||||||
gamevar wd 128 0 // wall distance
|
|
||||||
gamevar cfd 1024 0 // ceiling/floor distance
|
|
||||||
|
|
||||||
// Outputs of "state checknearwall".
|
|
||||||
gamevar hitwall -1 0
|
|
||||||
|
|
||||||
// Temporary variables.
|
|
||||||
gamevar tmp 0 0
|
|
||||||
gamevar ret 0 0
|
|
||||||
gamevar x 0 0
|
|
||||||
gamevar y 0 0
|
|
||||||
gamevar z 0 0
|
|
||||||
gamevar sectnum 0 0
|
|
||||||
|
|
||||||
define CM_BLK_WAL 1
|
|
||||||
|
|
||||||
state getcoords
|
|
||||||
setvarvar x sprite[THISACTOR].x
|
|
||||||
setvarvar y sprite[THISACTOR].y
|
|
||||||
setvarvar z sprite[THISACTOR].z
|
|
||||||
setvarvar sectnum sprite[THISACTOR].sectnum
|
|
||||||
ends
|
|
||||||
|
|
||||||
// Inputs: (wd, cfd)
|
|
||||||
// Returns: hitwall
|
|
||||||
// -1: hit no wall
|
|
||||||
// >=0: hit that wall
|
|
||||||
state checknearwall
|
|
||||||
setvar hitwall -1
|
|
||||||
|
|
||||||
setvarvar tmp wd, shiftvarl tmp 14
|
|
||||||
|
|
||||||
state getcoords
|
|
||||||
clipmovenoslide ret (x, y, z, sectnum) (tmp, 0) (wd, cfd, cfd) CM_BLK_WAL
|
|
||||||
ifvarn ret 0 { setvarvar hitwall ret, subvar hitwall 32768, break }
|
|
||||||
|
|
||||||
state getcoords
|
|
||||||
clipmovenoslide ret (x, y, z, sectnum) (0, tmp) (wd, cfd, cfd) CM_BLK_WAL
|
|
||||||
ifvarn ret 0 { setvarvar hitwall ret, subvar hitwall 32768, break }
|
|
||||||
|
|
||||||
mulvar tmp -1
|
|
||||||
|
|
||||||
state getcoords
|
|
||||||
clipmovenoslide ret (x, y, z, sectnum) (tmp, 0) (wd, cfd, cfd) CM_BLK_WAL
|
|
||||||
ifvarn ret 0 { setvarvar hitwall ret, subvar hitwall 32768, break }
|
|
||||||
|
|
||||||
state getcoords
|
|
||||||
clipmovenoslide ret (x, y, z, sectnum) (0, tmp) (wd, cfd, cfd) CM_BLK_WAL
|
|
||||||
ifvarn ret 0 { setvarvar hitwall ret, subvar hitwall 32768, break }
|
|
||||||
ends
|
|
||||||
|
|
||||||
////////// Example usage //////////
|
|
||||||
|
|
||||||
onevent EVENT_PROCESSINPUT
|
|
||||||
state checknearwall
|
|
||||||
endevent
|
|
||||||
|
|
||||||
|
|
||||||
define Q_tmp 500
|
|
||||||
definequote Q_tmp <write on me>
|
|
||||||
|
|
||||||
define Q_no 501
|
|
||||||
definequote Q_no Hit no wall
|
|
||||||
|
|
||||||
define Q_yes 502
|
|
||||||
definequote Q_yes Hit wall %d
|
|
||||||
|
|
||||||
define Q_info 503
|
|
||||||
definequote Q_info pos: %d %d %d sect: %d
|
|
||||||
|
|
||||||
onevent EVENT_DISPLAYREST
|
|
||||||
qsprintf Q_tmp Q_info, x y z sectnum
|
|
||||||
minitext (100, 8) Q_tmp (0, 0)
|
|
||||||
|
|
||||||
ifvarn hitwall -1
|
|
||||||
{
|
|
||||||
qsprintf Q_tmp Q_yes hitwall
|
|
||||||
minitext (100, 16) Q_tmp (0, 0)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
minitext (100, 16) Q_no (0, 0)
|
|
||||||
}
|
|
||||||
endevent
|
|
|
@ -1,110 +0,0 @@
|
||||||
|
|
||||||
local bit = require("bit")
|
|
||||||
local band = bit.band
|
|
||||||
|
|
||||||
local tostring = tostring
|
|
||||||
|
|
||||||
local gv = gv
|
|
||||||
local actor = actor
|
|
||||||
local sector, wall, sprite = sector, wall, sprite
|
|
||||||
|
|
||||||
local printf = printf
|
|
||||||
local sectorsofbunch = sectorsofbunch
|
|
||||||
|
|
||||||
local con = require("con")
|
|
||||||
|
|
||||||
local D = require("CON.DEFS")
|
|
||||||
|
|
||||||
----------
|
|
||||||
|
|
||||||
local TROR_GLASSBREAKER = 2959 -- red 'T'
|
|
||||||
-- Actor controlling the timing of a TROR hplane breaking.
|
|
||||||
gameactor
|
|
||||||
{
|
|
||||||
TROR_GLASSBREAKER,
|
|
||||||
|
|
||||||
function(aci, pli, dist)
|
|
||||||
local spr = sprite[aci]
|
|
||||||
if (not (spr.lotag == 712 and spr.hitag == 119)) then -- check BREAKER_MAGIC
|
|
||||||
sprite.changestat(aci, actor.STAT.DEFAULT)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local cnt = actor[aci]:get_count()
|
|
||||||
local finish = (cnt >= 6)
|
|
||||||
|
|
||||||
if (cnt == 0) then
|
|
||||||
-- NOTE: INTERNAL interface, DON'T USE!
|
|
||||||
con._sound(aci, D.GLASS_BREAKING)
|
|
||||||
end
|
|
||||||
|
|
||||||
local bunchnum = spr.extra
|
|
||||||
for sectnum, what in sectorsofbunch(bunchnum, gv.BOTH_CF) do
|
|
||||||
local cf = sector[sectnum][what]
|
|
||||||
cf:set_picnum(D.GLASS2 + cnt)
|
|
||||||
cf.statbits:clear(sector.STAT.BLOCK + sector.STAT.HITSCAN)
|
|
||||||
if (finish) then
|
|
||||||
cf.statbits:clear(sector.STAT.TRANS_BITMASK)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (finish) then
|
|
||||||
con.killit()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
local DHP = sector.DAMAGEHPLANE
|
|
||||||
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"DAMAGEHPLANE",
|
|
||||||
|
|
||||||
function(aci, pli, RETURN)
|
|
||||||
local what, sectnum = sector.damagehplane_whatsect(RETURN)
|
|
||||||
local sec = sector[sectnum]
|
|
||||||
|
|
||||||
-- Part I: make various screens breakable when it's a ceiling picnum.
|
|
||||||
if (what == "ceiling") then
|
|
||||||
-- hit ceiling
|
|
||||||
if (sec.ceilingpicnum >= 263 and sec.ceilingpicnum <= 275) then
|
|
||||||
sec:set_ceilingpicnum(D.W_SCREENBREAK + gv.krand()%3)
|
|
||||||
gv.RETURN = DHP.GLASSBREAK
|
|
||||||
return con.longjmp()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
gv.RETURN = DHP.DEFAULT
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"DAMAGEHPLANE",
|
|
||||||
|
|
||||||
function(aci, pli, RETURN)
|
|
||||||
local what, sectnum = sector.damagehplane_whatsect(RETURN)
|
|
||||||
local sec = sector[sectnum]
|
|
||||||
|
|
||||||
-- Part II: breakable TROR hplanes
|
|
||||||
local cf = sec[what]
|
|
||||||
-- printf("damage %s of sector %d (pic %d, bunch %d, hittable: %s)", what, sectnum,
|
|
||||||
-- cf.picnum, cf.bunch, tostring(cf.statbits:test(sector.STAT.HITSCAN)))
|
|
||||||
|
|
||||||
if (cf.bunch >= 0 and (cf.picnum==198 or cf.picnum==D.GLASS2) and
|
|
||||||
cf.statbits:test(sector.STAT.HITSCAN)) then
|
|
||||||
local bi = con.insertsprite(TROR_GLASSBREAKER, wall[sec.wallptr], sectnum, actor.STAT.ACTOR)
|
|
||||||
local breaker = sprite[bi]
|
|
||||||
|
|
||||||
breaker.cstat = sprite.CSTAT.INVISIBLE
|
|
||||||
breaker.lotag, breaker.hitag = 712, 119 -- BREAKER_MAGIC
|
|
||||||
breaker.extra = cf.bunch
|
|
||||||
|
|
||||||
gv.RETURN = DHP.SUPPRESS
|
|
||||||
return con.longjmp()
|
|
||||||
end
|
|
||||||
|
|
||||||
gv.RETURN = DHP.DEFAULT
|
|
||||||
end
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
// In CON, the dangling else ambiguity is resolved in a very peculiar way:
|
|
||||||
// A cascade "ifP1 ifP2 ifP3 Stmt1 ... else Stmt2" is considered as if the
|
|
||||||
// predicates P1, P2, P3, ... are combined with a logical AND.
|
|
||||||
// So effectively, there is no ambiguity at all, and one of Stmt1 and Stmt2
|
|
||||||
// is guaranteed to be taken.
|
|
||||||
|
|
||||||
define Q 114
|
|
||||||
define GLOBAL 0
|
|
||||||
|
|
||||||
gamevar x 0 GLOBAL
|
|
||||||
// divisible by 2, 3, 5?
|
|
||||||
gamevar xm2 0 GLOBAL
|
|
||||||
gamevar xm3 0 GLOBAL
|
|
||||||
gamevar xm5 0 GLOBAL
|
|
||||||
|
|
||||||
state testelse
|
|
||||||
setvarvar xm2 x, modvar xm2 2
|
|
||||||
setvarvar xm3 x, modvar xm3 3
|
|
||||||
setvarvar xm5 x, modvar xm5 5
|
|
||||||
|
|
||||||
redefinequote Q ERROR: one path of if/else must be taken
|
|
||||||
|
|
||||||
ifvarn xm2 0
|
|
||||||
ifvarn xm3 0
|
|
||||||
ifvarn xm5 0
|
|
||||||
redefinequote Q x=%d is not divisible by either 2, 3 or 5
|
|
||||||
else
|
|
||||||
redefinequote Q x=%d is divisible by either 2, 3 or 5
|
|
||||||
|
|
||||||
qsprintf Q Q x
|
|
||||||
userquote Q
|
|
||||||
ends
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
setvar x 0
|
|
||||||
whilevarn x 31
|
|
||||||
{
|
|
||||||
state testelse
|
|
||||||
addvar x 1
|
|
||||||
}
|
|
||||||
endevent
|
|
||||||
|
|
||||||
// Test deferred code. This CON code doesn't make much sense, but check out
|
|
||||||
// the generated Lua code...
|
|
||||||
onevent EVENT_JUMP
|
|
||||||
ifpdistl 100 ifpdistg 0 ifcanseetarget
|
|
||||||
{
|
|
||||||
palfrom 32 32 32
|
|
||||||
ifcanseetarget
|
|
||||||
quote 29
|
|
||||||
ifcanseetarget
|
|
||||||
quote 30
|
|
||||||
setvar x 0
|
|
||||||
state testelse
|
|
||||||
}
|
|
||||||
else ifcanseetarget
|
|
||||||
quote 31
|
|
||||||
else ifvare 1 1
|
|
||||||
{
|
|
||||||
// Test empty brace-enclosed statement under if condition.
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
quote Q
|
|
||||||
}
|
|
||||||
endevent
|
|
|
@ -1,97 +0,0 @@
|
||||||
|
|
||||||
local require = require
|
|
||||||
|
|
||||||
local string = require("string")
|
|
||||||
local con = require("con")
|
|
||||||
local xmath = require("xmath")
|
|
||||||
|
|
||||||
local gv = gv
|
|
||||||
local sprite = sprite
|
|
||||||
local actor = actor
|
|
||||||
local player = player
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
|
|
||||||
local printf = printf
|
|
||||||
local gameevent = gameevent
|
|
||||||
local spritesofstat = spritesofstat
|
|
||||||
|
|
||||||
local Inf = 0/1
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
-- Insert MUSICANDSFX? (Delete it otherwise.)
|
|
||||||
insp = false
|
|
||||||
|
|
||||||
-- Hitag and lotag of last deleted MUSICANDSFX sprite.
|
|
||||||
tag = {}
|
|
||||||
tag.hi, tag.lo = 0, 0
|
|
||||||
|
|
||||||
-- Preliminary dummy of a local gamevar.
|
|
||||||
local ournumjumps = 0
|
|
||||||
|
|
||||||
local lastv = xmath.vec3()
|
|
||||||
ilastv = xmath.ivec3()
|
|
||||||
|
|
||||||
require "end_gamevars"
|
|
||||||
|
|
||||||
-- We may cache globals defined in the gamevar section afterwards, but not
|
|
||||||
-- refer to locals defined prior to the gamevar section in it.
|
|
||||||
local tag = tag
|
|
||||||
|
|
||||||
local D = require("CON.DEFS")
|
|
||||||
|
|
||||||
|
|
||||||
gameevent{"JUMP", actor.FLAGS.chain_beg,
|
|
||||||
function(aci, pli)
|
|
||||||
local ps = player[pli]
|
|
||||||
|
|
||||||
ournumjumps = ournumjumps+1
|
|
||||||
|
|
||||||
if (insp) then
|
|
||||||
-- Insert MUSICANDSFX sprite with same lo-/hitag as last deleted one.
|
|
||||||
printf("delmusicsfx: jump count=%d, inserting", ournumjumps)
|
|
||||||
|
|
||||||
local spr = sprite[con.spawn(D.MUSICANDSFX, aci)]
|
|
||||||
spr.lotag, spr.hitag = tag.lo, tag.hi
|
|
||||||
else
|
|
||||||
-- Delete nearest MUSICANDSFX sprite.
|
|
||||||
|
|
||||||
local nearestdst = Inf
|
|
||||||
local nearesti = -1
|
|
||||||
|
|
||||||
for i in spritesofstat(actor.STAT.FX) do
|
|
||||||
local dst = (sprite[i]-ps.pos):len2()
|
|
||||||
if (nearesti == -1 or (dst < nearestdst and dst < sprite[i].hitag)) then
|
|
||||||
printf("MSFX %d dist %d", i, dst)
|
|
||||||
nearesti = i
|
|
||||||
nearestdst = dst
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (nearesti >= 0) then
|
|
||||||
local spr = sprite[nearesti]
|
|
||||||
tag.lo, tag.hi = spr.lotag, spr.hitag
|
|
||||||
lastv.x, lastv.y, lastv.z = spr.x, spr.y, spr.z+0.5
|
|
||||||
ilastv.x, ilastv.y, ilastv.z = spr.x, spr.y, spr.z
|
|
||||||
actor.delete(nearesti)
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(nearesti < 0 or sprite[nearesti].picnum==D.MUSICANDSFX)
|
|
||||||
printf("delmusicsfx: jump count=%d, deleting sprite %d", ournumjumps, nearesti)
|
|
||||||
end
|
|
||||||
|
|
||||||
insp = not insp
|
|
||||||
end}
|
|
||||||
|
|
||||||
-- Display the number of times we jumped on the screen.
|
|
||||||
gameevent
|
|
||||||
{
|
|
||||||
"DISPLAYREST",
|
|
||||||
|
|
||||||
function()
|
|
||||||
con.minitext(240, 10, string.format("jumped %d times", ournumjumps))
|
|
||||||
end
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
|
|
||||||
actor 1890 1
|
|
||||||
// The following "else { if" must not be translated to an "elseif".
|
|
||||||
ifcount 1 nullop else
|
|
||||||
{
|
|
||||||
ifvare 0 0
|
|
||||||
spawn 1
|
|
||||||
else
|
|
||||||
spawn 2
|
|
||||||
|
|
||||||
ifvare 1 1
|
|
||||||
spawn 3
|
|
||||||
else ifvare 1 2
|
|
||||||
spawn 4
|
|
||||||
}
|
|
||||||
enda
|
|
|
@ -1,48 +0,0 @@
|
||||||
definequote 125 FIRST
|
|
||||||
definequote 126 SECOND
|
|
||||||
definequote 127 THIRD
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
echo 125
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
echo 126
|
|
||||||
break
|
|
||||||
echo 126
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
echo 127
|
|
||||||
endevent
|
|
||||||
|
|
||||||
eventloadactor 930 // "police line" ribbon
|
|
||||||
// make non-destroyable
|
|
||||||
setactor[THISACTOR].hitag 0
|
|
||||||
enda
|
|
||||||
|
|
||||||
eventloadactor 2491 // DUKECAR
|
|
||||||
killit
|
|
||||||
enda
|
|
||||||
|
|
||||||
/*
|
|
||||||
Outputs:
|
|
||||||
========
|
|
||||||
|
|
||||||
C-CON:
|
|
||||||
------
|
|
||||||
(Since r5097:)
|
|
||||||
THIRD
|
|
||||||
SECOND
|
|
||||||
FIRST
|
|
||||||
(Before r5097:)
|
|
||||||
THIRD
|
|
||||||
SECOND
|
|
||||||
|
|
||||||
LunaCON:
|
|
||||||
--------
|
|
||||||
THIRD
|
|
||||||
SECOND
|
|
||||||
FIRST
|
|
||||||
|
|
||||||
*/
|
|
|
@ -1,151 +0,0 @@
|
||||||
// Test for gamearrays / their persistence.
|
|
||||||
|
|
||||||
define QWESZ 10
|
|
||||||
define ASDSZ 12
|
|
||||||
|
|
||||||
define MAXTILES 30720 // XXX
|
|
||||||
|
|
||||||
gamearray qwe QWESZ
|
|
||||||
gamearray asd ASDSZ
|
|
||||||
|
|
||||||
define NEWQWESZ 9
|
|
||||||
define NEWASDSZ 14
|
|
||||||
|
|
||||||
gamearray qwe1 1 // should auto-resize when reading in
|
|
||||||
gamearray asd1 NEWASDSZ
|
|
||||||
|
|
||||||
gamevar qwesz 0 0
|
|
||||||
gamevar asdsz 0 0
|
|
||||||
|
|
||||||
// Test gamevars that are named like Lua keywords.
|
|
||||||
gamevar local 0 0
|
|
||||||
|
|
||||||
gamevar i 0 0
|
|
||||||
gamevar tmp 0 0
|
|
||||||
|
|
||||||
define Q_qwe_fn 400
|
|
||||||
define Q_asd_fn 401
|
|
||||||
definequote Q_qwe_fn LTEST_QWE_ARRAY.bin
|
|
||||||
definequote Q_asd_fn LTEST_ASD_ARRAY.bin
|
|
||||||
|
|
||||||
// Wouldn't run in C-CON otherwise; redefinequote/qsprintf errors out when
|
|
||||||
// passed a non-allocated quote as destination.
|
|
||||||
definequote 500 TEMP
|
|
||||||
definequote 501 TEMP
|
|
||||||
|
|
||||||
definequote 505 tile y sizes: 0:%d 1:%d 2:%d 3:%d 4:%d 5:%d 6:%d 7:%d 8:%d
|
|
||||||
definequote 506 1: 0x0a0a0d0d==168430861 ? %d
|
|
||||||
definequote 507 2: 0x0a0a0d0d==168430861 ? %d
|
|
||||||
|
|
||||||
gamevar MINUSONE -1 0
|
|
||||||
|
|
||||||
state error
|
|
||||||
setactor[MINUSONE].cstat 0
|
|
||||||
|
|
||||||
// This doesn't work in C-CON:
|
|
||||||
// setactor[-1].cstat 0
|
|
||||||
// error: symbol `1' is not a game variable.
|
|
||||||
ends
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
setvar i 0
|
|
||||||
whilevarn i QWESZ
|
|
||||||
{
|
|
||||||
setarray qwe[i] i
|
|
||||||
addvar i 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test nested array syntax:
|
|
||||||
setarray qwe[qwe[5]] 0
|
|
||||||
|
|
||||||
// qwe[] now:
|
|
||||||
// 0 1 2 3 4 0 6 7 8 9
|
|
||||||
|
|
||||||
setvar i 0
|
|
||||||
whilevarn i ASDSZ
|
|
||||||
{
|
|
||||||
setvarvar tmp i
|
|
||||||
addvar tmp 100
|
|
||||||
setarray asd[i] tmp
|
|
||||||
setvarvar local tmp
|
|
||||||
addvar i 1
|
|
||||||
}
|
|
||||||
|
|
||||||
copy qwe[4] /*->*/ asd[3] 3
|
|
||||||
|
|
||||||
setarray asd[4] 0x0a0a0d0d
|
|
||||||
|
|
||||||
// asd[] now:
|
|
||||||
// 100 101 102 4 0x0a0a0d0d 6 106 107 108 109 110 111
|
|
||||||
|
|
||||||
qsprintf 506 506 asd[4]
|
|
||||||
userquote 506
|
|
||||||
|
|
||||||
resizearray qwe NEWQWESZ // shrink
|
|
||||||
resizearray asd NEWASDSZ // grow
|
|
||||||
|
|
||||||
ifvarn qwe[4] 4 state error
|
|
||||||
ifvarn asd[4] 0x0a0a0d0d state error
|
|
||||||
|
|
||||||
writearraytofile qwe Q_qwe_fn
|
|
||||||
writearraytofile asd Q_asd_fn
|
|
||||||
|
|
||||||
readarrayfromfile qwe1 Q_qwe_fn
|
|
||||||
readarrayfromfile asd1 Q_asd_fn
|
|
||||||
|
|
||||||
qsprintf 507 507 asd[4]
|
|
||||||
userquote 507
|
|
||||||
|
|
||||||
ifvarn asd[4] 0x0a0a0d0d state error
|
|
||||||
|
|
||||||
// test array sizes
|
|
||||||
getarraysize qwe qwesz
|
|
||||||
getarraysize asd asdsz
|
|
||||||
|
|
||||||
ifvarn qwesz NEWQWESZ state error
|
|
||||||
ifvarn asdsz NEWASDSZ state error
|
|
||||||
|
|
||||||
// test array sizes with *1 arrays
|
|
||||||
getarraysize qwe1 qwesz
|
|
||||||
getarraysize asd1 asdsz
|
|
||||||
|
|
||||||
ifvarn qwesz NEWQWESZ state error
|
|
||||||
ifvarn asdsz NEWASDSZ state error
|
|
||||||
|
|
||||||
redefinequote 500 [%d]: qwe=%d, qwe1=%d, asd=%d, asd1=%d
|
|
||||||
redefinequote 501 Because C-CON can't qsprintf into an uninitialized one
|
|
||||||
|
|
||||||
setvar i 0
|
|
||||||
whilevarvarn i -1
|
|
||||||
{
|
|
||||||
ifvarl i NEWQWESZ
|
|
||||||
qsprintf 501 500 i qwe[i] qwe1[i] asd[i] asd1[i]
|
|
||||||
else
|
|
||||||
qsprintf 501 500 i -1 -1 asd[i] asd1[i]
|
|
||||||
userquote 501
|
|
||||||
addvar i 1
|
|
||||||
|
|
||||||
ifvare i NEWASDSZ
|
|
||||||
setvar i -1
|
|
||||||
}
|
|
||||||
|
|
||||||
redefinequote 500 MUST REACH
|
|
||||||
userquote 500
|
|
||||||
|
|
||||||
setvar i 120
|
|
||||||
modvar i 25
|
|
||||||
addlogvar i
|
|
||||||
|
|
||||||
|
|
||||||
//// Test system gamearrays
|
|
||||||
|
|
||||||
getarraysize tilesizx qwesz
|
|
||||||
ifvarn qwesz MAXTILES state error
|
|
||||||
|
|
||||||
copy tilesizy[0] /*->*/ qwe[0] 9
|
|
||||||
qsprintf 505 505 qwe[0] qwe[1] qwe[2] qwe[3] qwe[4] qwe[5] qwe[6] qwe[7] qwe[8]
|
|
||||||
userquote 505
|
|
||||||
|
|
||||||
// Must give a translation error:
|
|
||||||
// resizearray tilesizy 100
|
|
||||||
endevent
|
|
|
@ -1,94 +0,0 @@
|
||||||
// From http://wiki.eduke32.com/wiki/How_to_make_a_clock (author: The Commander)
|
|
||||||
// with modifications
|
|
||||||
|
|
||||||
gamevar sec 0 0
|
|
||||||
gamevar min 0 0
|
|
||||||
gamevar hour 0 0
|
|
||||||
gamevar mday 0 0
|
|
||||||
gamevar mon 0 0
|
|
||||||
gamevar year 0 0
|
|
||||||
gamevar wday 0 0
|
|
||||||
gamevar yday 0 0
|
|
||||||
|
|
||||||
onevent EVENT_DISPLAYREST
|
|
||||||
gettimedate sec min hour mday mon year wday yday
|
|
||||||
|
|
||||||
ifvarl hour 20 ifvarg hour 9
|
|
||||||
digitalnumberz 2992 285 16 hour 0 0 0 0 0 xdim ydim 65536
|
|
||||||
|
|
||||||
ifvarg hour 19
|
|
||||||
digitalnumberz 2992 282 16 hour 0 0 0 0 0 xdim ydim 65536
|
|
||||||
|
|
||||||
ifvarl hour 10
|
|
||||||
{
|
|
||||||
digitalnumberz 2992 288 16 hour 0 0 0 0 0 xdim ydim 65536
|
|
||||||
rotatesprite 269 16 65536 0 2992 0 0 0 0 0 xdim ydim
|
|
||||||
}
|
|
||||||
|
|
||||||
ifvarg min 9
|
|
||||||
digitalnumberz 2992 308 16 min 0 0 0 0 0 xdim ydim 65536
|
|
||||||
|
|
||||||
ifvarl min 10
|
|
||||||
{
|
|
||||||
digitalnumberz 2992 315 16 min 0 0 0 0 0 xdim ydim 65536
|
|
||||||
rotatesprite 296 16 65536 0 2992 0 0 0 0 0 xdim ydim
|
|
||||||
}
|
|
||||||
|
|
||||||
gametextz STARTALPHANUM 20 20 1 0 0 0 0 0 xdim ydim 32768
|
|
||||||
endevent
|
|
||||||
|
|
||||||
|
|
||||||
// Following code by Hendricks266, from
|
|
||||||
// http://forums.duke4.net/topic/1093-what-are-you-working-on-for-duke-right-now/page__view__findpost__p__152626
|
|
||||||
|
|
||||||
gamevar zoom 0 0
|
|
||||||
|
|
||||||
definequote 10000 abcdefghijklm
|
|
||||||
definequote 10001 nopqrstuvwxyz
|
|
||||||
|
|
||||||
onevent EVENT_DISPLAYREST
|
|
||||||
setvarvar zoom totalclock
|
|
||||||
modvar zoom 240
|
|
||||||
mulvar zoom 8192
|
|
||||||
divvar zoom 15
|
|
||||||
|
|
||||||
digitalnumberz DIGITALNUM 160 5 1234567890 0 0 16 windowx1 windowy1 windowx2 windowy2 zoom
|
|
||||||
|
|
||||||
gametextz STARTALPHANUM 320 30 10000 0 0 16 windowx1 windowy1 windowx2 windowy2 zoom
|
|
||||||
|
|
||||||
rotatesprite 160 70 zoom 0 SPINNINGNUKEICON 0 0 0 windowx1 windowy1 windowx2 windowy2
|
|
||||||
endevent
|
|
||||||
|
|
||||||
|
|
||||||
// Following code from the wiki 'showview' entry, with modifications
|
|
||||||
gamevar x 0 0
|
|
||||||
gamevar y 0 0
|
|
||||||
gamevar z 0 0
|
|
||||||
gamevar ang 0 0
|
|
||||||
gamevar horiz 0 0
|
|
||||||
gamevar temp 0 0
|
|
||||||
gamevar sect 0 0
|
|
||||||
|
|
||||||
onevent EVENT_DISPLAYREST
|
|
||||||
getplayer[THISACTOR].posx x
|
|
||||||
getplayer[THISACTOR].posy y
|
|
||||||
getplayer[THISACTOR].posz z
|
|
||||||
getplayer[THISACTOR].ang ang
|
|
||||||
getplayer[THISACTOR].horiz horiz
|
|
||||||
getplayer[THISACTOR].horizoff temp
|
|
||||||
addvarvar horiz temp
|
|
||||||
|
|
||||||
// Head cam.
|
|
||||||
subvar z 16384
|
|
||||||
updatesectorz x y z sect
|
|
||||||
// This upper view should be a bit wider.
|
|
||||||
ifvarg sect -1
|
|
||||||
showviewunbiased x y z ang horiz sect 16 8 156 39
|
|
||||||
|
|
||||||
// Groin cam.
|
|
||||||
addvar z 16384
|
|
||||||
addvar z 4096
|
|
||||||
updatesectorz x y z sect
|
|
||||||
ifvarg sect -1
|
|
||||||
showview x y z ang horiz sect 16 39 156 70
|
|
||||||
endevent
|
|
|
@ -1,27 +0,0 @@
|
||||||
definegametype 0 46051 Cooperative, not Dukematch
|
|
||||||
|
|
||||||
// NOTE: we need to define the rest anew too (or at least #4), because
|
|
||||||
// 'definegametype' does "g_numGametypes = idx+1", as opposed to
|
|
||||||
// "g_numGametypes = max(g_numGametypes, idx+1)".
|
|
||||||
definegametype 1 46051 Cooperative
|
|
||||||
definegametype 2 16410 Dukematch (no spawn)
|
|
||||||
definegametype 3 216088 Team Dukematch
|
|
||||||
definegametype 4 213018 Team Dukematch (no spawn)
|
|
||||||
|
|
||||||
gamevar temp 0 0
|
|
||||||
|
|
||||||
// Original version by Fox, from
|
|
||||||
// http://forums.duke4.net/topic/775-eduke32-20-and-polymer/page__view__findpost__p__183890
|
|
||||||
onevent EVENT_DISPLAYREST
|
|
||||||
// Notes:
|
|
||||||
// - index (THISACTOR) is never used.
|
|
||||||
// - incorrect if screenpeek != myconnectindex, e.g. by pressing [K]:
|
|
||||||
// - all three are equivalent:
|
|
||||||
getuserdef[THISACTOR].statusbarscale temp
|
|
||||||
getuserdef .statusbarscale temp
|
|
||||||
getuserdef[].statusbarscale temp
|
|
||||||
|
|
||||||
redefinequote 0 %ld
|
|
||||||
qsprintf 0 0 temp
|
|
||||||
gametext STARTALPHANUM 10 10 0 0 0 0 0 0 xdim ydim
|
|
||||||
endevent
|
|
|
@ -1,137 +0,0 @@
|
||||||
|
|
||||||
-- A spawner (NUKEBUTTON+3) of colored TRANSPORTERSTAR+4 sprites in a helical
|
|
||||||
-- arrangement.
|
|
||||||
|
|
||||||
local require = require
|
|
||||||
local math = require("math")
|
|
||||||
|
|
||||||
local con = require("con")
|
|
||||||
local xmath = require("xmath")
|
|
||||||
|
|
||||||
local gv = gv
|
|
||||||
local actor = actor
|
|
||||||
local player = player
|
|
||||||
local sprite = sprite
|
|
||||||
|
|
||||||
local gameactor = gameactor
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
-- Dual-typed per-actor array: false if a broken nuke switch is not enabled,
|
|
||||||
-- start game tic of when it was enabled otherwise.
|
|
||||||
-- NOTE: for objects that are not supposed to be deleted such as this one, it
|
|
||||||
-- would also be OK to use a plain table.
|
|
||||||
local nukeswStart = con.actorvar(false)
|
|
||||||
|
|
||||||
-- This one should be a per-actor variable because it holds info about
|
|
||||||
-- "volatile" actors.
|
|
||||||
local starPal = con.actorvar(0)
|
|
||||||
|
|
||||||
-- Color per decasecond, can be changed from outside.
|
|
||||||
COLOR = { 1, 2, 6, 7, 8 }
|
|
||||||
|
|
||||||
require("end_gamevars")
|
|
||||||
|
|
||||||
|
|
||||||
local bangvec = xmath.bangvec
|
|
||||||
local angvec = xmath.angvec
|
|
||||||
|
|
||||||
|
|
||||||
local D = require("CON.DEFS")
|
|
||||||
local GTICSPERSEC = gv.GTICSPERSEC
|
|
||||||
|
|
||||||
gameactor
|
|
||||||
{
|
|
||||||
D.TRANSPORTERSTAR+4,
|
|
||||||
|
|
||||||
flags = actor.FLAGS.NOCLIP,
|
|
||||||
|
|
||||||
move = con.move{100},
|
|
||||||
movflags = actor.MOVFLAGS.geth,
|
|
||||||
|
|
||||||
func = function(aci)
|
|
||||||
local spr = sprite[aci]
|
|
||||||
-- NOTE: this is prettier than calling it 'a', even if 'act' is used to
|
|
||||||
-- denote an action in other places:
|
|
||||||
local act = actor[aci]
|
|
||||||
|
|
||||||
if (act:has_action(0)) then
|
|
||||||
act:set_action(1) -- TODO: actor constructors, i.e. 'init' callbacks
|
|
||||||
|
|
||||||
local decasec = math.floor((gv.gametic - nukeswStart[spr.owner])/(GTICSPERSEC*10)) % 12
|
|
||||||
|
|
||||||
local pal = COLOR[decasec+1]
|
|
||||||
if (pal ~= nil) then
|
|
||||||
starPal[aci] = pal
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Every 2nd minute, we stop coloring the spawned stars. This tests
|
|
||||||
-- per-actor variable resetting to the default value.
|
|
||||||
spr.pal = starPal[aci]
|
|
||||||
end
|
|
||||||
|
|
||||||
if (act:checkbump()) then
|
|
||||||
con.killit()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Test spr:changesect() vs. sprite.changesect()
|
|
||||||
local sectnum = spr.sectnum
|
|
||||||
|
|
||||||
for i=0,gv.numsectors-1 do
|
|
||||||
if (spr.pal ~= 2 and spr.pal ~= 7) then
|
|
||||||
sprite.changesect(aci, i) -- noticeably faster...
|
|
||||||
else
|
|
||||||
spr:changesect(i) -- ...than this
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
sprite.changesect(aci, sectnum)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
local CS = sprite.CSTAT
|
|
||||||
local SPAWNSPERTIC = 10 --> 300/second --> 18000 per minute
|
|
||||||
local TWOPI = 2*math.pi
|
|
||||||
|
|
||||||
gameactor
|
|
||||||
{
|
|
||||||
D.NUKEBUTTON+3, -- destroyed end-of-level nuke switch
|
|
||||||
|
|
||||||
function(aci)
|
|
||||||
local spr = sprite[aci]
|
|
||||||
|
|
||||||
for pi in player.all() do
|
|
||||||
-- XXX: how to emulate "use switch" distance checking code, but in
|
|
||||||
-- an actor-centric fashion?
|
|
||||||
if (not nukeswStart[aci] and player.holdskey(pi, "OPEN")
|
|
||||||
and (player[pi].pos - spr):len2sq() < 256^2) then
|
|
||||||
-- Enable us.
|
|
||||||
nukeswStart[aci] = gv.gametic
|
|
||||||
spr.cstatbits:clear(CS.TRANS_BITMASK)
|
|
||||||
spr.cstatbits:set(CS.TRANS1)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local startgtic = nukeswStart[aci]
|
|
||||||
if (not startgtic) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local hei, zofs = spr:getheightofs()
|
|
||||||
local radius = hei/2
|
|
||||||
|
|
||||||
for i=0,SPAWNSPERTIC-1 do
|
|
||||||
-- Make one second go once round the circle, spawning
|
|
||||||
-- SPAWNSPERTIC*GTICSPERSEC stars.
|
|
||||||
local ii = ((gv.gametic*SPAWNSPERTIC)%(GTICSPERSEC*SPAWNSPERTIC)) + i
|
|
||||||
local v = (radius/16)*angvec(ii*TWOPI/(GTICSPERSEC*SPAWNSPERTIC))
|
|
||||||
local circvec = xmath.vec3(0, v.x, 16*v.y):rotate(spr.ang)
|
|
||||||
local pos = spr^(zofs + radius) + 256*bangvec(spr.ang) + circvec
|
|
||||||
|
|
||||||
con.insertsprite{D.TRANSPORTERSTAR+4, pos, spr.sectnum, actor.STAT.ACTOR, aci,
|
|
||||||
xrepeat=3, yrepeat=3, ang=spr.ang}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
}
|
|
|
@ -1,179 +0,0 @@
|
||||||
|
|
||||||
// Map state test.
|
|
||||||
// To warp to a particular map, press the steroids key.
|
|
||||||
// Volume: player angle / 512, displayed on screen.
|
|
||||||
// Level: current weapon.
|
|
||||||
// Clear mapstate: press [QuickKick]
|
|
||||||
|
|
||||||
define Q_tmp 400
|
|
||||||
definequote Q_tmp ====== TEMP =======
|
|
||||||
|
|
||||||
definequote 401 WILL WARP TO VOLUME %d
|
|
||||||
|
|
||||||
define SQ 402
|
|
||||||
definequote SQ <source>
|
|
||||||
definequote 403 <dest>
|
|
||||||
definequote 404 <dest>
|
|
||||||
definequote 405 <dest>
|
|
||||||
definequote 406 <dest>
|
|
||||||
definequote 407 <dest>
|
|
||||||
definequote 408 <dest>
|
|
||||||
definequote 409 <dest>
|
|
||||||
definequote 410 <dest>
|
|
||||||
definequote 411 <dest>
|
|
||||||
definequote 412 <dest>
|
|
||||||
definequote 413 <dest>
|
|
||||||
definequote 414 <dest>
|
|
||||||
|
|
||||||
define Q_cms 500
|
|
||||||
definequote Q_cms Cleared mapstate for volume %d level %d
|
|
||||||
|
|
||||||
// Test gamevar flags GAMEVAR_NODEFAULT and GAMEVAR_NORESET.
|
|
||||||
// Global.
|
|
||||||
define GV_NODEFAULT 1024
|
|
||||||
define GV_NORESET 131072
|
|
||||||
define GV_NODEFAULT_NORESET 132096
|
|
||||||
// Per-player.
|
|
||||||
define PV_NODEFAULT 1025
|
|
||||||
define PV_NORESET 131073
|
|
||||||
define PV_NODEFAULT_NORESET 132097
|
|
||||||
// Per-actor (not very meaningful, as the APLAYER actor will in general have a
|
|
||||||
// different sprite for different levels).
|
|
||||||
define AV_NODEFAULT 1026
|
|
||||||
define AV_NORESET 131074
|
|
||||||
define AV_NODEFAULT_NORESET 132098
|
|
||||||
|
|
||||||
// global
|
|
||||||
gamevar numwarps 0 0
|
|
||||||
gamevar numwarpsD 0 GV_NODEFAULT
|
|
||||||
gamevar numwarpsR 0 GV_NORESET
|
|
||||||
gamevar numwarpsDR 0 GV_NODEFAULT_NORESET
|
|
||||||
// per-player
|
|
||||||
gamevar Pnumwarps 0 1
|
|
||||||
gamevar PnumwarpsD 0 PV_NODEFAULT
|
|
||||||
gamevar PnumwarpsR 0 PV_NORESET
|
|
||||||
gamevar PnumwarpsDR 0 PV_NODEFAULT_NORESET
|
|
||||||
// per-actor
|
|
||||||
gamevar Anumwarps 0 2
|
|
||||||
gamevar AnumwarpsD 0 AV_NODEFAULT
|
|
||||||
gamevar AnumwarpsR 0 AV_NORESET
|
|
||||||
gamevar AnumwarpsDR 0 AV_NODEFAULT_NORESET
|
|
||||||
|
|
||||||
gamevar pal 0 0
|
|
||||||
gamevar dq 0 0
|
|
||||||
gamevar y 0 0
|
|
||||||
|
|
||||||
state calcvolume
|
|
||||||
getplayer[THISACTOR].ang gs
|
|
||||||
andvar gs 2047
|
|
||||||
divvar gs 512
|
|
||||||
ends
|
|
||||||
|
|
||||||
state dispnumwarps
|
|
||||||
gametextz STARTALPHANUM 20 y dq 0 pal 0 0 0 xdim ydim 32768
|
|
||||||
addvar dq 1
|
|
||||||
addvar y 8
|
|
||||||
ends
|
|
||||||
|
|
||||||
onevent EVENT_DISPLAYREST
|
|
||||||
state calcvolume
|
|
||||||
qsprintf Q_tmp /*<-*/ 401 /**/ gs
|
|
||||||
gametext STARTALPHANUM 20 20 Q_tmp 0 0 0 0 0 xdim ydim
|
|
||||||
|
|
||||||
setvar y 40
|
|
||||||
setvar dq 403
|
|
||||||
setvar pal 0
|
|
||||||
|
|
||||||
// global
|
|
||||||
redefinequote SQ numwarps (no flags): %d
|
|
||||||
qsprintf dq SQ numwarps
|
|
||||||
state dispnumwarps
|
|
||||||
redefinequote SQ numwarps (NODEFAULT): %d
|
|
||||||
qsprintf dq SQ numwarpsD
|
|
||||||
state dispnumwarps
|
|
||||||
redefinequote SQ numwarps (NORESET): %d
|
|
||||||
qsprintf dq SQ numwarpsR
|
|
||||||
state dispnumwarps
|
|
||||||
redefinequote SQ numwarps (NODEFAULT, NORESET): %d
|
|
||||||
qsprintf dq SQ numwarpsDR
|
|
||||||
state dispnumwarps
|
|
||||||
|
|
||||||
// per-player
|
|
||||||
setvar pal 14
|
|
||||||
redefinequote SQ Pnumwarps (no flags): %d
|
|
||||||
qsprintf dq SQ Pnumwarps
|
|
||||||
state dispnumwarps
|
|
||||||
redefinequote SQ Pnumwarps (NODEFAULT): %d
|
|
||||||
qsprintf dq SQ PnumwarpsD
|
|
||||||
state dispnumwarps
|
|
||||||
redefinequote SQ Pnumwarps (NORESET): %d
|
|
||||||
qsprintf dq SQ PnumwarpsR
|
|
||||||
state dispnumwarps
|
|
||||||
redefinequote SQ Pnumwarps (NODEFAULT, NORESET): %d
|
|
||||||
qsprintf dq SQ PnumwarpsDR
|
|
||||||
state dispnumwarps
|
|
||||||
|
|
||||||
// per-actor
|
|
||||||
setvar pal 10
|
|
||||||
redefinequote SQ Anumwarps (no flags): %d
|
|
||||||
qsprintf dq SQ Anumwarps
|
|
||||||
state dispnumwarps
|
|
||||||
redefinequote SQ Anumwarps (NODEFAULT): %d
|
|
||||||
qsprintf dq SQ AnumwarpsD
|
|
||||||
state dispnumwarps
|
|
||||||
redefinequote SQ Anumwarps (NORESET): %d
|
|
||||||
qsprintf dq SQ AnumwarpsR
|
|
||||||
state dispnumwarps
|
|
||||||
redefinequote SQ Anumwarps (NODEFAULT, NORESET): %d
|
|
||||||
qsprintf dq SQ AnumwarpsDR
|
|
||||||
state dispnumwarps
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_USESTEROIDS
|
|
||||||
// global
|
|
||||||
addvar numwarps 1
|
|
||||||
addvar numwarpsD 1
|
|
||||||
addvar numwarpsR 1
|
|
||||||
addvar numwarpsDR 1
|
|
||||||
// per-player
|
|
||||||
addvar Pnumwarps 1
|
|
||||||
addvar PnumwarpsD 1
|
|
||||||
addvar PnumwarpsR 1
|
|
||||||
addvar PnumwarpsDR 1
|
|
||||||
// per-actor
|
|
||||||
addvar Anumwarps 1
|
|
||||||
addvar AnumwarpsD 1
|
|
||||||
addvar AnumwarpsR 1
|
|
||||||
addvar AnumwarpsDR 1
|
|
||||||
|
|
||||||
savemapstate
|
|
||||||
|
|
||||||
state calcvolume
|
|
||||||
startlevel gs currentweapon
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_QUICKKICK
|
|
||||||
state calcvolume
|
|
||||||
|
|
||||||
qsprintf Q_tmp Q_cms gs currentweapon
|
|
||||||
userquote Q_tmp
|
|
||||||
|
|
||||||
// Calc linear map index.
|
|
||||||
mulvar gs 64 // MAXLEVELS (XXX: should be exposed via CON)
|
|
||||||
addvarvar gs currentweapon
|
|
||||||
|
|
||||||
clearmapstate gs
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
loadmapstate
|
|
||||||
endevent
|
|
||||||
|
|
||||||
|
|
||||||
onevent EVENT_USEJETPACK
|
|
||||||
savegamevar numwarpsD
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_USENIGHTVISION
|
|
||||||
readgamevar numwarpsD
|
|
||||||
endevent
|
|
|
@ -1,63 +0,0 @@
|
||||||
|
|
||||||
local xmath = require("xmath")
|
|
||||||
|
|
||||||
local sprite = sprite
|
|
||||||
|
|
||||||
--
|
|
||||||
|
|
||||||
local maputil = {}
|
|
||||||
|
|
||||||
-- csfunc = maputil.CreateCrossSpriteFunc(use_sprite_i_func, use_sprite_j_func)
|
|
||||||
--
|
|
||||||
-- use_sprite_i_func: function(spri, ud), where
|
|
||||||
-- <spri> is a reference to sprite i
|
|
||||||
-- use_sprite_j_func: function(sprj, spri, ud), where
|
|
||||||
-- <spri> is a reference to sprite i
|
|
||||||
-- <sprj> is a reference to sprite j
|
|
||||||
--
|
|
||||||
-- csfunc: function(userdata, process_sprite_j_func), where
|
|
||||||
-- <userdata> is passed as <ud> to the functions described above
|
|
||||||
-- <process_sprite_j_func>: function(sprj, spri, ud), the user-provided
|
|
||||||
-- function that processes sprite j in some way
|
|
||||||
function maputil.CreateCrossSpriteFunc(use_sprite_i_func, use_sprite_j_func)
|
|
||||||
return function(userdata, process_sprite_j_func)
|
|
||||||
for i in sprite.all() do
|
|
||||||
if (use_sprite_i_func(sprite[i], userdata)) then
|
|
||||||
for j in sprite.all() do
|
|
||||||
if (use_sprite_j_func(sprite[j], sprite[i], userdata)) then
|
|
||||||
process_sprite_j_func(sprite[j], sprite[i], userdata)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Functions for "for all sprites Y of tile X that are closer than D, ..."
|
|
||||||
-- Passed userdefs: { picnumi, picnumj, maxldist }
|
|
||||||
local function snearpic_usei(spr, ud)
|
|
||||||
return spr.picnum == ud[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function snearpic_usej(sprj, spri, ud)
|
|
||||||
return sprj.picnum == ud[2] and xmath.ldist(spri, sprj) <= ud[3]
|
|
||||||
end
|
|
||||||
|
|
||||||
local snearpic_func = maputil.CreateCrossSpriteFunc(snearpic_usei, snearpic_usej)
|
|
||||||
|
|
||||||
-- maputil.for_sprites_near_picnum(picnumi, picnumj, dist, process_sprite_func)
|
|
||||||
--
|
|
||||||
-- Runs the following loop:
|
|
||||||
--
|
|
||||||
-- for all sprites i with tile <picnumi>,
|
|
||||||
-- for all sprites j of tile <picnumj> that are closer to sprite i than <dist> [*],
|
|
||||||
-- call process_sprite_func(sprite[j], sprite[i])
|
|
||||||
--
|
|
||||||
-- [*] using xmath.ldist()
|
|
||||||
function maputil.for_sprites_near_picnum(picnumi, picnumj, dist, process_sprite_func)
|
|
||||||
snearpic_func({picnumi, picnumj, dist}, process_sprite_func)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
return maputil
|
|
|
@ -1,57 +0,0 @@
|
||||||
// NOTE: This file is misnamed, "break" is local control flow, of course.
|
|
||||||
|
|
||||||
state teststate_break
|
|
||||||
ifvare 1 1
|
|
||||||
{
|
|
||||||
redefinequote 114 BEFORE
|
|
||||||
echo 114
|
|
||||||
|
|
||||||
break
|
|
||||||
|
|
||||||
redefinequote 114 AFTER
|
|
||||||
echo 114
|
|
||||||
}
|
|
||||||
|
|
||||||
redefinequote 114 STILL LIVE INNER
|
|
||||||
echo 114
|
|
||||||
ends
|
|
||||||
|
|
||||||
definequote 499 ===
|
|
||||||
definequote 500 MAXSPRITES: %d
|
|
||||||
definequote 501 MAXSTATUS: %d
|
|
||||||
definequote 502 MAX_WEAPONS: %d
|
|
||||||
|
|
||||||
// XXX: EVENT_INIT not reached with LunaCON
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
state teststate_break
|
|
||||||
|
|
||||||
redefinequote 114 STILL LIVE OUTER
|
|
||||||
echo 114
|
|
||||||
|
|
||||||
qsprintf 499 /*<-*/ 500 MAXSPRITES
|
|
||||||
echo 499
|
|
||||||
qsprintf 499 /*<-*/ 501 MAXSTATUS
|
|
||||||
echo 499
|
|
||||||
qsprintf 499 /*<-*/ 502 MAX_WEAPONS
|
|
||||||
echo 499
|
|
||||||
endevent
|
|
||||||
|
|
||||||
// Test number parsing
|
|
||||||
gamevar MINUS_ONE -1 0
|
|
||||||
gamevar INT32_MIN 0x80000000 0
|
|
||||||
gamevar INT32_MIN_ -0x80000000 0
|
|
||||||
gamevar INT32_MINh 0x80000000h 0
|
|
||||||
gamevar INT32_MIN_h -0x80000000h 0
|
|
||||||
|
|
||||||
gamevar INT32_MIN_P1 0x80000001 0
|
|
||||||
gamevar INT32_MIN_P1h 0x80000001h 0
|
|
||||||
//gamevar INT32_MAX_ -0x80000001 0 // error
|
|
||||||
|
|
||||||
gamevar INT32_MAX 0x7fffffff 0
|
|
||||||
gamevar INT32_MAXh 0x7fffffffh 0
|
|
||||||
|
|
||||||
// 9 digits? truncate it, but only because there's an F there.
|
|
||||||
gamevar SOMEHEX 0xFFEFC0001 0
|
|
||||||
|
|
||||||
// This one throws an error:
|
|
||||||
//gamevar BADHEX 0xBFEFC0001 0
|
|
|
@ -1,22 +0,0 @@
|
||||||
definequote 125 SPAWNED HEAVYHBOMB
|
|
||||||
definequote 126 RAN EVENT_EGS
|
|
||||||
definequote 127 RAN TEST STATE
|
|
||||||
|
|
||||||
state teststate1
|
|
||||||
return
|
|
||||||
quote 127
|
|
||||||
ends
|
|
||||||
|
|
||||||
onevent EVENT_EGS
|
|
||||||
ifactor HEAVYHBOMB
|
|
||||||
{
|
|
||||||
state teststate1 // after teststate1's return, return from EVENT_EGS!
|
|
||||||
quote 126
|
|
||||||
}
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_FIRE
|
|
||||||
// the concrete actor is irrelevant, only placeholder
|
|
||||||
spawn HEAVYHBOMB // --> EVENT_EGS
|
|
||||||
quote 125
|
|
||||||
endevent
|
|
|
@ -1,75 +0,0 @@
|
||||||
|
|
||||||
gamevar vol 0 0
|
|
||||||
gamevar lev 0 0
|
|
||||||
|
|
||||||
// out-of-bounds volume/level numbers
|
|
||||||
gamevar badvol_oob 99 0
|
|
||||||
gamevar badlev_oob 999 0
|
|
||||||
|
|
||||||
// volume/level numbers for which no level is defined
|
|
||||||
gamevar badvol_nd 3 0
|
|
||||||
gamevar badlev_nd 32 0
|
|
||||||
|
|
||||||
define Q_tmp 255
|
|
||||||
definequote Q_tmp <temp>
|
|
||||||
define Q_tmp2 256
|
|
||||||
definequote Q_tmp2 <temp>
|
|
||||||
|
|
||||||
define Q_last_level 1000
|
|
||||||
definequote Q_last_level Last level (1-based): %d
|
|
||||||
define Q_dtime 1001
|
|
||||||
definequote Q_dtime Designer time for last level: %s
|
|
||||||
|
|
||||||
state print_designer_time
|
|
||||||
// This must not invoke an oob access on the C side if ud.last_level < 0!
|
|
||||||
qgetsysstr Q_tmp2 STR_DESIGNERTIME
|
|
||||||
|
|
||||||
qsprintf Q_tmp Q_dtime Q_tmp2
|
|
||||||
userquote Q_tmp
|
|
||||||
ends
|
|
||||||
|
|
||||||
onevent EVENT_NEWGAME
|
|
||||||
getuserdef .last_level lev
|
|
||||||
qsprintf Q_tmp Q_last_level lev
|
|
||||||
userquote Q_tmp
|
|
||||||
|
|
||||||
state print_designer_time
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
state print_designer_time
|
|
||||||
|
|
||||||
// must fail, since the current player is -1 in this event
|
|
||||||
qgetsysstr Q_tmp STR_PLAYERNAME
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_USESTEROIDS
|
|
||||||
getuserdef .volume_number vol
|
|
||||||
setuserdef .volume_number badvol_oob // LunaCON errors here
|
|
||||||
qgetsysstr Q_tmp STR_VOLUMENAME // C-CON errors here
|
|
||||||
setuserdef .volume_number vol
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_USEJETPACK
|
|
||||||
getuserdef .volume_number vol
|
|
||||||
getuserdef .level_number lev
|
|
||||||
|
|
||||||
setuserdef .volume_number badvol_oob // LunaCON errors here
|
|
||||||
setuserdef .level_number badlev_oob
|
|
||||||
qgetsysstr Q_tmp STR_MAPNAME // C-CON errors here
|
|
||||||
|
|
||||||
setuserdef .volume_number vol
|
|
||||||
setuserdef .level_number lev
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_JUMP
|
|
||||||
getuserdef .volume_number vol
|
|
||||||
getuserdef .level_number lev
|
|
||||||
|
|
||||||
setuserdef .volume_number badvol_nd
|
|
||||||
setuserdef .level_number badlev_nd
|
|
||||||
qgetsysstr Q_tmp STR_MAPFILENAME // LunaCON, C-CON error here
|
|
||||||
|
|
||||||
setuserdef .volume_number vol
|
|
||||||
setuserdef .level_number lev
|
|
||||||
endevent
|
|
|
@ -1,71 +0,0 @@
|
||||||
// Tests for the qsubstr command.
|
|
||||||
// This one should be safe to run in C-CON.
|
|
||||||
|
|
||||||
// overlong string at definition (should warn)
|
|
||||||
define QSRC 400
|
|
||||||
definequote QSRC -123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789=123456789
|
|
||||||
define QSRC_SHORT 401
|
|
||||||
definequote QSRC_SHORT -123456789
|
|
||||||
|
|
||||||
define QDST 500
|
|
||||||
definequote QDST
|
|
||||||
|
|
||||||
// for gettimedate
|
|
||||||
gamevar sec 0 0
|
|
||||||
gamevar x 0 0
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
// first, a plain, non-border-case call
|
|
||||||
qsubstr QDST QSRC 10 10 // -> "-123456789"
|
|
||||||
userquote QDST
|
|
||||||
|
|
||||||
// length zero yields the empty string
|
|
||||||
qsubstr QDST QSRC 20 0 // -> ""
|
|
||||||
userquote QDST
|
|
||||||
|
|
||||||
// start+length > strlen of source: no error
|
|
||||||
qsubstr QDST QSRC_SHORT 8 6 // -> "89"
|
|
||||||
userquote QDST
|
|
||||||
|
|
||||||
// start == strlen of source
|
|
||||||
qsubstr QDST QSRC_SHORT 10 1 // -> ""
|
|
||||||
userquote QDST
|
|
||||||
|
|
||||||
// Make QDST contain
|
|
||||||
// -123456789<NUL>123456789-...
|
|
||||||
qstrcpy QDST QSRC_SHORT
|
|
||||||
qstrcpy QSRC_SHORT QSRC
|
|
||||||
qstrcpy QSRC_SHORT QDST
|
|
||||||
userquote QSRC_SHORT // -> "-123456789"
|
|
||||||
|
|
||||||
// start == strlen of source, trailing bytes after source <NUL>
|
|
||||||
qsubstr QDST QSRC_SHORT 10 2 // -> ""
|
|
||||||
userquote QDST
|
|
||||||
|
|
||||||
// start is beyond-<NUL> of the source
|
|
||||||
qsubstr QDST QSRC_SHORT 11 9 // -> ""
|
|
||||||
userquote QDST
|
|
||||||
|
|
||||||
// copy whole string (127 chars + 1 <NUL> == MAXQUOTELEN)
|
|
||||||
qsubstr QDST QSRC 0 1000 // -> "-123456789-123456789-...=123456"
|
|
||||||
userquote QDST
|
|
||||||
|
|
||||||
/** TESTS FOR source == dest **/
|
|
||||||
|
|
||||||
qsubstr QDST QDST 0 20 // -> "-123456789-123456789"
|
|
||||||
userquote QDST
|
|
||||||
|
|
||||||
qsubstr QDST QDST 5 10 // -> "56789-1234"
|
|
||||||
userquote QDST
|
|
||||||
|
|
||||||
/** TESTS FOR invalid input **/
|
|
||||||
|
|
||||||
gettimedate sec x x x x x x x
|
|
||||||
ifvarand sec 1
|
|
||||||
qsubstr QDST QSRC -1 10 // invalid start
|
|
||||||
else
|
|
||||||
qsubstr QDST QSRC 0 -1 // invalid length
|
|
||||||
|
|
||||||
// Reached only in C-CON:
|
|
||||||
qsubstr QDST QSRC 128 0 // invalid start
|
|
||||||
endevent
|
|
|
@ -1,120 +0,0 @@
|
||||||
// Don't try in C-CON!
|
|
||||||
|
|
||||||
gamevar slen 0 0
|
|
||||||
gamevar n -12345678 0 // a number having length 9 in the decimal representation
|
|
||||||
|
|
||||||
// overlong string at definition (should warn)
|
|
||||||
definequote 400 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789=123456789-
|
|
||||||
|
|
||||||
// alloc some quotes, test empty quote syntax
|
|
||||||
definequote 401
|
|
||||||
definequote 402
|
|
||||||
definequote 403
|
|
||||||
definequote 404
|
|
||||||
definequote 405
|
|
||||||
definequote 406
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
redefinequote 114 QWE
|
|
||||||
|
|
||||||
redefinequote 115 012345678|
|
|
||||||
// qstrcat X Y, where X==Y: duplicates the quote
|
|
||||||
qstrcat 115 115
|
|
||||||
userquote 115 // "012345678|012345678|"
|
|
||||||
|
|
||||||
// simple qstrcpy test
|
|
||||||
qstrcpy 116 115
|
|
||||||
userquote 116 // "012345678|012345678|"
|
|
||||||
|
|
||||||
redefinequote 117 %s -- %d -- %s -- %d slen=%d
|
|
||||||
// test:
|
|
||||||
// - same destination quote as the format quote
|
|
||||||
// - multiple conversions
|
|
||||||
qstrlen slen 115 // -> 10
|
|
||||||
qsprintf 117 /*<-*/ 117 /*args:*/ 116 9999 114 5555 slen
|
|
||||||
userquote 117 // "012345678|012345678| -- 9999 -- QWE -- 5555 slen=10"
|
|
||||||
|
|
||||||
redefinequote 117 %s -- %d -- %s -- %d slen=%d
|
|
||||||
// test:
|
|
||||||
// - same destination quote as one of the source quotes under %s conversion
|
|
||||||
qsprintf 116 /*<-*/ 117 /*args:*/ 116 9999 116 5555 slen
|
|
||||||
userquote 116 // "012345678|012345678| -- 9999 -- 012345678|012345678| -- 5555 slen=10"
|
|
||||||
|
|
||||||
// aliased qstrcpy test (a no-op)
|
|
||||||
qstrcpy 115 115
|
|
||||||
userquote 115 // still "012345678|012345678|"
|
|
||||||
|
|
||||||
qstrcat 115 115 // len 40
|
|
||||||
qstrcat 115 115 // len 80
|
|
||||||
qstrcat 115 115
|
|
||||||
// result now: 12 x "012345678|" concantenated with "0123456" (= total length 127 = MAXQUOTELEN-1)
|
|
||||||
qstrcat 115 115 // this one is redundant, but must not overflow the quote buffer
|
|
||||||
userquote 115
|
|
||||||
|
|
||||||
////////// qstrncat test
|
|
||||||
redefinequote 300 012345678|
|
|
||||||
qstrncat 300 300 2
|
|
||||||
userquote 300 // "012345678|01"
|
|
||||||
//////////
|
|
||||||
|
|
||||||
redefinequote 117 X%s
|
|
||||||
qsprintf 117 /*<-*/ 117 /*args:*/ 115
|
|
||||||
userquote 117 // result: "X" .. 12 x "012345678|" .. "012345" (= total length 127 = MAXQUOTELEN-1)
|
|
||||||
|
|
||||||
// 32 %d (or %ld) conversions
|
|
||||||
redefinequote 117 %ld|%d|%ld|%d|%ld|%ld|%d|%ld|%d|%ld/%ld|%d|%ld|%d|%ld|%ld|%d|%ld|%d|%ld/%ld|%d|%ld|%d|%ld|%ld|%d|%ld|%d|%ld/%d/%d
|
|
||||||
// string shorter than MAXQUOTELEN-1:
|
|
||||||
qsprintf 116 /*<-*/ 117 /*args:*/ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
||||||
// result: "0|1|2|3|4|5|6|7|8|9/0|1|2|3|4|5|6|7|8|9/0|1|2|3|4|5|6|7|8|9/0/1"
|
|
||||||
userquote 116
|
|
||||||
|
|
||||||
// string longer than MAXQUOTELEN-1:
|
|
||||||
qsprintf 116 /*<-*/ 117 /*args:*/ n n n n n n n n n n /**/ n n n n n n n n n n /**/ n n
|
|
||||||
userquote 116
|
|
||||||
// result: 10 x "-12345678" .. "/-12345678|-12345678|-123456" (= total length 127 = MAXQUOTELEN-1)
|
|
||||||
|
|
||||||
// overlong string at redefinition
|
|
||||||
redefinequote 117 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789=123456789-
|
|
||||||
userquote 117 // should end in "=1234567" (= total length 127 = MAXQUOTELEN-1)
|
|
||||||
userquote 400 // same thing
|
|
||||||
|
|
||||||
// test passing empty quote
|
|
||||||
userquote 401
|
|
||||||
|
|
||||||
// qgetsysstr test
|
|
||||||
qgetsysstr 401 STR_MAPNAME
|
|
||||||
qgetsysstr 402 STR_MAPFILENAME
|
|
||||||
// qgetsysstr 403 STR_PLAYERNAME
|
|
||||||
qgetsysstr 404 STR_VERSION
|
|
||||||
qgetsysstr 405 STR_GAMETYPE
|
|
||||||
qgetsysstr 406 STR_VOLUMENAME
|
|
||||||
|
|
||||||
redefinequote 400 %s | %s | %s | %s | %s | %s
|
|
||||||
qsprintf 400 /*<-*/ 400 /*args:*/ 401 402 403 404 405 406
|
|
||||||
userquote 400
|
|
||||||
endevent
|
|
||||||
|
|
||||||
// Test of 'getpname' command.
|
|
||||||
onevent EVENT_JUMP
|
|
||||||
getpname 400 THISACTOR
|
|
||||||
userquote 400
|
|
||||||
endevent
|
|
||||||
|
|
||||||
|
|
||||||
// Following code by Hendricks266, from
|
|
||||||
// http://forums.duke4.net/topic/1382-duke-64-mod-thread/page__view__findpost__p__150497
|
|
||||||
gamevar temp 0 0
|
|
||||||
gamevar x 0 0
|
|
||||||
|
|
||||||
definequote 666 This quote sucks
|
|
||||||
|
|
||||||
onevent EVENT_DISPLAYREST
|
|
||||||
qstrlen temp 666
|
|
||||||
setvarvar x tilesizx[STARTALPHANUM] // rough approximation
|
|
||||||
mulvarvar x temp
|
|
||||||
divvar x 2
|
|
||||||
addvar x 160 // put your centered position here
|
|
||||||
gametext STARTALPHANUM x 30 666 0 0 16 0 0 xdim ydim
|
|
||||||
endevent
|
|
||||||
|
|
||||||
//////////
|
|
|
@ -1,56 +0,0 @@
|
||||||
// Use as root file, not as additional CON module.
|
|
||||||
|
|
||||||
// Test dynamic tile remapping.
|
|
||||||
dynamicremap
|
|
||||||
// Make MASKWALL2 (fence in E1L1 rooftop) execute the C side code of DUKECAR.
|
|
||||||
// This must be before the original DUKECAR definition, because label
|
|
||||||
// redefinitions are ignored in CON.
|
|
||||||
// XXX: Does MASKWALL2's code get executed, too?
|
|
||||||
define DUKECAR 913
|
|
||||||
|
|
||||||
include GAME.CON
|
|
||||||
// NOTE:
|
|
||||||
// DEFS.CON 520:6: warning: label "DUKECAR" not redefined with new value 2491 (old: 913)
|
|
||||||
|
|
||||||
gamevar LOGO_FLAGS 6399 0 // 255+2048+4096, test "no E4 intro", "no E1 bonus" bits
|
|
||||||
|
|
||||||
useractor 4 58 1 NO NO 0
|
|
||||||
// usertype 4 is "rotation-fixed actor"
|
|
||||||
// tile 58 is the space suit
|
|
||||||
|
|
||||||
state killme
|
|
||||||
enda
|
|
||||||
|
|
||||||
// Test useractor type "enemy"
|
|
||||||
define CYCLOIDHEAD 490
|
|
||||||
// Will have a flat-sprite-on-floor shadow and be an autoaim target.
|
|
||||||
useractor 1 CYCLOIDHEAD 10
|
|
||||||
ifpdistl 1024 tip
|
|
||||||
state killme
|
|
||||||
enda
|
|
||||||
|
|
||||||
onevent EVENT_JUMP
|
|
||||||
mail 2
|
|
||||||
money 5
|
|
||||||
paper 3
|
|
||||||
endevent
|
|
||||||
|
|
||||||
// Speed up sector effects a little 8-)
|
|
||||||
gamevar ra_temp 0 0
|
|
||||||
eventloadactor GPSPEED
|
|
||||||
getactor[THISACTOR].lotag ra_temp
|
|
||||||
mulvar ra_temp 4
|
|
||||||
setactor[THISACTOR].lotag ra_temp
|
|
||||||
enda
|
|
||||||
|
|
||||||
gamevar ii 0 0
|
|
||||||
onevent EVENT_PROCESSINPUT
|
|
||||||
setvar ii 0
|
|
||||||
whilevarvarn ii MAXSPRITES
|
|
||||||
{
|
|
||||||
ifvare sprite[ii].picnum 58 // space suit
|
|
||||||
ssp ii CLIPMASK0
|
|
||||||
|
|
||||||
addvar ii 1
|
|
||||||
}
|
|
||||||
endevent
|
|
|
@ -1,349 +0,0 @@
|
||||||
// ScreenText Test
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is half a test and half a demonstration.
|
|
||||||
For example, some of the spacing and offset stuff could be replaced with tests of other things, like the backwards bit, line wrapping, and space lengths.
|
|
||||||
Also, if/when Lunatic adds support for \n characters.
|
|
||||||
*/
|
|
||||||
|
|
||||||
definequote 999
|
|
||||||
|
|
||||||
definequote 1000 AbCdEfGhIjKlM
|
|
||||||
definequote 1001 nOpQrStUvWxYz
|
|
||||||
|
|
||||||
definequote 1010 X1234567890-.!?;:'\ / %
|
|
||||||
|
|
||||||
definequote 1020 ^2R^8G^1B^7Y
|
|
||||||
|
|
||||||
definequote 1337 1337
|
|
||||||
|
|
||||||
definequote 2000 Left
|
|
||||||
definequote 2001 Center
|
|
||||||
definequote 2002 Right
|
|
||||||
|
|
||||||
definequote 2003 Top
|
|
||||||
definequote 2004 Center
|
|
||||||
definequote 2005 Bottom
|
|
||||||
|
|
||||||
definequote 3000 Spacing
|
|
||||||
definequote 3001 X-Offset
|
|
||||||
definequote 3002 %d: UiUiUiUiUi
|
|
||||||
definequote 3003 Justification
|
|
||||||
|
|
||||||
definequote 4000 Normal: aBcDeF
|
|
||||||
definequote 4001 Upper: aBcDeF
|
|
||||||
definequote 4002 Lower: aBcDeF
|
|
||||||
definequote 4003 Inverted: aBcDeF
|
|
||||||
|
|
||||||
definequote 5000 Rotate
|
|
||||||
definequote 5001 Top, Left:
|
|
||||||
definequote 5002 Center, Center:
|
|
||||||
definequote 5003 Bottom, Right:
|
|
||||||
|
|
||||||
gamevar font STARTALPHANUM 0
|
|
||||||
gamevar x 0 0
|
|
||||||
gamevar y 0 0
|
|
||||||
gamevar z 65536 0
|
|
||||||
gamevar blockangle 0 0
|
|
||||||
gamevar charangle 0 0
|
|
||||||
gamevar q 1000 0
|
|
||||||
gamevar shade 0 0
|
|
||||||
gamevar pal 0 0
|
|
||||||
gamevar o 16 0
|
|
||||||
gamevar alpha 0 0
|
|
||||||
gamevar xspace 5 0
|
|
||||||
gamevar yline 8 0
|
|
||||||
gamevar xbetween 0 0
|
|
||||||
gamevar ybetween 0 0
|
|
||||||
gamevar f 0 0
|
|
||||||
gamevar x1 0 0
|
|
||||||
gamevar y1 0 0
|
|
||||||
gamevar x2 0 0
|
|
||||||
gamevar y2 0 0
|
|
||||||
|
|
||||||
gamevar temp 0 0
|
|
||||||
gamevar doaddtvblend 0 0
|
|
||||||
|
|
||||||
state resetbounds
|
|
||||||
setvarvar x1 windowx1
|
|
||||||
setvarvar y1 windowy1
|
|
||||||
setvarvar x2 windowx2
|
|
||||||
setvarvar y2 windowy2
|
|
||||||
ends
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
state resetbounds
|
|
||||||
endevent
|
|
||||||
|
|
||||||
define TEXT_XRIGHT 0x00000001
|
|
||||||
define TEXT_XCENTER 0x00000002
|
|
||||||
define TEXT_YBOTTOM 0x00000004
|
|
||||||
define TEXT_YCENTER 0x00000008
|
|
||||||
define TEXT_INTERNALSPACE 0x00000010
|
|
||||||
define TEXT_TILESPACE 0x00000020
|
|
||||||
define TEXT_INTERNALLINE 0x00000040
|
|
||||||
define TEXT_TILELINE 0x00000080
|
|
||||||
define TEXT_XOFFSETZERO 0x00000100
|
|
||||||
define TEXT_XJUSTIFY 0x00000200
|
|
||||||
define TEXT_YOFFSETZERO 0x00000400
|
|
||||||
define TEXT_YJUSTIFY 0x00000800
|
|
||||||
define TEXT_LINEWRAP 0x00001000
|
|
||||||
define TEXT_UPPERCASE 0x00002000
|
|
||||||
define TEXT_INVERTCASE 0x00004000
|
|
||||||
define TEXT_IGNOREESCAPE 0x00008000
|
|
||||||
define TEXT_LITERALESCAPE 0x00010000
|
|
||||||
define TEXT_BACKWARDS 0x00020000
|
|
||||||
define TEXT_GAMETEXTNUMHACK 0x00040000
|
|
||||||
define TEXT_DIGITALNUMBER 0x00080000
|
|
||||||
define TEXT_BIGALPHANUM 0x00100000
|
|
||||||
define TEXT_GRAYFONT 0x00200000
|
|
||||||
|
|
||||||
state increment_line
|
|
||||||
ifvarg q 0
|
|
||||||
screentext font x y z blockangle charangle q shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar y 8
|
|
||||||
ends
|
|
||||||
|
|
||||||
state increment_line_test
|
|
||||||
ifvarg q 0
|
|
||||||
{
|
|
||||||
qsprintf 999 q temp
|
|
||||||
screentext font x y z blockangle charangle 999 shade pal o alpha xspace yline temp ybetween f x1 y1 x2 y2
|
|
||||||
}
|
|
||||||
addvar temp 1
|
|
||||||
addvar y 8
|
|
||||||
ends
|
|
||||||
|
|
||||||
onevent EVENT_DISPLAYREST
|
|
||||||
state resetbounds
|
|
||||||
|
|
||||||
// screentext font x y z blockangle charangle q shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
|
|
||||||
// spacing
|
|
||||||
setvar f 0
|
|
||||||
setvar x 35
|
|
||||||
setvar y 3
|
|
||||||
setvar pal 0
|
|
||||||
|
|
||||||
setvar temp 0
|
|
||||||
setvar q 3000
|
|
||||||
state increment_line_test
|
|
||||||
|
|
||||||
setvar temp -3
|
|
||||||
setvar q 3002
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
|
|
||||||
// justification
|
|
||||||
setvar f 0
|
|
||||||
orvar f TEXT_XCENTER
|
|
||||||
orvar f TEXT_XJUSTIFY
|
|
||||||
screentext font 160 y z blockangle charangle 3003 shade 12 o alpha xspace yline 320 ybetween f x1 y1 x2 y2
|
|
||||||
addvar y 8
|
|
||||||
setvar f 0
|
|
||||||
|
|
||||||
// x-offset
|
|
||||||
setvar temp 0
|
|
||||||
setvar q 3001
|
|
||||||
state increment_line_test
|
|
||||||
|
|
||||||
orvar f TEXT_XOFFSETZERO
|
|
||||||
setvar temp 5
|
|
||||||
setvar q 3002
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
state increment_line_test
|
|
||||||
|
|
||||||
addvar y 4
|
|
||||||
|
|
||||||
// case
|
|
||||||
setvar f 0
|
|
||||||
setvar q 4000
|
|
||||||
setvar pal 23
|
|
||||||
state increment_line
|
|
||||||
setvar q 4001
|
|
||||||
orvar f TEXT_UPPERCASE
|
|
||||||
state increment_line
|
|
||||||
setvar q 4002
|
|
||||||
orvar f TEXT_INVERTCASE
|
|
||||||
state increment_line
|
|
||||||
setvar q 4003
|
|
||||||
xorvar f TEXT_UPPERCASE
|
|
||||||
state increment_line
|
|
||||||
|
|
||||||
// Calculate periodically cycling alpha value. PERIODIC_ALPHA.
|
|
||||||
setvarvar temp totalclock
|
|
||||||
shiftvarl temp 2
|
|
||||||
|
|
||||||
sin alpha temp // alpha is now in [-2^14 .. 2^14]
|
|
||||||
shiftvarr alpha 7 // [-2^7 .. 2^7]
|
|
||||||
addvar alpha 128 // [0 .. 256]
|
|
||||||
ifvarg alpha 254 { setvar alpha 255 setvar doaddtvblend 1 }
|
|
||||||
else ifvarl alpha 2 { setvar doaddtvblend 0 }
|
|
||||||
|
|
||||||
ifvare doaddtvblend 1
|
|
||||||
{
|
|
||||||
shiftvarr alpha 2 // [0 63]
|
|
||||||
|
|
||||||
ifvarg alpha 31
|
|
||||||
{
|
|
||||||
setvar temp 63
|
|
||||||
subvarvar temp alpha
|
|
||||||
setvarvar alpha temp
|
|
||||||
orvar o 32 // RS_TRANS2
|
|
||||||
}
|
|
||||||
// alpha now in [0 .. 31]
|
|
||||||
|
|
||||||
addvar alpha 101 // additive translucency tables are assumed to be at [101 .. 132]
|
|
||||||
mulvar alpha -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// divider line
|
|
||||||
rotatespritea 240 65 32768 512 WINDOWBORDER1 0 2 0 85 x1 y1 x2 y2
|
|
||||||
|
|
||||||
setvar pal 12
|
|
||||||
|
|
||||||
// x-alignment
|
|
||||||
setvar f 0
|
|
||||||
setvar x 220
|
|
||||||
screentext font x 127 z blockangle charangle 2000 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
|
|
||||||
orvar f TEXT_XCENTER
|
|
||||||
screentext font x 137 z blockangle charangle 2001 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
|
|
||||||
xorvar f TEXT_XCENTER
|
|
||||||
orvar f TEXT_XRIGHT
|
|
||||||
screentext font x 147 z blockangle charangle 2002 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
|
|
||||||
rotatespritea x 132 32768 0 WINDOWBORDER2 127 0 0 85 x1 y1 x2 y2
|
|
||||||
|
|
||||||
// y-alignment
|
|
||||||
setvar f TEXT_UPPERCASE
|
|
||||||
screentext font 180 162 z blockangle charangle 2003 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
|
|
||||||
orvar f TEXT_YCENTER
|
|
||||||
screentext font 208 162 z blockangle charangle 2004 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
|
|
||||||
xorvar f TEXT_YCENTER
|
|
||||||
orvar f TEXT_YBOTTOM
|
|
||||||
screentext font 260 162 z blockangle charangle 2005 shade pal o alpha xspace yline 0 ybetween f x1 y1 x2 y2
|
|
||||||
rotatespritea 240 163 32768 512 WINDOWBORDER1 127 0 0 85 x1 y1 x2 y2
|
|
||||||
|
|
||||||
setvar pal 0
|
|
||||||
|
|
||||||
// bluefont vs. redfont XOFFSETZERO comparison
|
|
||||||
setvar f 0
|
|
||||||
orvar f TEXT_XOFFSETZERO
|
|
||||||
screentext font 0 175 65536 blockangle charangle 1010 shade pal o alpha xspace yline 13 ybetween f x1 y1 x2 y2
|
|
||||||
orvar f TEXT_BIGALPHANUM
|
|
||||||
orvar f TEXT_YBOTTOM
|
|
||||||
screentext BIGALPHANUM 0 200 z blockangle charangle 1010 shade pal o alpha xspace yline 13 ybetween f x1 y1 x2 y2
|
|
||||||
|
|
||||||
// block rotation
|
|
||||||
setvar y 2
|
|
||||||
screentext MINIFONT 170 y z blockangle charangle 5001 shade 10 o alpha 2 yline 1 ybetween 0 x1 y1 x2 y2
|
|
||||||
screentext MINIFONT 237 y z blockangle charangle 5002 shade 14 o alpha 2 yline 1 ybetween TEXT_XCENTER x1 y1 x2 y2
|
|
||||||
screentext MINIFONT 317 y z blockangle charangle 5003 shade 16 o alpha 2 yline 1 ybetween TEXT_XRIGHT x1 y1 x2 y2
|
|
||||||
|
|
||||||
setvar f 0
|
|
||||||
setvar x 175
|
|
||||||
setvar y 12
|
|
||||||
setvar pal 10
|
|
||||||
setvar blockangle 0
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
|
|
||||||
setvar blockangle -256
|
|
||||||
setvar x 245
|
|
||||||
setvar y 40
|
|
||||||
setvar f 0
|
|
||||||
setvar pal 14
|
|
||||||
orvar f TEXT_XCENTER
|
|
||||||
orvar f TEXT_YCENTER
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
|
|
||||||
setvar blockangle -512
|
|
||||||
setvar x 317
|
|
||||||
setvar y 19
|
|
||||||
setvar f 0
|
|
||||||
setvar pal 16
|
|
||||||
orvar f TEXT_XRIGHT
|
|
||||||
orvar f TEXT_YBOTTOM
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar blockangle 128
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
|
|
||||||
setvar pal 15
|
|
||||||
|
|
||||||
// character rotation
|
|
||||||
setvar blockangle 0
|
|
||||||
setvar charangle 256
|
|
||||||
setvar f 0
|
|
||||||
setvar x 175
|
|
||||||
setvar y 78
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar charangle 256
|
|
||||||
addvar y 13
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar charangle 256
|
|
||||||
addvar y 13
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
addvar charangle 256
|
|
||||||
addvar y 13
|
|
||||||
screentext font x y z blockangle charangle 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
setvar charangle 0
|
|
||||||
|
|
||||||
setvar x 225
|
|
||||||
setvar y 78
|
|
||||||
screentext font x y z 128 -128 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
screentext font x y z 256 -256 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
screentext font x y z 384 -384 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
screentext font x y z 512 -512 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
|
|
||||||
setvar x 312
|
|
||||||
setvar y 90
|
|
||||||
orvar f TEXT_XRIGHT
|
|
||||||
orvar f TEXT_YCENTER
|
|
||||||
screentext font x y z -128 128 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
screentext font x y z -256 256 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
screentext font x y z -384 384 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
screentext font x y z -512 512 5000 shade pal o alpha xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
|
|
||||||
// alpha, sideways, and escape colors
|
|
||||||
setvar pal 13
|
|
||||||
setvar blockangle 0
|
|
||||||
setvar f 0
|
|
||||||
orvar f TEXT_XCENTER
|
|
||||||
screentext font 0 100 z 1536 charangle 1020 shade pal o 0 xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
orvar f TEXT_IGNOREESCAPE
|
|
||||||
screentext font 10 100 z 1536 charangle 1020 shade pal o 85 xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
orvar f TEXT_LITERALESCAPE
|
|
||||||
screentext font 20 100 z 1536 charangle 1020 shade pal o 170 xspace yline xbetween ybetween f x1 y1 x2 y2
|
|
||||||
|
|
||||||
ifvarand o 32 xorvar o 32 // clear RS_TRANS2
|
|
||||||
setvar alpha 0 // Clear alpha so it's only active from PERIODIC_ALPHA to here.
|
|
||||||
endevent
|
|
|
@ -1,906 +0,0 @@
|
||||||
--[[
|
|
||||||
Usage: in Mapster32,
|
|
||||||
> lua "shadexfog=reload'shadexfog'"
|
|
||||||
-- for example
|
|
||||||
> lua "shadexfog.create(100, 255,255,255)"
|
|
||||||
> lua "shadexfog.translate(100, 2)"
|
|
||||||
In EDuke32, simply pass this module at the command line.
|
|
||||||
--]]
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local error = error
|
|
||||||
local print = print
|
|
||||||
local printf = printf
|
|
||||||
local tonumber = tonumber
|
|
||||||
local type = type
|
|
||||||
local unpack = unpack
|
|
||||||
|
|
||||||
local bit = require("bit")
|
|
||||||
local math = require("math")
|
|
||||||
local string = require("string")
|
|
||||||
local min, max = math.min, math.max
|
|
||||||
local floor = math.floor
|
|
||||||
|
|
||||||
local sector, wall, sprite = sector, wall, sprite
|
|
||||||
|
|
||||||
local engine = require("engine")
|
|
||||||
local gv = gv
|
|
||||||
|
|
||||||
----------
|
|
||||||
|
|
||||||
local shadexfog = {}
|
|
||||||
|
|
||||||
-- Example:
|
|
||||||
-- lua "shadexfog.createremap(30, {[2]=0, [3]=1, [12]=0, [13]=1})"
|
|
||||||
-- creates a pal 30 which maps the blue and orange ramps to the gray ones.
|
|
||||||
-- (Compare with the rows of http://wiki.eduke32.com/wiki/File:Pala.png)
|
|
||||||
--
|
|
||||||
-- Sexdecatuple remappings of Duke3D pals loaded from LOOKUP.DAT:
|
|
||||||
-- Remappings that are not expressible as such and identity maps (pal 3 and 9)
|
|
||||||
-- omitted.
|
|
||||||
--
|
|
||||||
-- 2: { [0]=8, [1]=13, [2]=8, [3]=13, [4]=13, [5]=8, [6]=8, [7]=13, [9]=8, [10]=8, [11]=13, [12]=8, [14]=8, }
|
|
||||||
-- 5: { [8]=2, [13]=3, }
|
|
||||||
-- 7: { [0]=10, [1]=9, [2]=10, [3]=9, [4]=9, [5]=10, [6]=10, [7]=9, [8]=10, [11]=9, [12]=9, [13]=9, [14]=9, }
|
|
||||||
-- 8: { [0]=6, [1]=7, [2]=6, [3]=7, [4]=7, [5]=6, [8]=6, [9]=7, [10]=6, [11]=7, [12]=7, [13]=7, [14]=6, }
|
|
||||||
-- 11: { [4]=7, [5]=6, }
|
|
||||||
-- 12: { [4]=1, [5]=0, }
|
|
||||||
-- 15: { [4]=3, [5]=2, }
|
|
||||||
-- 17: { [2]=5, [3]=4, [4]=7, [5]=6, [6]=5, [7]=4, [12]=5, [14]=4, }
|
|
||||||
-- 18: { [4]=1, [5]=0, }
|
|
||||||
-- 19: { [2]=8, [3]=13, [4]=1, [5]=0, [6]=8, [7]=13, [12]=8, [14]=13, }
|
|
||||||
-- 20: { [2]=5, [3]=4, [4]=1, [5]=0, [6]=5, [7]=4, [12]=5, [14]=4, }
|
|
||||||
-- 21: { [4]=13, [5]=8, }
|
|
||||||
-- 22: { [4]=7, [5]=6, }
|
|
||||||
-- 25: { [6]=8, [7]=13, }
|
|
||||||
function shadexfog.createremap(palnum, remaptab)
|
|
||||||
local sht = engine.getshadetab(0)
|
|
||||||
engine.setshadetab(palnum, sht:remap16(remaptab))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create 32 palookups corrensponding to different *shade levels* of a fog
|
|
||||||
-- palookup, called a "shade-x-fog" palookup set in the following.
|
|
||||||
--
|
|
||||||
-- Pals <startpalnum> .. <startpalnum>+31 will be taken.
|
|
||||||
-- <fogr>, <fogg>, <fogb>: intensities of the fog color, [0 .. 255]
|
|
||||||
function shadexfog.create(startpalnum, fogr, fogg, fogb)
|
|
||||||
local MAXPALNUM = 255-31-engine.RESERVEDPALS
|
|
||||||
if (not (startpalnum >= 1 and startpalnum <= MAXPALNUM)) then
|
|
||||||
error("invalid startpalnum, max="..MAXPALNUM, 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local basesht = engine.getshadetab(0)
|
|
||||||
|
|
||||||
-- Encode the shade in different pal numbers! The shade tables are
|
|
||||||
-- constructed with a fog in their place.
|
|
||||||
for dummyshade=0,31 do
|
|
||||||
local sht = engine.shadetab()
|
|
||||||
|
|
||||||
for f=0,31 do
|
|
||||||
for i=0,255 do
|
|
||||||
local r, g, b = engine.getrgb(basesht[dummyshade][i])
|
|
||||||
|
|
||||||
local nr, ng, nb =
|
|
||||||
(r*(32-f) + fogr*f) / 32,
|
|
||||||
(g*(32-f) + fogg*f) / 32,
|
|
||||||
(b*(32-f) + fogb*f) / 32
|
|
||||||
|
|
||||||
sht[f][i] = engine.nearcolor(nr, ng, nb)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
engine.setshadetab(startpalnum + dummyshade, sht)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function trans(what, startpalnum, fogintensity)
|
|
||||||
if (what.pal >= startpalnum and what.pal <= startpalnum+31) then
|
|
||||||
-- Auto-detect earlier translation with the same <startpalnum>.
|
|
||||||
what.shade = what.pal - startpalnum
|
|
||||||
end
|
|
||||||
|
|
||||||
local shade = min(max(what.shade, 0), 31)
|
|
||||||
what.pal = startpalnum + shade
|
|
||||||
what.shade = fogintensity
|
|
||||||
end
|
|
||||||
|
|
||||||
-- shadexfog.translate(startpalnum, fogintensity [, vis])
|
|
||||||
--
|
|
||||||
-- Translate the whole map for use with a shade-x-fog palookup set.
|
|
||||||
-- .pal becomes the <startpalnum> + former .shade
|
|
||||||
-- .shade becomes the <fogintensity> [0 .. 31]
|
|
||||||
-- If <vis> is passed and >= 0, set all sector's visibility to that value.
|
|
||||||
--
|
|
||||||
-- Notes:
|
|
||||||
-- - auto-detects when the translation has been applied with the *same*
|
|
||||||
-- <startpalnum> (if a different one is desired, must reload map).
|
|
||||||
-- - if shades < 0 or > 31 present, loss of information
|
|
||||||
function shadexfog.translate(startpalnum, fogintensity, vis)
|
|
||||||
for i=0,gv.numsectors-1 do
|
|
||||||
trans(sector[i].ceiling, startpalnum, fogintensity)
|
|
||||||
trans(sector[i].floor, startpalnum, fogintensity)
|
|
||||||
if (vis and vis >= 0) then
|
|
||||||
sector[i].visibility = vis
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=0,gv.numwalls-1 do
|
|
||||||
trans(wall[i], startpalnum, fogintensity)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (gv.LUNATIC_CLIENT == gv.LUNATIC_CLIENT_EDUKE32 and LUNATIC_FIRST_TIME) then
|
|
||||||
shadexfog.create(100, 255,255,255)
|
|
||||||
print("created shadexfog palookups")
|
|
||||||
end
|
|
||||||
|
|
||||||
---------- BASE SHADE TABLE TESTS ----------
|
|
||||||
|
|
||||||
-- sht = shadexfog.create_depth_shtab([palnum])
|
|
||||||
function shadexfog.create_depth_shtab(palnum)
|
|
||||||
local sht = engine.shadetab()
|
|
||||||
|
|
||||||
for s=0,31 do
|
|
||||||
for i=0,255 do
|
|
||||||
sht[s][i] = s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (palnum) then
|
|
||||||
engine.setshadetab(palnum, sht)
|
|
||||||
end
|
|
||||||
return sht
|
|
||||||
end
|
|
||||||
|
|
||||||
function shadexfog.create_vismarker_shtab(palnum)
|
|
||||||
local sht = engine.getshadetab(0)
|
|
||||||
|
|
||||||
for i=0,255 do
|
|
||||||
sht[1][i] = 242
|
|
||||||
sht[30][i] = 245
|
|
||||||
end
|
|
||||||
|
|
||||||
if (palnum) then
|
|
||||||
engine.setshadetab(palnum, sht)
|
|
||||||
end
|
|
||||||
return sht
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Basic test of whether for a color index i corresponding to a color (r,g,b),
|
|
||||||
-- getclosestcol() returns a color index ii corresponding to the same color.
|
|
||||||
-- (In the Duke3D palette, there are duplicates, so the requirement i==ii is
|
|
||||||
-- too strict.)
|
|
||||||
function shadexfog.test_nearcolor()
|
|
||||||
for i=0,255 do
|
|
||||||
local r, g, b = engine.getrgb(i)
|
|
||||||
local ii = engine.nearcolor(r, g, b)
|
|
||||||
local rr, gg, bb = engine.getrgb(ii)
|
|
||||||
|
|
||||||
if (r~=rr or g~=gg or b~=bb) then
|
|
||||||
printf("diff %d: %d,%d,%d %d,%d,%d", i, r,g,b, rr,gg,bb)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Change the .pal member of all sector ceilings/floors, walls and sprites to
|
|
||||||
-- <palnum>.
|
|
||||||
function shadexfog.challpal(palnum)
|
|
||||||
for i=0,gv.numsectors-1 do
|
|
||||||
sector[i].ceilingpal = palnum
|
|
||||||
sector[i].floorpal = palnum
|
|
||||||
end
|
|
||||||
for i=0,gv.numwalls-1 do
|
|
||||||
wall[i].pal = palnum
|
|
||||||
end
|
|
||||||
for i in sprite.all() do
|
|
||||||
sprite[i].pal = palnum
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create our version of the base shade table (palookup 0)
|
|
||||||
--
|
|
||||||
-- NOTE: Nope, the base shade table is NOT created by applying a linear ramp to
|
|
||||||
-- the base palette colors!!!
|
|
||||||
local function create_base_shtab(basesht)
|
|
||||||
local basesht = basesht or engine.getshadetab(0)
|
|
||||||
|
|
||||||
local sht = engine.shadetab()
|
|
||||||
sht[0] = basesht[0]
|
|
||||||
for sh=1,31 do
|
|
||||||
for i=0,255-16 do
|
|
||||||
-- NOTE that this fails, see BASESHT_0_NOT_IDENTITY:
|
|
||||||
-- assert(basesht[0][i] == i)
|
|
||||||
local r, g, b = engine.getrgb(i)
|
|
||||||
local f = 1
|
|
||||||
r = ((32-f*sh+0.5)*r)/32
|
|
||||||
g = ((32-f*sh+0.5)*g)/32
|
|
||||||
b = ((32-f*sh+0.5)*b)/32
|
|
||||||
r, g, b = max(0,r), max(0,g), max(0,b) -- if f is > 1
|
|
||||||
sht[sh][i] = engine.nearcolor(r, g, b)
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=255-16+1,255 do
|
|
||||||
-- fullbrights
|
|
||||||
sht[sh][i] = basesht[0][i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return sht
|
|
||||||
end
|
|
||||||
|
|
||||||
local function create_base_shtab_2(basesht)
|
|
||||||
local basesht = basesht or engine.getshadetab(0)
|
|
||||||
|
|
||||||
local perm16 = { [0]=0,1, 2,3, 5,4, 6,7, 8,13, 10,11, 12,9, 14,15 }
|
|
||||||
basesht = basesht:remap16(perm16)
|
|
||||||
|
|
||||||
local iperm16 = {}
|
|
||||||
for i=0,15 do
|
|
||||||
iperm16[perm16[i]] = i
|
|
||||||
end
|
|
||||||
|
|
||||||
local iperm = {}
|
|
||||||
for i=0,255 do
|
|
||||||
iperm[i] = 16*(iperm16[floor(i/16)]) + i%16
|
|
||||||
end
|
|
||||||
|
|
||||||
local baseidx = {}
|
|
||||||
for i=0,255-16 do
|
|
||||||
baseidx[i] = i < 192 and 32*floor(i/32) or 16*floor(i/16)
|
|
||||||
end
|
|
||||||
|
|
||||||
local sht = engine.shadetab()
|
|
||||||
|
|
||||||
for sh=0,31 do
|
|
||||||
for i=0,255-16 do
|
|
||||||
local bi = baseidx[i]
|
|
||||||
local cidx = bi + floor(((31-sh)*(i - bi))/31)
|
|
||||||
sht[sh][i] = iperm[cidx]
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=255-16+1,255 do
|
|
||||||
-- fullbrights
|
|
||||||
sht[sh][i] = basesht[0][i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return sht:remap16(iperm16)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ismapster32 = (gv.LUNATIC_CLIENT == gv.LUNATIC_CLIENT_MAPSTER32)
|
|
||||||
|
|
||||||
if (ismapster32) then
|
|
||||||
-- Wrapper around engine.savePaletteDat() that errors on unexpected events.
|
|
||||||
function shadexfog.save(filename, palnum, blendnum, moreblends, lognumalphatabs)
|
|
||||||
local ok, errmsg, nummoreblends = engine.savePaletteDat(
|
|
||||||
filename, palnum, blendnum, moreblends, lognumalphatabs)
|
|
||||||
if (not ok) then
|
|
||||||
error(errmsg)
|
|
||||||
end
|
|
||||||
|
|
||||||
printf('Wrote base palette, shade and translucency tables to "%s".', filename)
|
|
||||||
if (nummoreblends > 0) then
|
|
||||||
printf(" Also wrote %d additional translucency tables.", nummoreblends)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function shadexfog.saveLookupDat(filename, lookups)
|
|
||||||
local ok, errmsg, numlookups = engine.saveLookupDat(filename, lookups)
|
|
||||||
if (not ok) then
|
|
||||||
error(errmsg)
|
|
||||||
end
|
|
||||||
|
|
||||||
printf('Wrote %d lookup tables and 5 base palettes to "%s".',
|
|
||||||
numlookups, filename)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Create our (failed) version of the base shade table at set it to palookup
|
|
||||||
-- number <palnum>.
|
|
||||||
-- <secver>: use second attempt?
|
|
||||||
function shadexfog.create0(palnum, secver)
|
|
||||||
local sht0 = secver and create_base_shtab_2() or create_base_shtab()
|
|
||||||
engine.setshadetab(palnum, sht0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function shadexfog.test_create0()
|
|
||||||
local basesht = engine.getshadetab(0)
|
|
||||||
|
|
||||||
for i=0,255 do
|
|
||||||
if (basesht[0][i] ~= i) then
|
|
||||||
-- BASESHT_0_NOT_IDENTITY
|
|
||||||
printf("Base shade table at index %d: %d", i, basesht[0][i])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local sht = create_base_shtab(basesht)
|
|
||||||
|
|
||||||
local ok = true
|
|
||||||
for sh=1,31 do
|
|
||||||
for i=0,255 do
|
|
||||||
local ouri, origi = sht[sh][i], basesht[sh][i]
|
|
||||||
-- if (sht[sh][i] ~= basesht[sh][i]) then
|
|
||||||
if (math.abs(ouri - origi) > 1) then
|
|
||||||
printf("Constructed shade table DIFFERS AT shade %d index %d: orig %d ours %d",
|
|
||||||
sh, i, basesht[sh][i], sht[sh][i])
|
|
||||||
ok = false
|
|
||||||
goto out
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
::out::
|
|
||||||
if (ok) then
|
|
||||||
printf("Constructed shade table IDENTICAL WITH original one")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
---------- Blending table tests ----------
|
|
||||||
|
|
||||||
-- shadexfog.create_trans(startblendidx, func [, numtables [, fullbrightsOK]])
|
|
||||||
--
|
|
||||||
-- <func>: must be
|
|
||||||
-- rr, gg, bb = f(r,g,b, R,G,B, level, numtables)
|
|
||||||
-- If reverse translucency bit clear, (r,g,b) is background and (R,G,B) is
|
|
||||||
-- foreground (incoming).
|
|
||||||
-- ('level' is the table index, from 1 to <numtables>)
|
|
||||||
-- <numtables>: number of tables to create, from <startblendidx> on. Default: 1
|
|
||||||
function shadexfog.create_trans(startblendidx, func, numtables, fullbrightsOK)
|
|
||||||
numtables = numtables or 1
|
|
||||||
local lastokcol = fullbrightsOK and 255 or 255-16
|
|
||||||
|
|
||||||
local tab = engine.blendtab()
|
|
||||||
|
|
||||||
for level=1,numtables do
|
|
||||||
for i=0,255 do
|
|
||||||
local r,g,b = engine.getrgb(i)
|
|
||||||
for j=0,255 do
|
|
||||||
local R,G,B = engine.getrgb(j)
|
|
||||||
|
|
||||||
local rr, gg, bb = func(r,g,b, R,G,B, level, numtables)
|
|
||||||
tab[i][j] = engine.nearcolor(rr, gg, bb, lastokcol)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
engine.setblendtab(startblendidx + level-1, tab)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function check_numtables(numtables)
|
|
||||||
if (numtables ~= nil) then
|
|
||||||
if (type(numtables) ~= "number" or not (numtables >= 1 and numtables <= 128)) then
|
|
||||||
error("invalid argument #2: must be a number in [1 .. 128]", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (bit.band(numtables, numtables-1) ~= 0) then
|
|
||||||
error("invalid argument #2: must be a power of two", 2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- shadexfog.create_alpha_trans(startblendidx [, numtables [, fullbrightsOK]])
|
|
||||||
--
|
|
||||||
-- Creates <numtables> blending tables of smooth alpha translucency, with
|
|
||||||
-- fractions 1/(2*numtables), 2/(2*numtables) ... numtables/(2*numtables).
|
|
||||||
-- <numtables> must be a power of two in [1 .. 128].
|
|
||||||
function shadexfog.create_alpha_trans(startblendidx, numtables, fullbrightsOK)
|
|
||||||
check_numtables(numtables)
|
|
||||||
|
|
||||||
shadexfog.create_trans(
|
|
||||||
startblendidx,
|
|
||||||
|
|
||||||
function(r,g,b, R,G,B, alpha, numtabs)
|
|
||||||
local f = alpha/(2*numtabs)
|
|
||||||
local F = 1-f
|
|
||||||
return f*r+F*R, f*g+F*G, f*b+F*B
|
|
||||||
end,
|
|
||||||
|
|
||||||
numtables, fullbrightsOK
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- shadexfog.create_additive_trans(startblendidx [, numtables [, fullbrightsOK]])
|
|
||||||
function shadexfog.create_additive_trans(startblendidx, numtables, fullbrightsOK)
|
|
||||||
shadexfog.create_trans(
|
|
||||||
startblendidx,
|
|
||||||
|
|
||||||
function(r,g,b, R,G,B, level, numtabs)
|
|
||||||
local f = level/numtabs
|
|
||||||
return min(f*r+R, 255), min(f*g+G, 255), min(f*b+B, 255)
|
|
||||||
end,
|
|
||||||
|
|
||||||
numtables, fullbrightsOK
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- shadexfog.create_brightpass_trans(startblendidx [, numtables [, fullbrightsOK]])
|
|
||||||
function shadexfog.create_brightpass_trans(startblendidx, numtables, fullbrightsOK)
|
|
||||||
shadexfog.create_trans(
|
|
||||||
startblendidx,
|
|
||||||
|
|
||||||
function(r,g,b, R,G,B, alpha, numtabs)
|
|
||||||
local a = alpha/numtabs
|
|
||||||
local F = 1 - min(a, (R+G+B) / (3*255))
|
|
||||||
local f = 1 - F
|
|
||||||
return f*r+F*R, f*g+F*G, f*b+F*B
|
|
||||||
end,
|
|
||||||
|
|
||||||
numtables, fullbrightsOK
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not ismapster32) then
|
|
||||||
return shadexfog
|
|
||||||
end
|
|
||||||
|
|
||||||
--========== Mapster32 Lua menu hooks ==========--
|
|
||||||
|
|
||||||
local getnumber16 = engine.getnumber16
|
|
||||||
local GNF = engine.GETNUMFLAG
|
|
||||||
local GNF_BOOL = GNF.NEXTFREE
|
|
||||||
|
|
||||||
local df = GNF.RET_M1_ON_CANCEL -- default getnumber16() flags
|
|
||||||
|
|
||||||
local MAXUSERPALOOKUP = 256-1-8 -- KEEPINSYNC engine.lua:check_palidx()
|
|
||||||
|
|
||||||
-- wrapped_func = CreateMenuFunction(argdesc)
|
|
||||||
--
|
|
||||||
-- <argdesc>: table with [0]=<func> and then, entries { name, init, max [, noret] }
|
|
||||||
local function CreateMenuFunction(argdesc)
|
|
||||||
return function()
|
|
||||||
local func = argdesc[0]
|
|
||||||
assert(type(func) == "function")
|
|
||||||
local args = {}
|
|
||||||
|
|
||||||
for i=1,#argdesc do
|
|
||||||
local ad = argdesc[i]
|
|
||||||
assert(type(ad) == "table" and #ad == 3 or #ad == 4)
|
|
||||||
|
|
||||||
local moreflags = ad[4] or 0
|
|
||||||
args[i] = getnumber16(ad[1]..": ", ad[2], ad[3], bit.bor(df, moreflags))
|
|
||||||
if (bit.band(moreflags, GNF.NEG_ALLOWED)==0 and args[i] < 0) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if (bit.band(moreflags, GNF_BOOL)~=0) then
|
|
||||||
args[i] = (args[i] > 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
func(unpack(args))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Replace chevrons (angle brackets!) with printext16 markup.
|
|
||||||
local function replchev(matchstr) return "^15"..matchstr:sub(2,-2).."^O" end
|
|
||||||
-- Replace ASCII code escapes like \XXX. We can't use these escapes in Lua [[ ... ]] strings.
|
|
||||||
local function replascii(matchstr) return string.char(tonumber(matchstr)) end
|
|
||||||
-- Format a whole string for the menu system:
|
|
||||||
local function formatHelp(str)
|
|
||||||
return str:gsub(
|
|
||||||
"(%b<>)", replchev):gsub(
|
|
||||||
"%*(.-)%*", "^014%1^O"):gsub(
|
|
||||||
"%\\(%d+)", replascii)
|
|
||||||
end
|
|
||||||
|
|
||||||
----------
|
|
||||||
|
|
||||||
engine.clearMenu()
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Create shadexfog palset",
|
|
||||||
CreateMenuFunction{
|
|
||||||
[0] = shadexfog.create,
|
|
||||||
{ "Starting palnum", 100, MAXUSERPALOOKUP-31 },
|
|
||||||
{ "Red fog color [0-255]", 0, 255 },
|
|
||||||
{ "Green fog color [0-255]", 0, 255 },
|
|
||||||
{ "Blue fog color [0-255]", 0, 255 },
|
|
||||||
},
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<shadexfog.create(startpalnum, fogr, fogg, fogb)>
|
|
||||||
<_______________________________________________>
|
|
||||||
|
|
||||||
Creates 32 shade tables corresponding to different *shade levels*
|
|
||||||
of a fog palookup, together called a *shade-x-fog* palookup set.
|
|
||||||
|
|
||||||
Pals <startpalnum> to <startpalnum>+31 will be taken.
|
|
||||||
<fogr>, <fogg>, <fogb>: intensities of the fog color, [0 .. 255]
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Translate map for shxfog",
|
|
||||||
CreateMenuFunction{
|
|
||||||
[0] = shadexfog.translate,
|
|
||||||
{ "Starting palnum", 100, MAXUSERPALOOKUP-31 },
|
|
||||||
{ "Fog intensity [0-31]", 0, 31 },
|
|
||||||
{ "Change all sectors' visibility to (Esc: don't)", 0, 255, GNF.NEG_ALLOWED },
|
|
||||||
},
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<shadexfog.translate(startpalnum, fogintensity [, vis])>
|
|
||||||
<______________________________________________________>
|
|
||||||
|
|
||||||
Translates the whole map for use with a shade-x-fog palookup set.
|
|
||||||
|
|
||||||
.pal becomes the <startpalnum> + former .shade
|
|
||||||
.shade becomes the <fogintensity> [0 .. 31]
|
|
||||||
|
|
||||||
If <vis> is passed and >= 0, set all sector's visibility to that
|
|
||||||
value.
|
|
||||||
|
|
||||||
*Notes:*
|
|
||||||
- auto-detects when the translation has been applied with the *same*
|
|
||||||
<startpalnum> (if a different one is desired, must reload map).
|
|
||||||
- if shades > 31 or < 0 present, there is loss of information
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Change pal of everything",
|
|
||||||
CreateMenuFunction{
|
|
||||||
[0] = shadexfog.challpal,
|
|
||||||
{ "Pal to change to", 0, MAXUSERPALOOKUP },
|
|
||||||
},
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<shadexfog.challpal(palnum)>
|
|
||||||
<__________________________>
|
|
||||||
|
|
||||||
Changes the .pal member of all sector ceilings/floors, walls and
|
|
||||||
sprites to <palnum>.
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Create alpha trans. tabs",
|
|
||||||
CreateMenuFunction{
|
|
||||||
[0] = shadexfog.create_alpha_trans,
|
|
||||||
{ "Starting blendnum", 1, 255 },
|
|
||||||
{ "Number of blending tables", 32, 255 },
|
|
||||||
{ "Fullbright result colors OK?", 0, 1, GNF_BOOL },
|
|
||||||
},
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[=[
|
|
||||||
<shadexfog.create_alpha_trans(startblend [, numtables [, fullbriOK]])>
|
|
||||||
<____________________________________________________________________>
|
|
||||||
|
|
||||||
Creates <numtables> blending tables of smooth alpha translucency,
|
|
||||||
starting with the blending number <startblend>, with values of alpha
|
|
||||||
|
|
||||||
1/(2\255<numtables>), 2/(2\255<numtables>) ... <numtables>/(2\255<numtables>).
|
|
||||||
|
|
||||||
<numtables> must be a power of two in [1 .. 128].
|
|
||||||
|
|
||||||
<fullbriOK>: should fullbright color indices (>= 240) be permitted as
|
|
||||||
the blending result of two color indices?
|
|
||||||
]=]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Create addtv. trans. tabs",
|
|
||||||
CreateMenuFunction{
|
|
||||||
[0] = shadexfog.create_additive_trans,
|
|
||||||
{ "Starting blendnum", 1, 255 },
|
|
||||||
{ "Number of blending tables", 32, 255 },
|
|
||||||
{ "Fullbright result colors OK?", 0, 1, GNF_BOOL },
|
|
||||||
},
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[=[
|
|
||||||
<shadexfog.create_additive_trans(startbl [, numtables [, fullbriOK]])>
|
|
||||||
<____________________________________________________________________>
|
|
||||||
|
|
||||||
Creates <numtables> blending tables of smooth additive translucency,
|
|
||||||
starting with the blending number <startbl>, with factors for the
|
|
||||||
background color
|
|
||||||
|
|
||||||
1/<numtables>, 2/<numtables> ... <numtables>/<numtables>.
|
|
||||||
|
|
||||||
<numtables> must be a power of two in [1 .. 128].
|
|
||||||
|
|
||||||
<fullbriOK>: should fullbright color indices (>= 240) be permitted as
|
|
||||||
the blending result of two color indices?
|
|
||||||
]=]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Create bri.pass tr. tabs",
|
|
||||||
CreateMenuFunction{
|
|
||||||
[0] = shadexfog.create_brightpass_trans,
|
|
||||||
{ "Starting blendnum", 1, 255 },
|
|
||||||
{ "Number of blending tables", 32, 255 },
|
|
||||||
{ "Fullbright result colors OK?", 0, 1, GNF_BOOL },
|
|
||||||
},
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[=[
|
|
||||||
<shadexfog.create_brightpass_trans(startbl [, numtabs [, fullbriOK]])>
|
|
||||||
<____________________________________________________________________>
|
|
||||||
|
|
||||||
Creates <numtabs> blending tables of "brightpass" translucency,
|
|
||||||
starting with the blending number <startbl>, with fractions
|
|
||||||
|
|
||||||
1/<numtables>, 2/<numtables> ... <numtables>/<numtables>.
|
|
||||||
|
|
||||||
<fullbriOK>: should fullbright color indices (>= 240) be permitted as
|
|
||||||
the blending result of two color indices?
|
|
||||||
]=]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Create base shade table",
|
|
||||||
CreateMenuFunction{
|
|
||||||
[0] = shadexfog.create0,
|
|
||||||
{ "Pal number", 100, MAXUSERPALOOKUP },
|
|
||||||
{ "Second attempt?", 1, 1, GNF_BOOL },
|
|
||||||
},
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<shadexfog.create0(palnum, secver)>
|
|
||||||
<_________________________________>
|
|
||||||
|
|
||||||
Creates our version of the base shade table at set it to palookup
|
|
||||||
number <palnum>.
|
|
||||||
|
|
||||||
<secver>: use second attempt instead of the first? This one is more
|
|
||||||
similar to the base shade table shipped with Duke3D, but still
|
|
||||||
shows significant differences.
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Create c.index remapping",
|
|
||||||
function()
|
|
||||||
local palnum = getnumber16("Pal number: ", 100, MAXUSERPALOOKUP)
|
|
||||||
if (palnum < 0) then return end
|
|
||||||
|
|
||||||
local remaptab = {}
|
|
||||||
while (true) do
|
|
||||||
local srchex = getnumber16("Source hexadecatuple (0: finish): ", 0, 14)
|
|
||||||
if (srchex < 0) then return end
|
|
||||||
local dsthex = getnumber16("Destn. hexadecatuple (0: finish): ", 0, 14)
|
|
||||||
if (dsthex < 0) then return end
|
|
||||||
|
|
||||||
if (srchex == 0 and dsthex == 0) then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
remaptab[srchex] = dsthex
|
|
||||||
end
|
|
||||||
|
|
||||||
shadexfog.createremap(palnum, remaptab)
|
|
||||||
end,
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<shadexfog.createremap(palnum, remaptab)>
|
|
||||||
<_______________________________________>
|
|
||||||
|
|
||||||
Creates a color index remapping expressed as mappings of sexdeca-
|
|
||||||
tuples (16-tuples) of the base palette at pal <palnum>.
|
|
||||||
|
|
||||||
Duke3D's default base palette can be considered to consist of six
|
|
||||||
ramps of 32 colors each, three ramps of 16 colors each and a
|
|
||||||
remaining fullbright color set. The sexdecatuples are as follows:
|
|
||||||
|
|
||||||
< 0, 1>: gray ramp
|
|
||||||
< 2, 3>: skin color ramp
|
|
||||||
< 5, 4>: blue ramp (note that the 16-tuples are in reverse order)
|
|
||||||
< 6, 7>: nightvision yellow/green
|
|
||||||
< 8, 13>: red ramp
|
|
||||||
< 10, 11>: almost gray ramp, but with a slight red hue
|
|
||||||
|
|
||||||
< 9>: yellow (slightly more red than green)
|
|
||||||
< 12>: "dirty" orange
|
|
||||||
< 14>: blue-purple-red
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
local function getNumberRange(what, whating)
|
|
||||||
local str = engine.getstring("Additional "..what.." numbers (e.g. '64,100-131,255'): ")
|
|
||||||
if (str == nil) then return end
|
|
||||||
if (str == "") then return {} end
|
|
||||||
|
|
||||||
if (not str:find("^[%d,%-]+$")) then
|
|
||||||
error("Additional "..whating.." numbers string must contain only digits or ',' or '-'", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local moreblends = {}
|
|
||||||
local didnumstr = {}
|
|
||||||
|
|
||||||
for n1, n2 in str:gmatch("(%d+)%-(%d+)") do -- parse number ranges
|
|
||||||
moreblends[#moreblends+1] = { tonumber(n1), tonumber(n2) }
|
|
||||||
didnumstr[n1] = true
|
|
||||||
didnumstr[n2] = true
|
|
||||||
end
|
|
||||||
|
|
||||||
for n in str:gmatch("%d+") do -- parse single numbers
|
|
||||||
if (not didnumstr[n]) then
|
|
||||||
moreblends[#moreblends+1] = tonumber(n)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return moreblends
|
|
||||||
end
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Save pal+sh+trans DAT f.",
|
|
||||||
function()
|
|
||||||
local filename = engine.getstring("File name: ")
|
|
||||||
if (filename == nil) then return end
|
|
||||||
|
|
||||||
local palnum = getnumber16("Pal number of base shade table: ", 0, MAXUSERPALOOKUP)
|
|
||||||
if (palnum < 0) then return end
|
|
||||||
local blendnum = getnumber16("Blendnum of base transluc. table: ", 0, 255)
|
|
||||||
if (blendnum < 0) then return end
|
|
||||||
|
|
||||||
local moreblends = getNumberRange("blend", "blending")
|
|
||||||
if (moreblends == nil) then return end
|
|
||||||
|
|
||||||
local lognumalphatabs
|
|
||||||
if (#moreblends > 0) then
|
|
||||||
lognumalphatabs = getnumber16("log2 of last alpha blending table index (1-7, 0: none): ", 0, 7)
|
|
||||||
if (lognumalphatabs < 0) then return end
|
|
||||||
if (lognumalphatabs == 0) then lognumalphatabs = nil end
|
|
||||||
end
|
|
||||||
|
|
||||||
shadexfog.save(filename, palnum, blendnum, moreblends, lognumalphatabs)
|
|
||||||
end,
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<shadexfog.save(filename, palnum, blendnum, moreblends, lognumalpha)>
|
|
||||||
<___________________________________________________________________>
|
|
||||||
|
|
||||||
Writes out a full PALETTE.DAT-formatted file named <filename> with the
|
|
||||||
base shade table numbered <palnum> and the base translucency table
|
|
||||||
numbered <blendnum>.
|
|
||||||
|
|
||||||
Finally, you are asked to specify additional blending tables that can
|
|
||||||
be stored in EDuke32's extended PALETTE.DAT format. If one or more
|
|
||||||
additional blending table is specified, you are also queried for the
|
|
||||||
log2 of the last alpha blending table index, <lognumalpha>. Since alpha
|
|
||||||
blending tables are assumed to be set up at indices 1 to
|
|
||||||
exp(2, <lognumalpha>), it is also the log2 of their total count.
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Save lookups DAT file",
|
|
||||||
function()
|
|
||||||
local filename = engine.getstring("File name: ")
|
|
||||||
if (filename ~= nil and filename ~= "") then
|
|
||||||
local lookups = {
|
|
||||||
-- Duke3D 1.5 LOOKUP.DAT order
|
|
||||||
1,2,6,7,8, 3,4,5,9,10,
|
|
||||||
12,13,15,16,18, 19,11,14,17,20,
|
|
||||||
21,22,23,24,25
|
|
||||||
}
|
|
||||||
|
|
||||||
local morelookups = getNumberRange("lookup", "lookup")
|
|
||||||
if (morelookups == nil) then return end
|
|
||||||
|
|
||||||
if (#morelookups > 0) then
|
|
||||||
for i=1,#morelookups do
|
|
||||||
lookups[#lookups+1] = morelookups[i]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shadexfog.saveLookupDat(filename, lookups)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<shadexfog.saveLookupDat(filename, lookups)>
|
|
||||||
<__________________________________________>
|
|
||||||
|
|
||||||
Saves the color index lookups (i.e. first 256 values of each shade
|
|
||||||
table) of the pal numbers which have lookups in Duke3D's unaltered
|
|
||||||
LOOKUP.DAT, plus optional ones provided by the user.
|
|
||||||
|
|
||||||
The default ones are, in this order:
|
|
||||||
1,2,6,7,8, 3,4,5,9,10, 12,13,15,16,18, 19,11,14,17,20, 21,22,23,24,25.
|
|
||||||
(All pal numbers from 1 to 25 are present.)
|
|
||||||
|
|
||||||
<filename>: the name of the LOOKUP.DAT-formatted file to create
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc("_________DEBUG_________", function() end
|
|
||||||
--[[
|
|
||||||
,
|
|
||||||
" \01\02\03\04\05\06\07\08\09\10\11\12\13\14\15\
|
|
||||||
\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31\
|
|
||||||
\128\129\130\131\132\133\134\135\136\137\138\139\140\141\142\143\
|
|
||||||
\144\145\146\147\148\149\150\151\152\153\154\155\156\157\158\159\
|
|
||||||
\160\161\162\163\164\165\166\167\168\169\170\171\172\173\174\175\
|
|
||||||
\176\177\178\179\180\181\182\183\184\185\186\187\188\189\190\191\
|
|
||||||
\192\193\194\195\196\197\198\199\200\201\202\203\204\205\206\207\
|
|
||||||
\208\209\210\211\212\213\214\215\216\217\218\219\220\221\222\223\
|
|
||||||
\224\225\226\227\228\229\230\231\232\233\234\235\236\237\238\239\
|
|
||||||
\240\241\242\243\244\245\246\247\248\249\250\251\252\253\254\255"
|
|
||||||
--]]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc("Setup dbg. water basepal", engine.setupDebugBasePal,
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<engine.setupDebugBasePal()>
|
|
||||||
<__________________________>
|
|
||||||
|
|
||||||
Overwrites the water base palette with one where each 16-tuple
|
|
||||||
(except the fullbrights) consists of a single representative
|
|
||||||
color.
|
|
||||||
|
|
||||||
This can be used to get a quick glimpse about what ramps are present
|
|
||||||
in particular tiles. With this information, custom lookups can be
|
|
||||||
created more directedly with the <Create c.index remapping> menu
|
|
||||||
entry.
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Create depth shade tab",
|
|
||||||
CreateMenuFunction{
|
|
||||||
[0] = shadexfog.create_depth_shtab,
|
|
||||||
{ "Pal number", 100, MAXUSERPALOOKUP },
|
|
||||||
},
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<shadexfog.create_depth_shtab(palnum)>
|
|
||||||
<____________________________________>
|
|
||||||
|
|
||||||
Creates a shade table for debugging purposes at pal <palnum>.
|
|
||||||
|
|
||||||
For every color index, the shade table maps shade index <i> to
|
|
||||||
color index <i> (of the first ramp of gray colors, assuming Build
|
|
||||||
has loaded a base shade table with 32 shade levels).
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc(
|
|
||||||
"Create vismarker sh. tab",
|
|
||||||
CreateMenuFunction{
|
|
||||||
[0] = shadexfog.create_vismarker_shtab,
|
|
||||||
{ "Pal number", 100, MAXUSERPALOOKUP },
|
|
||||||
},
|
|
||||||
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<shadexfog.create_vismarker_shtab(palnum)>
|
|
||||||
<________________________________________>
|
|
||||||
|
|
||||||
Creates a shade table for debugging purposes at pal <palnum>.
|
|
||||||
|
|
||||||
For every color index, the shade table maps shade index 1 to
|
|
||||||
a ^14bright yellow^O color and shade index 30 to a ^13purple^O color.
|
|
||||||
Thus, it can be useful in visualizing the limits of the
|
|
||||||
fog/visibility attenuation.
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
engine.registerMenuFunc("Linearize default basep.", engine.linearizeBasePal,
|
|
||||||
formatHelp
|
|
||||||
[[
|
|
||||||
<engine.linearizeBasePal()>
|
|
||||||
<_________________________>
|
|
||||||
|
|
||||||
Overwrites the default base palette with one where certain ramps have
|
|
||||||
their attenuation linearized. This is mainly useful for debugging
|
|
||||||
purposes as it excludes the effect of this nonlinearity for
|
|
||||||
comparison of fog/visibility between classic and OpenGL modes.
|
|
||||||
]]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
do
|
|
||||||
return shadexfog
|
|
||||||
end
|
|
|
@ -1,139 +0,0 @@
|
||||||
// Timing test for four 0..MAXSPRITES-1 access loops,
|
|
||||||
// fwd->back->fwd->back.
|
|
||||||
// Results with a LTO=1 RELEASE=1 C-CON build on x86: 42 ms for N=10.
|
|
||||||
// (Compare with Lunatic results in test.elua)
|
|
||||||
|
|
||||||
define N 10
|
|
||||||
gamevar n 0 0
|
|
||||||
gamevar i 0 0
|
|
||||||
gamevar t 0 0
|
|
||||||
gamevar t2 0 0
|
|
||||||
define MAXSPRITES 16384
|
|
||||||
define MAXSPRM1 16383 // MAXSPRITES-1
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
getticks t
|
|
||||||
|
|
||||||
setvar n 0
|
|
||||||
whilevarn n N
|
|
||||||
{
|
|
||||||
setvar i 0
|
|
||||||
whilevarvarn i MAXSPRITES
|
|
||||||
{
|
|
||||||
setactor[i].blend 1
|
|
||||||
addvar i 1
|
|
||||||
}
|
|
||||||
|
|
||||||
setvar i MAXSPRM1
|
|
||||||
whilevarvarn i -1
|
|
||||||
{
|
|
||||||
setactor[i].shade 1
|
|
||||||
subvar i 1
|
|
||||||
}
|
|
||||||
|
|
||||||
setvar i 0
|
|
||||||
whilevarvarn i MAXSPRITES
|
|
||||||
{
|
|
||||||
setactor[i].xoffset 0
|
|
||||||
addvar i 1
|
|
||||||
}
|
|
||||||
|
|
||||||
setvar i MAXSPRM1
|
|
||||||
whilevarvarn i -1
|
|
||||||
{
|
|
||||||
setactor[i].yoffset 0
|
|
||||||
subvar i 1
|
|
||||||
}
|
|
||||||
|
|
||||||
addvar n 1
|
|
||||||
}
|
|
||||||
|
|
||||||
getticks t2
|
|
||||||
subvarvar t2 t
|
|
||||||
|
|
||||||
// qsprintf test
|
|
||||||
redefinequote 116 0..MAXSPITES-1
|
|
||||||
redefinequote 117 in total
|
|
||||||
|
|
||||||
redefinequote 114 %d x four %s iterations took %d ms %s
|
|
||||||
qsprintf 115 /*<-*/ 114 /*args:*/ n 116 t2 117
|
|
||||||
|
|
||||||
echo 115
|
|
||||||
endevent
|
|
||||||
|
|
||||||
|
|
||||||
////////// Periodically alpha-faded liztroop //////////
|
|
||||||
|
|
||||||
gamevar alpha 0 0
|
|
||||||
gamevar tmp 0 0
|
|
||||||
|
|
||||||
onevent EVENT_GAME // XXX: better: in EVENT_EGS + EVENT_LOADACTOR
|
|
||||||
ifactor LIZTROOP
|
|
||||||
{
|
|
||||||
getactor[THISACTOR].mdflags tmp
|
|
||||||
orvar tmp 16
|
|
||||||
setactor[THISACTOR].mdflags tmp
|
|
||||||
}
|
|
||||||
endevent
|
|
||||||
|
|
||||||
define NUMALPHATABS 128
|
|
||||||
define C1 0 //must be log2(128/NUMALPHATABS)
|
|
||||||
define C2 257 // must be 2*NUMALPHATABS+1
|
|
||||||
|
|
||||||
define ALPHA_TABS_BY_EDUKE32 1 // assume alpha->blend handling by EDuke32
|
|
||||||
|
|
||||||
onevent EVENT_ANIMATESPRITES
|
|
||||||
setvarvar tmp totalclock
|
|
||||||
shiftvarl tmp 2
|
|
||||||
|
|
||||||
sin alpha tmp // alpha is now in [-2^14 .. 2^14]
|
|
||||||
shiftvarr alpha 7 // [-2^7 .. 2^7]
|
|
||||||
addvar alpha 128 // [0 .. 256]
|
|
||||||
shiftvarr alpha C1 // [0 .. 2*NUMALPHATABS]
|
|
||||||
|
|
||||||
ifvarn ALPHA_TABS_BY_EDUKE32 0
|
|
||||||
{
|
|
||||||
subvar alpha 1
|
|
||||||
ifvarl alpha 0 setvar alpha 0
|
|
||||||
setactor[THISACTOR].alpha alpha
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
ifvare alpha 0
|
|
||||||
{
|
|
||||||
// clear translucent bits, 0xfdfe == 0xffff-(1+512)
|
|
||||||
gettspr[THISACTOR].tsprcstat tmp
|
|
||||||
andvar tmp 0xfdfe
|
|
||||||
settspr[THISACTOR].tsprcstat tmp
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gettspr[THISACTOR].tsprcstat tmp
|
|
||||||
orvar tmp 2
|
|
||||||
settspr[THISACTOR].tsprcstat tmp
|
|
||||||
|
|
||||||
// Assume blending tables [1 .. NUMALPHATABS] are installed, like
|
|
||||||
// generated by shadexfog.lua:create_alpha_trans(1, NUMALPHATABS).
|
|
||||||
ifvarg alpha NUMALPHATABS
|
|
||||||
{
|
|
||||||
setvarvar tmp C2
|
|
||||||
subvarvar tmp alpha
|
|
||||||
setvarvar alpha tmp
|
|
||||||
|
|
||||||
// Set "reverse translucent" cstat bit
|
|
||||||
gettspr[THISACTOR].tsprcstat tmp
|
|
||||||
orvar tmp 512
|
|
||||||
settspr[THISACTOR].tsprcstat tmp
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Clear cstat bit 512
|
|
||||||
gettspr[THISACTOR].tsprcstat tmp
|
|
||||||
andvar tmp 0xfdff
|
|
||||||
settspr[THISACTOR].tsprcstat tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
settspr[THISACTOR].tsprblend alpha
|
|
||||||
}
|
|
||||||
endevent
|
|
|
@ -1,31 +0,0 @@
|
||||||
|
|
||||||
gamevar tmp 0 0
|
|
||||||
gamevar cs 0 0
|
|
||||||
|
|
||||||
state setup_animatesprites
|
|
||||||
getactor[THISACTOR].mdflags tmp
|
|
||||||
orvar tmp 16
|
|
||||||
setactor[THISACTOR].mdflags tmp
|
|
||||||
ends
|
|
||||||
|
|
||||||
onevent EVENT_EGS
|
|
||||||
state setup_animatesprites
|
|
||||||
endevent
|
|
||||||
|
|
||||||
onevent EVENT_LOADACTOR
|
|
||||||
state setup_animatesprites
|
|
||||||
endevent
|
|
||||||
|
|
||||||
// Assuming a PALETTE.DAT with e.g.:
|
|
||||||
// blend 0: 50/50 alpha
|
|
||||||
// blend 1: factor 1.0 additive
|
|
||||||
//
|
|
||||||
// See discussion starting from
|
|
||||||
// http://forums.duke4.net/topic/775-eduke32-20-and-polymer/page__view__findpost__p__213408
|
|
||||||
onevent EVENT_ANIMATESPRITES
|
|
||||||
getactor[THISACTOR].cstat cs
|
|
||||||
ifvarand cs 2 ifvarand cs 512
|
|
||||||
setactor[THISACTOR].blend 1
|
|
||||||
else
|
|
||||||
setactor[THISACTOR].blend 0
|
|
||||||
endevent
|
|
|
@ -1,128 +0,0 @@
|
||||||
gamevar j 0 0
|
|
||||||
gamevar k 0 0
|
|
||||||
|
|
||||||
definequote 400 QWE
|
|
||||||
|
|
||||||
definequote 500 ===
|
|
||||||
|
|
||||||
onevent EVENT_ENTERLEVEL
|
|
||||||
switch j
|
|
||||||
case 0
|
|
||||||
case -1
|
|
||||||
userquote 29
|
|
||||||
break
|
|
||||||
case 1
|
|
||||||
userquote 30
|
|
||||||
break
|
|
||||||
default
|
|
||||||
userquote 31
|
|
||||||
break
|
|
||||||
case 0
|
|
||||||
redefinequote 400 ASD
|
|
||||||
userquote 400
|
|
||||||
break
|
|
||||||
endswitch
|
|
||||||
|
|
||||||
addvar j 1
|
|
||||||
|
|
||||||
switch j
|
|
||||||
case 0
|
|
||||||
case -2
|
|
||||||
userquote 29
|
|
||||||
break
|
|
||||||
case 3
|
|
||||||
userquote 30
|
|
||||||
break
|
|
||||||
default
|
|
||||||
userquote 31
|
|
||||||
break
|
|
||||||
endswitch
|
|
||||||
|
|
||||||
// test "-" codegen (must not generate comment!).
|
|
||||||
subvar j -3
|
|
||||||
|
|
||||||
// result:
|
|
||||||
// ASD (from second case 0)
|
|
||||||
// BRIGHTNESS LEVEL: THREE (from default)
|
|
||||||
|
|
||||||
|
|
||||||
////////// test nested switch/while //////////
|
|
||||||
|
|
||||||
ifvare 0 1
|
|
||||||
break // outer level: "do return end"
|
|
||||||
|
|
||||||
setvar j 0
|
|
||||||
whilevarn j 3
|
|
||||||
{
|
|
||||||
addvar j 1
|
|
||||||
userquote 500
|
|
||||||
break // while is inner: "continue"
|
|
||||||
userquote 400
|
|
||||||
} // 3x "===", no quote 400
|
|
||||||
|
|
||||||
setvar j 0
|
|
||||||
whilevarn j 1
|
|
||||||
{
|
|
||||||
switch j
|
|
||||||
case 0
|
|
||||||
addvar j 1
|
|
||||||
// switch is inner: "do return end" (likewise with following
|
|
||||||
// "break" commands):
|
|
||||||
break
|
|
||||||
addvar j 1
|
|
||||||
break
|
|
||||||
case 1
|
|
||||||
nullop
|
|
||||||
break
|
|
||||||
default
|
|
||||||
nullop
|
|
||||||
break
|
|
||||||
endswitch
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: this is syntactically invalid, though may be useful:
|
|
||||||
// case X:
|
|
||||||
// default:
|
|
||||||
// <code...>
|
|
||||||
|
|
||||||
setvar j 0
|
|
||||||
switch j
|
|
||||||
case 0,
|
|
||||||
whilevarn j 1
|
|
||||||
{
|
|
||||||
addvar j 1
|
|
||||||
break // while is inner: "continue"
|
|
||||||
}
|
|
||||||
break
|
|
||||||
endswitch
|
|
||||||
/*
|
|
||||||
// LunaCON only: nested switches.
|
|
||||||
// Untested, but the generated code looks sane.
|
|
||||||
switch j
|
|
||||||
case 0,
|
|
||||||
switch k
|
|
||||||
default
|
|
||||||
userquote 29 // BRIGHTNESS LEVEL: ONE
|
|
||||||
endswitch
|
|
||||||
case 1,
|
|
||||||
userquote 30
|
|
||||||
endswitch
|
|
||||||
*/
|
|
||||||
|
|
||||||
// nested whilevar*n
|
|
||||||
setvar j -1
|
|
||||||
setvar k -1
|
|
||||||
whilevarn j 0
|
|
||||||
{
|
|
||||||
whilevarn k 0
|
|
||||||
{
|
|
||||||
setvar k 0
|
|
||||||
break
|
|
||||||
setvar k 1
|
|
||||||
}
|
|
||||||
|
|
||||||
setvar j 0
|
|
||||||
break
|
|
||||||
setvar j -1
|
|
||||||
}
|
|
||||||
endevent
|
|
|
@ -1,115 +0,0 @@
|
||||||
#!/usr/bin/env luajit
|
|
||||||
|
|
||||||
local ffi = require "ffi"
|
|
||||||
local io = require "io"
|
|
||||||
local os = require "os"
|
|
||||||
local math = require "math"
|
|
||||||
|
|
||||||
local tablesfn = arg[1]
|
|
||||||
local taylor_n = tonumber(arg[2])
|
|
||||||
|
|
||||||
if (tablesfn==nil) then
|
|
||||||
-- taylor_n is a number: use that n for Taylor approximation of atan
|
|
||||||
-- taylor_n==nil: use math.atan
|
|
||||||
print("Usage: ./tables.lua ../path/to/TABLES.DAT [talyor_n]")
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local fd, errmsg = io.open(tablesfn)
|
|
||||||
if (fd==nil) then
|
|
||||||
print("Failed opening "..tablesfn..": "..errmsg)
|
|
||||||
return 1 -- XXX: not working, neither os.exit(1) as of 20120901
|
|
||||||
end
|
|
||||||
|
|
||||||
local sintab = ffi.new("int16_t [?]", 2048)
|
|
||||||
local sinstr = fd:read(2048*2)
|
|
||||||
if (sinstr==nil or #sinstr<2048*2) then
|
|
||||||
print("Failed reading whole sine table")
|
|
||||||
fd:close()
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
|
|
||||||
ffi.copy(sintab, sinstr, 2048*2)
|
|
||||||
sinstr = nil
|
|
||||||
|
|
||||||
--== sintable
|
|
||||||
|
|
||||||
local oursin = ffi.new("int16_t [?]", 2048)
|
|
||||||
local BANG2RAD = math.pi/1024
|
|
||||||
for a=0,2047 do
|
|
||||||
local s = 16384*math.sin(a*BANG2RAD)
|
|
||||||
oursin[a] = s
|
|
||||||
|
|
||||||
-- if (a < 1024) then print(a..": "..s) end
|
|
||||||
end
|
|
||||||
--print("")
|
|
||||||
|
|
||||||
for a=0,2047 do
|
|
||||||
local dif = oursin[a]-sintab[a]
|
|
||||||
assert(dif == 0)
|
|
||||||
--[[
|
|
||||||
if (dif ~= 0) then
|
|
||||||
print(a..": "..dif)
|
|
||||||
end
|
|
||||||
--]]
|
|
||||||
if (a < 512) then
|
|
||||||
assert(sintab[a] == sintab[1024-a])
|
|
||||||
end
|
|
||||||
|
|
||||||
if (a >= 1024) then
|
|
||||||
assert(sintab[a] == -sintab[a-1024])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--== radarang
|
|
||||||
|
|
||||||
local ratab = ffi.new("int16_t [?]", 640)
|
|
||||||
local rastr = fd:read(640*2)
|
|
||||||
fd:close()
|
|
||||||
|
|
||||||
ffi.copy(ratab, rastr, 640*2)
|
|
||||||
rastr = nil
|
|
||||||
|
|
||||||
local function atan_taylor(x, n)
|
|
||||||
local absx = math.abs(x)
|
|
||||||
|
|
||||||
local at = 0
|
|
||||||
for i=0,n do
|
|
||||||
local twoip2 = 2*i+1
|
|
||||||
local xp = x^twoip2
|
|
||||||
local pmone = (-1)^i
|
|
||||||
|
|
||||||
if (absx < 1) then
|
|
||||||
at = at + (pmone * xp)/twoip2
|
|
||||||
else
|
|
||||||
at = at + pmone/(xp * twoip2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (absx > 1) then
|
|
||||||
at = math.pi/2 - at
|
|
||||||
end
|
|
||||||
|
|
||||||
return at
|
|
||||||
end
|
|
||||||
|
|
||||||
local atan
|
|
||||||
if (taylor_n==nil) then
|
|
||||||
atan = math.atan
|
|
||||||
else
|
|
||||||
atan = function (x) return atan_taylor(x, taylor_n) end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
for i=0,639 do
|
|
||||||
local at = -64*atan((640-0.5-i)/160)/BANG2RAD
|
|
||||||
local ra = ratab[i]
|
|
||||||
|
|
||||||
at = math.ceil(at)
|
|
||||||
|
|
||||||
-- print(i ..": ".. ra .." ".. at .." ".. at/ra .." ".. at-ra)
|
|
||||||
end
|
|
||||||
|
|
||||||
--[[
|
|
||||||
local ourra = ffi.new("int16_t [?]", 640)
|
|
||||||
--]]
|
|
|
@ -1,148 +0,0 @@
|
||||||
#!/usr/bin/env luajit
|
|
||||||
|
|
||||||
-- Usage: luajit bittest.lua <number or "x"> [-ffi] [-bchk]
|
|
||||||
|
|
||||||
local require = require
|
|
||||||
local string = require "string"
|
|
||||||
local math = require "math"
|
|
||||||
|
|
||||||
local bitar = require "bitar"
|
|
||||||
|
|
||||||
local print = print
|
|
||||||
local tonumber = tonumber
|
|
||||||
|
|
||||||
local getticks
|
|
||||||
|
|
||||||
if (string.dump) then
|
|
||||||
-- stand-alone
|
|
||||||
local os = require "os"
|
|
||||||
function getticks()
|
|
||||||
return os.clock()*1000
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- embedded
|
|
||||||
getticks = gv.gethiticks
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- based on example from http://bitop.luajit.org/api.html
|
|
||||||
|
|
||||||
local m = string.dump and tonumber(arg[1]) or 1e7
|
|
||||||
local maxidx = math.floor(m/32)
|
|
||||||
|
|
||||||
local ffiar_p, boundchk_p = false, false
|
|
||||||
|
|
||||||
if (string.dump) then
|
|
||||||
if (arg[2]=="-ffi" or arg[3]=="-ffi") then
|
|
||||||
ffiar_p = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if (arg[2]=="-bchk" or arg[3]=="-bchk") then
|
|
||||||
boundchk_p = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function sieve()
|
|
||||||
local count = 0
|
|
||||||
local p = {}
|
|
||||||
|
|
||||||
if (ffiar_p) then
|
|
||||||
-- stand-alone using unchecked int32_t array: on x86_64 approx. 80 ms
|
|
||||||
-- for m = 1e7 (enabling bound checking makes it be around 100 ms)
|
|
||||||
local ffi = require "ffi"
|
|
||||||
local pp = ffi.new("int32_t [?]", maxidx + 1)
|
|
||||||
|
|
||||||
p = pp
|
|
||||||
|
|
||||||
if (boundchk_p) then
|
|
||||||
local mt = {
|
|
||||||
__index = function(tab,idx)
|
|
||||||
if (idx >= 0 and idx <= maxidx) then
|
|
||||||
return pp[idx]
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
__newindex = function(tab,idx,val)
|
|
||||||
if (idx >= 0 and idx <= maxidx) then
|
|
||||||
pp[idx] = val
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
p = setmetatable({}, mt)
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=0,maxidx do p[i] = -1; end
|
|
||||||
else
|
|
||||||
p = bitar.new(m, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local t = getticks()
|
|
||||||
|
|
||||||
if (ffiar_p) then
|
|
||||||
local bit = require "bit"
|
|
||||||
|
|
||||||
for i=2,m do
|
|
||||||
if (bit.band(p[bit.rshift(i, 5)], bit.lshift(1, i)) ~= 0) then
|
|
||||||
count = count + 1
|
|
||||||
for j=i+i,m,i do
|
|
||||||
local jx = bit.rshift(j, 5)
|
|
||||||
p[jx] = bit.band(p[jx], bit.rol(0xfffffffe, j));
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for i=2,m do
|
|
||||||
if (p:isset(i)) then
|
|
||||||
count = count + 1
|
|
||||||
for j=i+i,m,i do p:set0(j); end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- When using bitar module: x86_64: approx. 110 ms
|
|
||||||
print(string.format("[%s] Found %d primes up to %d (%.02f ms)",
|
|
||||||
ffiar_p and "ffi-ar"..(boundchk_p and ", bchk" or "") or "tab-ar",
|
|
||||||
count, m, getticks()-t))
|
|
||||||
|
|
||||||
return p, count
|
|
||||||
end
|
|
||||||
|
|
||||||
if (string.dump) then
|
|
||||||
local function printf(fmt, ...) print(string.format(fmt, ...)) end
|
|
||||||
|
|
||||||
local p, count = sieve()
|
|
||||||
local t = getticks()
|
|
||||||
|
|
||||||
if (ffiar_p) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- test serialization
|
|
||||||
local ser = tostring(p)
|
|
||||||
local maxbidx_str = string.match(ser, '%(([0-9]+),')
|
|
||||||
local p2 = bitar.new(tonumber(maxbidx_str), string.match(ser, "'(.*)'"))
|
|
||||||
printf("serialization + new: %.02f ms", tostring(getticks()-t))
|
|
||||||
|
|
||||||
assert(p==p2)
|
|
||||||
if (m >= 2) then
|
|
||||||
assert(#p == count+2) -- +2 is because 0 and 1 are set even though they're not primes
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not ffiar_p) then
|
|
||||||
math.randomseed(os.time())
|
|
||||||
local maxbidx = math.random(0, 65536)
|
|
||||||
local p3 = bitar.new(maxbidx, 1)
|
|
||||||
assert(#p3 == maxbidx+1) -- bits 0 to maxbidx inclusive are set
|
|
||||||
end
|
|
||||||
--[[
|
|
||||||
print(p)
|
|
||||||
print(p-p) -- test set difference
|
|
||||||
print(-p)
|
|
||||||
--]]
|
|
||||||
|
|
||||||
-- Set difference of self with self is the same as set intersection of self
|
|
||||||
-- with complement of self:
|
|
||||||
assert(p-p == p*(-p))
|
|
||||||
end
|
|
|
@ -1,89 +0,0 @@
|
||||||
#!/usr/bin/env luajit
|
|
||||||
|
|
||||||
local ffi = require "ffi"
|
|
||||||
local math = require "math"
|
|
||||||
local os = require "os"
|
|
||||||
|
|
||||||
local xmath = require "xmath"
|
|
||||||
|
|
||||||
local ldist = xmath.ldist
|
|
||||||
local sqrt = math.sqrt
|
|
||||||
|
|
||||||
local function printf(fmt, ...)
|
|
||||||
print(string.format(fmt, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function edist(p1, p2)
|
|
||||||
return sqrt(p1.x*p1.x + p2.x*p2.x)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- z dummy is so that there's no error with xmath.rotate()
|
|
||||||
local vec2 = ffi.typeof("struct { int32_t x, y, z /* dummy */; }")
|
|
||||||
|
|
||||||
local numpoints = tonumber(arg[1]) or 1e4
|
|
||||||
local Nsq = numpoints*numpoints
|
|
||||||
printf("number of points: %d, testing %d distances", numpoints, Nsq)
|
|
||||||
|
|
||||||
local B = 8192
|
|
||||||
|
|
||||||
local pts = {}
|
|
||||||
for i=1,numpoints do
|
|
||||||
pts[i] = vec2(math.random(-B, B), math.random(B, -B))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- test edist
|
|
||||||
local t = os.clock()
|
|
||||||
|
|
||||||
local sum = 0
|
|
||||||
for i=1,numpoints do
|
|
||||||
for j=1,numpoints do
|
|
||||||
sum = sum+edist(pts[i], pts[j])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
t = os.clock()-t
|
|
||||||
printf("edist: %.03fns per call, mean=%.03f", (1e9*t)/Nsq, sum/Nsq)
|
|
||||||
|
|
||||||
-- test ldist
|
|
||||||
t = os.clock()
|
|
||||||
|
|
||||||
local sum = 0
|
|
||||||
for i=1,numpoints do
|
|
||||||
for j=1,numpoints do
|
|
||||||
sum = sum+ldist(pts[i], pts[j])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
t = os.clock()-t
|
|
||||||
printf("ldist: %.03fns per call, mean=%.03f", (1e9*t)/Nsq, sum/Nsq)
|
|
||||||
|
|
||||||
|
|
||||||
-- test rotation
|
|
||||||
t = os.clock()
|
|
||||||
|
|
||||||
-- from control.lua (the CON version of rotatepoint)
|
|
||||||
local function _rotatepoint(pivotx, pivoty, posx, posy, ang)
|
|
||||||
local pos = xmath.ivec3(posx, posy)
|
|
||||||
local pivot = xmath.ivec3(pivotx, pivoty)
|
|
||||||
pos = xmath.rotate(pos, ang, pivot):toivec3()
|
|
||||||
return pos.x, pos.y
|
|
||||||
end
|
|
||||||
|
|
||||||
sum = 0
|
|
||||||
for i=1,numpoints do
|
|
||||||
for j=1,numpoints do
|
|
||||||
-- local p = xmath.rotate(pts[i], j, pts[j])
|
|
||||||
-- sum = sum+p.x
|
|
||||||
sum = sum + _rotatepoint(pts[j].x, pts[j].y, pts[i].x, pts[i].y, j)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
t = os.clock()-t
|
|
||||||
|
|
||||||
printf("rotate: %.03fns per call", (1e9)/Nsq)
|
|
||||||
|
|
||||||
-- Results (helixhorned x86, x86_64)
|
|
||||||
-- number of points: 10000, testing 100000000 distances
|
|
||||||
-- edist: 6.300ns per call, mean=6286.597
|
|
||||||
-- ldist: 17.600ns per call, mean=8692.612
|
|
||||||
-- rotate: 10.000ns per call [even with _rotatepoint()!]
|
|
|
@ -1,85 +0,0 @@
|
||||||
#!/usr/bin/env luajit
|
|
||||||
|
|
||||||
local os = require("os")
|
|
||||||
|
|
||||||
local xmath = require("xmath")
|
|
||||||
|
|
||||||
|
|
||||||
-- XXX: perf regression? (See below PERF_REGRESSION)
|
|
||||||
-- No, happens only with Clang build. (Why?)
|
|
||||||
local N = os.exit and (arg[1] and tostring(arg[1])) or 1e5 --1e6
|
|
||||||
|
|
||||||
local A,B = {}, {}
|
|
||||||
local V,W = {}, {}
|
|
||||||
|
|
||||||
local randvec
|
|
||||||
|
|
||||||
local args = { ... }
|
|
||||||
local ourname = args[1]
|
|
||||||
|
|
||||||
if (os.exit) then
|
|
||||||
local math = require("math")
|
|
||||||
|
|
||||||
randvec = function()
|
|
||||||
return xmath.vec2(math.random(), math.random())
|
|
||||||
end
|
|
||||||
|
|
||||||
print("Running stand-alone. ourname: "..tostring(ourname))
|
|
||||||
else
|
|
||||||
local randgen = require("randgen")
|
|
||||||
local s = randgen.new(true)
|
|
||||||
|
|
||||||
-- NOTE: factoring out the inner s:getdbl() into a separate function
|
|
||||||
-- reduces performance seriously (about an order of magnitude!)
|
|
||||||
randvec = function()
|
|
||||||
return xmath.vec2(s:getdbl(), s:getdbl())
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Test optional arguments from our_require().
|
|
||||||
printf("Running %s embedded with opt arg %s", ourname, tostring(args[2]))
|
|
||||||
end
|
|
||||||
|
|
||||||
local t1 = os.clock()
|
|
||||||
|
|
||||||
if (os.exit == nil) then
|
|
||||||
local randgen = require("randgen")
|
|
||||||
local r = randgen.new(true)
|
|
||||||
|
|
||||||
for i=1,4*2*N do
|
|
||||||
-- This is to test the performance compared to a direct
|
|
||||||
-- ffiC.rand_jkiss_dbl() call in randgen.lua
|
|
||||||
r:getdbl()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local t2 = os.clock()
|
|
||||||
|
|
||||||
-- init random points and vectors
|
|
||||||
for i=1,N do
|
|
||||||
A[i] = randvec()
|
|
||||||
B[i] = randvec()
|
|
||||||
V[i] = randvec()
|
|
||||||
W[i] = randvec()
|
|
||||||
end
|
|
||||||
|
|
||||||
local t3 = os.clock()
|
|
||||||
|
|
||||||
local v = xmath.vec2(0, 0)
|
|
||||||
for i=1,N do
|
|
||||||
local intersp = xmath.intersect(A[i],V[i], B[i],W[i], true)
|
|
||||||
if (intersp ~= nil) then
|
|
||||||
v = v + intersp
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local t4 = os.clock()
|
|
||||||
|
|
||||||
-- x86_64 (embedded): approx. 200 ms (vs. the 100 ms of direct
|
|
||||||
-- ffiC.rand_jkiss_dbl()):
|
|
||||||
-- x86: 170 ms
|
|
||||||
print("getdbl: ".. 1000*(t2-t1))
|
|
||||||
print("genpoints: ".. 1000*(t3-t2)) -- x86_64: 500 ms, x86: 700 ms
|
|
||||||
print("intersect: ".. 1000*(t4-t3)) -- x86_64, x86: about 35 ms <- thanks to allocation sinking (else, about 500 ms?)
|
|
||||||
print("result: ".. tostring(v))
|
|
||||||
|
|
||||||
-- PERF_REGRESSION: with N==1e6 getdbl, genpoints now about 1000ms from EDuke32!
|
|
|
@ -1,81 +0,0 @@
|
||||||
|
|
||||||
local require = require
|
|
||||||
local con = require("con")
|
|
||||||
local bit = require("bit")
|
|
||||||
local math = require("math")
|
|
||||||
|
|
||||||
local printf = printf
|
|
||||||
local tostring = tostring
|
|
||||||
local rs = con.rotatesprite
|
|
||||||
|
|
||||||
local gameevent = gameevent
|
|
||||||
|
|
||||||
local gv = gv
|
|
||||||
local player = player
|
|
||||||
|
|
||||||
|
|
||||||
module(...) --====================
|
|
||||||
|
|
||||||
test_gamevar = 123
|
|
||||||
local a_local_gamevar = "yes, this one too"
|
|
||||||
|
|
||||||
test_gamevar2 = 'qwe'
|
|
||||||
|
|
||||||
a_table = { ELT1='ELT1!', ELT2=4444, ATAB={ q=333, [4]="!four!", w=444, ["true"]=false } }
|
|
||||||
local ref_to_a_table = a_table
|
|
||||||
ref_to_tabtab = ref_to_a_table.ATAB
|
|
||||||
local l_ref_to_tabtab = ref_to_tabtab
|
|
||||||
|
|
||||||
ref_to_a_table.selfref = a_table
|
|
||||||
a_table[false] = { 1,2,3,con.actorvar(512) }
|
|
||||||
|
|
||||||
require "end_gamevars" --==========
|
|
||||||
|
|
||||||
not_a_gamevar = "no"
|
|
||||||
|
|
||||||
printf("a_table.ATAB[4]=%s", tostring(a_table.ATAB[4]))
|
|
||||||
a_table.ATAB[4] = "!FOUR!"
|
|
||||||
|
|
||||||
|
|
||||||
local DOT1x5 = 3135
|
|
||||||
local BAR1x5 = 3163
|
|
||||||
|
|
||||||
local function draw_hline_dotted(x1, x2, y, pal,stat)
|
|
||||||
for x=x1,x2,2 do
|
|
||||||
local pl = player[0]
|
|
||||||
local tile = (x==x1 or x==x2) and BAR1x5 or DOT1x5
|
|
||||||
|
|
||||||
if (pl.curr_weapon==2) then
|
|
||||||
x = x + 16*math.sin(2*math.pi*gv.totalclock/120)
|
|
||||||
elseif (pl.curr_weapon==1) then
|
|
||||||
x = x + (pl.ang - 1024)/100
|
|
||||||
end
|
|
||||||
|
|
||||||
rs(x,y, 65536, 0, tile, 0,pal,stat,0, 0,0,gv.xdim-1,gv.ydim-1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function draw_two_hlines(y, stat)
|
|
||||||
if (player[0].curr_weapon ~= 3) then
|
|
||||||
stat = bit.bor(stat, 8)
|
|
||||||
end
|
|
||||||
draw_hline_dotted(0,160, y, 0, stat)
|
|
||||||
-- XXX: rotatesprite(*, 320, ...) with stat 1024 and classic draws out of
|
|
||||||
-- bounds (wraps to lower left corner). (Or is it the left one? In any case,
|
|
||||||
-- weird stuff happens.)
|
|
||||||
draw_hline_dotted(162,318, y, 2, stat)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function rotatesprite_test()
|
|
||||||
local stats = { [0]=0, 256, 512, 1024 }
|
|
||||||
for i=0,3 do
|
|
||||||
local stat = stats[i]
|
|
||||||
draw_two_hlines(50+25*i, stats[i])
|
|
||||||
end
|
|
||||||
|
|
||||||
-- NUKEBUTTON
|
|
||||||
rs(30,170, 32768, 2047*((gv.totalclock/240)%1), 142, 0,0,8+1024,0, 0,0,gv.xdim-1,gv.ydim-1)
|
|
||||||
end
|
|
||||||
|
|
||||||
gameevent{gv.EVENT_DISPLAYREST, rotatesprite_test}
|
|
|
@ -1,51 +0,0 @@
|
||||||
// This CON module provides 'state thisactor_getzrange', reimplementing
|
|
||||||
// VM_GetZRange() from the C source. This routine obtains actor[].ceilingz and
|
|
||||||
// actor[].floorz for the current actor (THISACTOR), using getzrange() with the
|
|
||||||
// initial z position displaced by -256.
|
|
||||||
//
|
|
||||||
// Original author: Helixhorned.
|
|
||||||
|
|
||||||
|
|
||||||
// In
|
|
||||||
gamevar tagz_x 0 0
|
|
||||||
gamevar tagz_y 0 0
|
|
||||||
gamevar tagz_z 0 0
|
|
||||||
gamevar tagz_sectnum 0 0
|
|
||||||
|
|
||||||
// Temp
|
|
||||||
gamevar tagz_cstat 0 0
|
|
||||||
|
|
||||||
// Out
|
|
||||||
gamevar tagz_ceilz 0 0
|
|
||||||
gamevar tagz_ceilhit 0 0
|
|
||||||
gamevar tagz_florz 0 0
|
|
||||||
gamevar tagz_florhit 0 0
|
|
||||||
|
|
||||||
// ZOFFSET in the EDuke32 source.
|
|
||||||
define TAGZ_ZOFFSET 256
|
|
||||||
// In the EDuke32 source, the <walldist> passed to VM_GetZRange().
|
|
||||||
define TAGZ_WALLDIST 127
|
|
||||||
|
|
||||||
// Updates the current actor's actor[].floorz and actor[].ceilngz members in
|
|
||||||
// the same way A_GetZRange() does.
|
|
||||||
state thisactor_getzrange
|
|
||||||
setvarvar tagz_x sprite[THISACTOR].x
|
|
||||||
setvarvar tagz_y sprite[THISACTOR].y
|
|
||||||
setvarvar tagz_z sprite[THISACTOR].z, subvar tagz_z TAGZ_ZOFFSET
|
|
||||||
setvarvar tagz_sectnum sprite[THISACTOR].sectnum
|
|
||||||
|
|
||||||
// Back up and clear cstat.
|
|
||||||
setvarvar tagz_cstat sprite[THISACTOR].cstat
|
|
||||||
setactor[THISACTOR].cstat 0
|
|
||||||
|
|
||||||
getzrange tagz_x tagz_y tagz_z tagz_sectnum
|
|
||||||
/*out:*/ tagz_ceilz tagz_ceilhit tagz_florz tagz_florhit
|
|
||||||
/*in:*/ TAGZ_WALLDIST CLIPMASK0
|
|
||||||
|
|
||||||
// Restore cstat.
|
|
||||||
setactor[THISACTOR].cstat tagz_cstat
|
|
||||||
|
|
||||||
// Set actor[] members for the current actor.
|
|
||||||
setactor[THISACTOR].htceilingz tagz_ceilz
|
|
||||||
setactor[THISACTOR].htfloorz tagz_florz
|
|
||||||
ends
|
|
|
@ -1,5 +0,0 @@
|
||||||
|
|
||||||
onevent EVENT_DRAW3DSCREEN
|
|
||||||
rotatesprite 40 80 131072 0 2523 0 0 330 windowx1 windowy1 windowx2 windowy2
|
|
||||||
rotatesprite 280 80 131072 0 2523 0 0 522 windowx1 windowy1 windowx2 windowy2
|
|
||||||
endevent
|
|
Binary file not shown.
|
@ -1,60 +0,0 @@
|
||||||
gamevar snd 351 0 // thunder sound
|
|
||||||
gamevar tmp 0 0
|
|
||||||
gamevar tmp2 0 0
|
|
||||||
gamevar shoots 2605 2 // RPG
|
|
||||||
|
|
||||||
gamevar WEAPON1_SHOOTS 2605 0
|
|
||||||
|
|
||||||
define MORTER2 1653
|
|
||||||
// NOTE: the custom MORTER2 doesn't spawn FRAMEEFFECT like the MORTER, which
|
|
||||||
// has some hard-wired code running.
|
|
||||||
defineprojectile MORTER2 PROJ_WORKSLIKE 6150
|
|
||||||
defineprojectile MORTER2 PROJ_SPAWNS EXPLOSION2
|
|
||||||
defineprojectile MORTER2 PROJ_SOUND RPG_SHOOT
|
|
||||||
defineprojectile MORTER2 PROJ_VEL 600
|
|
||||||
defineprojectile MORTER2 PROJ_EXTRA 165
|
|
||||||
defineprojectile MORTER2 PROJ_EXTRA_RAND 10
|
|
||||||
defineprojectile MORTER2 PROJ_DROP -200 // tested in test.elua
|
|
||||||
defineprojectile MORTER2 PROJ_ISOUND PIPEBOMB_EXPLODE
|
|
||||||
defineprojectile MORTER2 PROJ_HITRADIUS 2800
|
|
||||||
defineprojectile MORTER2 PROJ_BOUNCES 4
|
|
||||||
defineprojectile MORTER2 PROJ_OFFSET 128
|
|
||||||
defineprojectile MORTER2 PROJ_CLIPDIST 24
|
|
||||||
defineprojectile MORTER2 PROJ_TRAIL -1 // overridden in test.elua
|
|
||||||
defineprojectile MORTER2 PROJ_TNUM 6
|
|
||||||
defineprojectile MORTER2 PROJ_USERDATA 32 // test setting initial value
|
|
||||||
|
|
||||||
onevent EVENT_GAME
|
|
||||||
setvarvar WEAPON1_FIRESOUND snd
|
|
||||||
// setvarvar WEAPON1_SHOOTS shoots
|
|
||||||
endevent
|
|
||||||
|
|
||||||
definequote 400 Failed setting projectile userdata
|
|
||||||
|
|
||||||
useractor notenemy 909 // tree trunk
|
|
||||||
ifcount 120
|
|
||||||
{
|
|
||||||
zshoot -4096 BOUNCEMINE
|
|
||||||
|
|
||||||
// userdata: bitwise-OR in 64
|
|
||||||
getprojectile[MORTER2].userdata tmp
|
|
||||||
orvar tmp 64
|
|
||||||
setprojectile[MORTER2].userdata tmp
|
|
||||||
|
|
||||||
ezshoot -4096 MORTER2
|
|
||||||
getthisprojectile[RETURN].vel tmp
|
|
||||||
randvar tmp2 500
|
|
||||||
subvarvar tmp tmp2
|
|
||||||
setthisprojectile[RETURN].vel tmp
|
|
||||||
resetcount
|
|
||||||
|
|
||||||
// userdata: must be 32+64 now
|
|
||||||
getthisprojectile[RETURN].userdata tmp
|
|
||||||
ifvarn tmp 96
|
|
||||||
quote 400
|
|
||||||
}
|
|
||||||
enda
|
|
||||||
|
|
||||||
onevent EVENT_JUMP
|
|
||||||
tossweapon
|
|
||||||
endevent
|
|
|
@ -1,16 +0,0 @@
|
||||||
#!/usr/bin/env luajit
|
|
||||||
|
|
||||||
if (arg[1]==nil) then
|
|
||||||
print("Usage: "..arg[0].." ../path/to/*.ART")
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
|
|
||||||
B = require "build"
|
|
||||||
|
|
||||||
tile = B.loadarts(arg)
|
|
||||||
|
|
||||||
for i=0,B.MAX.TILES-1 do
|
|
||||||
if (tile.sizy[i] > 256) then
|
|
||||||
print(i..": "..tile.sizy[i])
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,636 +0,0 @@
|
||||||
|
|
||||||
-- Loaders for various BUILD structures for LuaJIT
|
|
||||||
|
|
||||||
|
|
||||||
local ffi = require "ffi"
|
|
||||||
local io = require "io"
|
|
||||||
local bit = require "bit"
|
|
||||||
local string = require "string"
|
|
||||||
local table = require "table"
|
|
||||||
|
|
||||||
local error = error
|
|
||||||
local assert = assert
|
|
||||||
local pairs = pairs
|
|
||||||
local print = print
|
|
||||||
local setmetatable = setmetatable
|
|
||||||
local tostring = tostring
|
|
||||||
local tonumber = tonumber
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
local STRUCTDEF = {
|
|
||||||
sector = [[
|
|
||||||
int16_t wallptr, wallnum;
|
|
||||||
int32_t ceilingz, floorz;
|
|
||||||
uint16_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, fogpal;
|
|
||||||
int16_t lotag, hitag, extra;
|
|
||||||
]],
|
|
||||||
|
|
||||||
wall = [[
|
|
||||||
int32_t x, y;
|
|
||||||
int16_t point2, nextwall, nextsector;
|
|
||||||
uint16_t cstat;
|
|
||||||
int16_t picnum, overpicnum;
|
|
||||||
int8_t shade;
|
|
||||||
uint8_t pal, xrepeat, yrepeat, xpanning, ypanning;
|
|
||||||
int16_t lotag, hitag, extra;
|
|
||||||
]],
|
|
||||||
|
|
||||||
sprite = [[
|
|
||||||
int32_t x, y, z;
|
|
||||||
uint16_t cstat;
|
|
||||||
int16_t picnum;
|
|
||||||
int8_t shade;
|
|
||||||
uint8_t pal, clipdist, blend;
|
|
||||||
uint8_t xrepeat, yrepeat;
|
|
||||||
int8_t xoffset, yoffset;
|
|
||||||
int16_t sectnum, statnum;
|
|
||||||
int16_t ang, owner, xvel, yvel, zvel;
|
|
||||||
int16_t lotag, hitag, extra;
|
|
||||||
]],
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi.cdef([[
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
]]..STRUCTDEF.sector..[[
|
|
||||||
} sectortype;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
]]..STRUCTDEF.wall..[[
|
|
||||||
} walltype;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
]]..STRUCTDEF.sprite..[[
|
|
||||||
} spritetype;
|
|
||||||
]])
|
|
||||||
|
|
||||||
ffi.cdef[[
|
|
||||||
size_t fread(void *ptr, size_t size, size_t nmemb, void *stream);
|
|
||||||
]]
|
|
||||||
|
|
||||||
local C = ffi.C
|
|
||||||
|
|
||||||
-- [<sector/wall/sprite>].<membername> = true
|
|
||||||
local is_member_tab = {
|
|
||||||
sector = {},
|
|
||||||
wall = {},
|
|
||||||
sprite = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
for what, sdef in pairs(STRUCTDEF) do
|
|
||||||
for membname in string.gmatch(sdef, "([a-z0-9_]+)[,;]") do
|
|
||||||
is_member_tab[what][membname] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function ismember(what, membname)
|
|
||||||
return (is_member_tab[what][membname] ~= nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
MAX =
|
|
||||||
{
|
|
||||||
SECTORS = { [7]=1024, [8]=4096, [9]=4096 },
|
|
||||||
WALLS = { [7]=8192, [8]=16384, [9]=16384 },
|
|
||||||
SPRITES = { [7]=4096, [8]=16384, [9]=16384 },
|
|
||||||
|
|
||||||
TILES = 30720,
|
|
||||||
}
|
|
||||||
local MAX = MAX
|
|
||||||
|
|
||||||
|
|
||||||
-- <dontclose>: if true, don't close file on error
|
|
||||||
local function doread(fh, basectype, numelts, dontclose)
|
|
||||||
assert(numelts > 0)
|
|
||||||
local typ = ffi.typeof("$ [?]", ffi.typeof(basectype))
|
|
||||||
local cd = ffi.new(typ, numelts)
|
|
||||||
|
|
||||||
if (C.fread(cd, ffi.sizeof(basectype), numelts, fh) ~= numelts) then
|
|
||||||
if (not dontclose) then
|
|
||||||
fh:close()
|
|
||||||
end
|
|
||||||
return nil, "Failed reading"
|
|
||||||
end
|
|
||||||
|
|
||||||
return cd
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Read base palette (i.e. first 768 bytes as R,G,B triplets) from a PALETTE.DAT.
|
|
||||||
-- <noquad>: if true, don't multiply components by 4
|
|
||||||
-- Returns:
|
|
||||||
-- on success: <uint8_t [768]> cdata (palette values scaled by 4)
|
|
||||||
-- on failure: nil, <errmsg>
|
|
||||||
function read_basepal(filename, noquad)
|
|
||||||
local fh, errmsg = io.open(filename, "rb")
|
|
||||||
if (fh == nil) then
|
|
||||||
return nil, errmsg
|
|
||||||
end
|
|
||||||
|
|
||||||
local palette, errmsg = doread(fh, "uint8_t", 768, true)
|
|
||||||
fh:close()
|
|
||||||
|
|
||||||
local f = noquad and 1 or 4
|
|
||||||
|
|
||||||
for i=0,768-1 do
|
|
||||||
palette[i] = f*palette[i]
|
|
||||||
end
|
|
||||||
|
|
||||||
return palette, errmsg
|
|
||||||
end
|
|
||||||
|
|
||||||
local function set_secwalspr_mt(structar, maxidx)
|
|
||||||
local mt = {
|
|
||||||
__index = function(tab, idx)
|
|
||||||
if (not (idx >= 0 and idx < maxidx)) then
|
|
||||||
error("Invalid structure array read access", 2)
|
|
||||||
end
|
|
||||||
return structar[idx]
|
|
||||||
end,
|
|
||||||
|
|
||||||
__newindex = function(tab, idx, newval)
|
|
||||||
error('cannot write directly to structure array', 2)
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
return setmetatable({}, mt)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function get_numyaxbunches(map)
|
|
||||||
if (map.version < 9) then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local numbunches = 0
|
|
||||||
local sectsperbunch = { [0]={}, [1]={} }
|
|
||||||
|
|
||||||
for i=0,map.numsectors-1 do
|
|
||||||
for cf=0,1 do
|
|
||||||
local sec = map.sector[i]
|
|
||||||
local stat = (cf==0) and sec.ceilingstat or sec.floorstat
|
|
||||||
local xpan = (cf==0) and sec.ceilingxpanning or sec.floorxpanning
|
|
||||||
|
|
||||||
if (bit.band(stat, 1024) ~= 0) then
|
|
||||||
if (xpan+1 > numbunches) then
|
|
||||||
numbunches = xpan+1
|
|
||||||
end
|
|
||||||
|
|
||||||
if (sectsperbunch[cf][xpan]==nil) then
|
|
||||||
sectsperbunch[cf][xpan] = 1
|
|
||||||
else
|
|
||||||
sectsperbunch[cf][xpan] = sectsperbunch[cf][xpan]+1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
map.numbunches = numbunches
|
|
||||||
map.sectsperbunch = sectsperbunch
|
|
||||||
end
|
|
||||||
|
|
||||||
--== sprite canonicalizer ==--
|
|
||||||
local function sprite2str(s)
|
|
||||||
local FMT = "%+11d_"
|
|
||||||
-- NOTE: this canonicalization isn't very useful except for debugging
|
|
||||||
-- copy-paste in the editor.
|
|
||||||
-- tostring(s): make sort stable
|
|
||||||
return string.format(FMT:rep(4).."%s", s.x, s.y, s.z, s.ang, tostring(s))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function canonicalize_sprite_order(map)
|
|
||||||
local numsprites = map.numsprites
|
|
||||||
|
|
||||||
map.spriten2o = {} -- mapping of new to old sprite index
|
|
||||||
|
|
||||||
if (numsprites == 0) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local spriteidx = {}
|
|
||||||
|
|
||||||
for i=0,numsprites-1 do -- 0->1 based indexing
|
|
||||||
spriteidx[i+1] = i
|
|
||||||
end
|
|
||||||
|
|
||||||
table.sort(spriteidx,
|
|
||||||
function(i1, i2)
|
|
||||||
return sprite2str(map.sprite[i1]) < sprite2str(map.sprite[i2])
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- deep-copied sprite structs
|
|
||||||
local spritedup = {}
|
|
||||||
|
|
||||||
for i=0,numsprites-1 do
|
|
||||||
-- save sorting permutation (0-based -> 0-based)
|
|
||||||
map.spriten2o[i] = assert(spriteidx[i+1])
|
|
||||||
|
|
||||||
-- back up struct
|
|
||||||
spritedup[i] = ffi.new("spritetype")
|
|
||||||
ffi.copy(spritedup[i], map.sprite[i], ffi.sizeof("spritetype"))
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=0,numsprites-1 do -- do the actual rearrangement
|
|
||||||
map.sprite[i] = spritedup[spriteidx[i+1]]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--== LOADBOARD ==--
|
|
||||||
-- returns:
|
|
||||||
-- on failure, nil, errmsg
|
|
||||||
-- on success, a table
|
|
||||||
-- {
|
|
||||||
-- version = <num>,
|
|
||||||
-- numsectors=<num>, numwalls=<num>, numsprites=<num>,
|
|
||||||
-- sector=<cdata (array of sectortype)>,
|
|
||||||
-- wall=<cdata (array of walltype)>,
|
|
||||||
-- sprite=nil or <cdata> (array of spritetype),
|
|
||||||
-- start =
|
|
||||||
-- { x=<num>, y=<num>, z=<num>, ang=<num>, sectnum=<num> },
|
|
||||||
-- numbunches = <num>,
|
|
||||||
-- sectsperbunch = {
|
|
||||||
-- [0] = { [<bunchnum>]=<number of ceilings> },
|
|
||||||
-- [1] = { [<bunchnum>]=<number of floors> }
|
|
||||||
-- }
|
|
||||||
-- }
|
|
||||||
function loadboard(filename, do_canonicalize_sprite)
|
|
||||||
local fh, errmsg = io.open(filename, "rb")
|
|
||||||
|
|
||||||
if (fh==nil) then
|
|
||||||
return nil, errmsg
|
|
||||||
end
|
|
||||||
|
|
||||||
local cd = doread(fh, "int32_t", 4)
|
|
||||||
if (cd==nil) then
|
|
||||||
return nil, "Couldn't read header"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- The table we'll return on success
|
|
||||||
local map = {
|
|
||||||
version = cd[0],
|
|
||||||
start = { x=cd[1], y=cd[2], z=cd[3] },
|
|
||||||
}
|
|
||||||
|
|
||||||
if (map.version < 7 or map.version > 9) then
|
|
||||||
fh:close()
|
|
||||||
return nil, "Invalid map version"
|
|
||||||
end
|
|
||||||
|
|
||||||
cd = doread(fh, "int16_t", 3)
|
|
||||||
if (cd==nil) then
|
|
||||||
return nil, "Couldn't read header (2)"
|
|
||||||
end
|
|
||||||
|
|
||||||
map.start.ang = cd[0]
|
|
||||||
map.start.sectnum = cd[1]
|
|
||||||
|
|
||||||
-- sectors
|
|
||||||
map.numsectors = cd[2]
|
|
||||||
if (map.numsectors == nil) then
|
|
||||||
return nil, "Couldn't read number of sectors"
|
|
||||||
end
|
|
||||||
if (map.numsectors <= 0 or map.numsectors > MAX.SECTORS[map.version]) then
|
|
||||||
fh:close()
|
|
||||||
return nil, "Invalid number of sectors"
|
|
||||||
end
|
|
||||||
|
|
||||||
map.sector = doread(fh, "sectortype", map.numsectors)
|
|
||||||
if (map.sector == nil) then
|
|
||||||
return nil, "Couldn't read sectors"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- walls
|
|
||||||
cd = doread(fh, "int16_t", 1)
|
|
||||||
if (cd == nil) then
|
|
||||||
return nil, "Couldn't read number of walls"
|
|
||||||
end
|
|
||||||
map.numwalls = cd[0]
|
|
||||||
if (map.numwalls <= 0 or map.numwalls > MAX.WALLS[map.version]) then
|
|
||||||
fh:close()
|
|
||||||
return nil, "Invalid number of walls"
|
|
||||||
end
|
|
||||||
|
|
||||||
map.wall = doread(fh, "walltype", map.numwalls)
|
|
||||||
if (map.wall == nil) then
|
|
||||||
return nil, "Couldn't read walls"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- sprites
|
|
||||||
cd = doread(fh, "int16_t", 1)
|
|
||||||
if (cd == nil) then
|
|
||||||
return nil, "Couldn't read number of sprites"
|
|
||||||
end
|
|
||||||
map.numsprites = cd[0]
|
|
||||||
if (map.numsprites < 0 or map.numsprites > MAX.SPRITES[map.version]) then
|
|
||||||
fh:close()
|
|
||||||
return nil, "Invalid number of sprites"
|
|
||||||
end
|
|
||||||
|
|
||||||
if (map.numsprites ~= 0) then
|
|
||||||
map.sprite = doread(fh, "spritetype", map.numsprites)
|
|
||||||
if (map.sprite == nil) then
|
|
||||||
return nil, "Couldn't read sprites"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
fh:close()
|
|
||||||
|
|
||||||
if (do_canonicalize_sprite) then
|
|
||||||
-- must do this before setting metatable
|
|
||||||
canonicalize_sprite_order(map)
|
|
||||||
end
|
|
||||||
|
|
||||||
map.sector = set_secwalspr_mt(map.sector, map.numsectors)
|
|
||||||
map.wall = set_secwalspr_mt(map.wall, map.numwalls)
|
|
||||||
map.sprite = set_secwalspr_mt(map.sprite, map.numsprites)
|
|
||||||
|
|
||||||
get_numyaxbunches(map)
|
|
||||||
|
|
||||||
-- done
|
|
||||||
return map
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local picanm_t = ffi.typeof[[
|
|
||||||
struct {
|
|
||||||
uint8_t num; // animate number
|
|
||||||
int8_t xofs, yofs;
|
|
||||||
uint8_t sf; // anim. speed and flags
|
|
||||||
}
|
|
||||||
]]
|
|
||||||
|
|
||||||
local artfile_mt = {
|
|
||||||
__index = {
|
|
||||||
-- Global -> local tile index (-1 if gtile is not in this ART file)
|
|
||||||
toltile = function(self, gtile)
|
|
||||||
if (self.tbeg <= gtile and gtile <= self.tend) then
|
|
||||||
return gtile - self.tbeg
|
|
||||||
end
|
|
||||||
return -1
|
|
||||||
end,
|
|
||||||
|
|
||||||
_check_ltile = function(self, ltile)
|
|
||||||
if (not (ltile >= 0 and ltile < self.numtiles)) then
|
|
||||||
error("Invalid local tile number "..ltile, 3)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
_getofs = function(self, ltile)
|
|
||||||
return self.tiledataofs + self.offs[ltile]
|
|
||||||
end,
|
|
||||||
|
|
||||||
dims = function(self, ltile)
|
|
||||||
self:_check_ltile(ltile)
|
|
||||||
return self.sizx[ltile], self.sizy[ltile]
|
|
||||||
end,
|
|
||||||
|
|
||||||
getpic = function(self, ltile)
|
|
||||||
local sx, sy = self:dims(ltile)
|
|
||||||
if (sx == 0 or sy == 0) then
|
|
||||||
-- Tile nonexistent/empty in this ART file
|
|
||||||
return nil, "Empty tile"
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(self.fh:seek("set", self:_getofs(ltile)))
|
|
||||||
return doread(self.fh, "uint8_t", sx*sy, self.grpfh ~= false) -- GRPFH_FALSE
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
|
|
||||||
__metatable = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- af, errmsg = artfile(filename [, grpfh, grpofs])
|
|
||||||
--
|
|
||||||
-- <filename>: File name of the file to get data from, expect if <grpfh>
|
|
||||||
-- passed. Always closed on error. Kept open on success.
|
|
||||||
-- <grpfh>: io.open() file handle to grouping file containing ART
|
|
||||||
-- uncompressed. Never closed, even on error.
|
|
||||||
-- <grpofs>: offset of the ART file in file given by <grpfh>
|
|
||||||
--
|
|
||||||
-- Returns:
|
|
||||||
-- * on error: nil, <errmsg>
|
|
||||||
-- * on success:
|
|
||||||
function artfile(filename, grpfh, grpofs)
|
|
||||||
local ogrpofs
|
|
||||||
local fh
|
|
||||||
|
|
||||||
if (grpfh) then
|
|
||||||
ogrpofs = grpfh:seek()
|
|
||||||
assert(grpfh:seek("set", grpofs))
|
|
||||||
fh = grpfh
|
|
||||||
else
|
|
||||||
local errmsg
|
|
||||||
fh, errmsg = io.open(filename, "rb")
|
|
||||||
if (fh == nil) then
|
|
||||||
return nil, errmsg
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Close file on error?
|
|
||||||
local dontclose = (grpfh ~= nil)
|
|
||||||
|
|
||||||
-- Maybe close file handle and return error message <msg>
|
|
||||||
local function err(msg, ...)
|
|
||||||
if (not dontclose) then
|
|
||||||
fh:close()
|
|
||||||
end
|
|
||||||
return nil, string.format(msg, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
local hdr = doread(fh, "int32_t", 4, dontclose)
|
|
||||||
-- artversion, numtiles, localtilestart, localtileend
|
|
||||||
if (hdr == nil) then
|
|
||||||
return err("Couldn't read header")
|
|
||||||
end
|
|
||||||
|
|
||||||
local af = {
|
|
||||||
filename = filename,
|
|
||||||
fh = fh,
|
|
||||||
grpfh = grpfh or false, -- GRPFH_FALSE
|
|
||||||
|
|
||||||
tbeg = hdr[2],
|
|
||||||
tend = hdr[3],
|
|
||||||
numtiles = hdr[3]-hdr[2]+1,
|
|
||||||
|
|
||||||
-- Members inserted later:
|
|
||||||
-- sizx, sizy: picanm: arrays of length af.numtiles
|
|
||||||
-- tiledataofs: byte offset in `fh' to beginning of tile data
|
|
||||||
-- offs: local byte offsets of each tile, relative of af.tiledataofs
|
|
||||||
--
|
|
||||||
-- Thus,
|
|
||||||
-- af.tiledataofs + af.offs[localtilenum]
|
|
||||||
-- is the byte offset of global tile
|
|
||||||
-- af.tbeg + localtilenum
|
|
||||||
-- in `fh'.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (af.numtiles <= 0) then
|
|
||||||
return err("Invalid tile start/end or empty ART file")
|
|
||||||
end
|
|
||||||
|
|
||||||
local lasttile = af.tbeg + af.numtiles
|
|
||||||
if (lasttile >= MAX.TILES) then
|
|
||||||
return err("Last tile %d beyond MAXTILES-1 (%d)", lasttile, MAX.TILES-1)
|
|
||||||
end
|
|
||||||
|
|
||||||
af.sizx = doread(fh, "int16_t", af.numtiles, dontclose)
|
|
||||||
af.sizy = doread(fh, "int16_t", af.numtiles, dontclose)
|
|
||||||
|
|
||||||
if (af.sizx==nil or af.sizy==nil) then
|
|
||||||
return err("Couldn't read sizx or sizy arrays")
|
|
||||||
end
|
|
||||||
|
|
||||||
af.picanm = doread(fh, picanm_t, af.numtiles, dontclose)
|
|
||||||
|
|
||||||
if (af.picanm == nil) then
|
|
||||||
return err("Couldn't read picanm array")
|
|
||||||
end
|
|
||||||
|
|
||||||
af.tiledataofs = assert(fh:seek())
|
|
||||||
af.offs = ffi.new("uint32_t [?]", af.numtiles)
|
|
||||||
|
|
||||||
local curofs = 0
|
|
||||||
|
|
||||||
for i=0,af.numtiles-1 do
|
|
||||||
local sx, sy = af.sizx[i], af.sizy[i]
|
|
||||||
|
|
||||||
if (sx > 0 and sy > 0) then
|
|
||||||
af.offs[i] = curofs
|
|
||||||
curofs = curofs + sx*sy
|
|
||||||
elseif (sx < 0 or sy < 0) then
|
|
||||||
-- Let's sanity-check stuff a little
|
|
||||||
return err("Local tile %d has negative x or y size", i)
|
|
||||||
else
|
|
||||||
af.offs[i] = -1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return setmetatable(af, artfile_mt)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local function set_sizarray_mt(sizar)
|
|
||||||
local mt = {
|
|
||||||
__index = function(tab, idx)
|
|
||||||
if (not (idx >= 0 and idx < MAX.TILES)) then
|
|
||||||
error("Invalid tile size array read access", 2)
|
|
||||||
end
|
|
||||||
return sizar[idx]
|
|
||||||
end,
|
|
||||||
|
|
||||||
__newindex = function(tab, idx, newval)
|
|
||||||
if (not (idx >= 0 and idx < MAX.TILES)) then
|
|
||||||
error("Invalid tile size array write access", 2)
|
|
||||||
end
|
|
||||||
sizar[idx] = newval
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
return setmetatable({}, mt)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--== LOADARTS (currently tilesizx and tilesizy only) ==--
|
|
||||||
-- filenames: a table with ART file names
|
|
||||||
-- returns:
|
|
||||||
-- on failure, nil, errmsg
|
|
||||||
-- on success, a table
|
|
||||||
-- {
|
|
||||||
-- sizx = <cdata (array of length MAXTILES)>
|
|
||||||
-- sizy = <cdata (array of length MAXTILES)>
|
|
||||||
-- }
|
|
||||||
function loadarts(filenames)
|
|
||||||
local tile = {
|
|
||||||
sizx = ffi.new("int16_t [?]", MAX.TILES),
|
|
||||||
sizy = ffi.new("int16_t [?]", MAX.TILES),
|
|
||||||
}
|
|
||||||
|
|
||||||
for fni=1,#filenames do
|
|
||||||
local fn = filenames[fni]
|
|
||||||
local fh, errmsg = io.open(fn, "rb")
|
|
||||||
|
|
||||||
if (fh==nil) then
|
|
||||||
return nil, errmsg
|
|
||||||
end
|
|
||||||
|
|
||||||
local cd = doread(fh, "int32_t", 4)
|
|
||||||
-- artversion, numtiles, localtilestart, localtileend
|
|
||||||
if (cd==nil) then
|
|
||||||
fh:close()
|
|
||||||
return nil, fn..": Couldn't read header"
|
|
||||||
end
|
|
||||||
|
|
||||||
local localtilestart = cd[2]
|
|
||||||
local numtileshere = cd[3]-localtilestart+1
|
|
||||||
-- print(fn.. ": "..cd[2].. ", "..cd[3])
|
|
||||||
|
|
||||||
if (numtileshere < 0 or localtilestart+numtileshere >= MAX.TILES) then
|
|
||||||
fh:close()
|
|
||||||
return nil, fn..": Invalid tile start/end"
|
|
||||||
end
|
|
||||||
|
|
||||||
if (numtileshere==0) then
|
|
||||||
fh:close()
|
|
||||||
else
|
|
||||||
-- X sizes
|
|
||||||
cd = doread(fh, "int16_t", numtileshere)
|
|
||||||
if (cd == nil) then
|
|
||||||
fh:close()
|
|
||||||
return nil, fn..": Couldn't read tilesizx array"
|
|
||||||
end
|
|
||||||
|
|
||||||
ffi.copy(tile.sizx+localtilestart, cd, numtileshere*2)
|
|
||||||
|
|
||||||
-- Y sizes
|
|
||||||
cd = doread(fh, "int16_t", numtileshere)
|
|
||||||
if (cd == nil) then
|
|
||||||
fh:close()
|
|
||||||
return nil, fn..": Couldn't read tilesizy array"
|
|
||||||
end
|
|
||||||
|
|
||||||
ffi.copy(tile.sizy+localtilestart, cd, numtileshere*2)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
tile.sizx = set_sizarray_mt(tile.sizx)
|
|
||||||
tile.sizy = set_sizarray_mt(tile.sizy)
|
|
||||||
|
|
||||||
return tile
|
|
||||||
end
|
|
||||||
|
|
||||||
-- defs [, rdefs] = readdefs(fn [, alsoreverse])
|
|
||||||
function readdefs(fn, alsoreverse)
|
|
||||||
local fh, errmsg = io.open(fn)
|
|
||||||
|
|
||||||
if (fh==nil) then
|
|
||||||
return nil, errmsg
|
|
||||||
end
|
|
||||||
|
|
||||||
local defs, rdefs = {}, nil
|
|
||||||
|
|
||||||
for line in fh:lines() do
|
|
||||||
local defname, numstr = string.match(line, "#?%s*define%s+([%a_][%w_]+)%s+([0-9]+)")
|
|
||||||
if (defname) then
|
|
||||||
defs[defname] = tonumber(numstr)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (alsoreverse) then
|
|
||||||
rdefs = {}
|
|
||||||
for defname, num in pairs(defs) do
|
|
||||||
rdefs[num] = defname
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
fh:close()
|
|
||||||
return defs, rdefs
|
|
||||||
end
|
|
|
@ -1,100 +0,0 @@
|
||||||
#!/usr/bin/env luajit
|
|
||||||
|
|
||||||
local io = require("io")
|
|
||||||
local os = require("os")
|
|
||||||
local string = require("string")
|
|
||||||
local format = string.format
|
|
||||||
local table = require("table")
|
|
||||||
|
|
||||||
local B = require("build")
|
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local C = ffi.C
|
|
||||||
|
|
||||||
ffi.cdef[[
|
|
||||||
int memcmp(const void *s1, const void *s2, size_t n);
|
|
||||||
]]
|
|
||||||
|
|
||||||
local fn1, fn2 = arg[1], arg[2]
|
|
||||||
local noOnlyLeft = (arg[3]=="-l") -- don't show diff lines where left has tile, right hasn't
|
|
||||||
|
|
||||||
local function err(str)
|
|
||||||
io.stderr:write(str, "\n")
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function printf(fmt, ...)
|
|
||||||
io.stdout:write(string.format(fmt, ...), "\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not fn1 or not fn2) then
|
|
||||||
err("Usage: "..arg[0].." </path/to/tilesXXX.ART> </path/to/tilesYYY.ART> [-l]")
|
|
||||||
end
|
|
||||||
|
|
||||||
local af1, errmsg = B.artfile(fn1)
|
|
||||||
if (errmsg) then
|
|
||||||
err(errmsg)
|
|
||||||
end
|
|
||||||
|
|
||||||
local af2, errmsg = B.artfile(fn2)
|
|
||||||
if (errmsg) then
|
|
||||||
err(errmsg)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Check for same start/end
|
|
||||||
|
|
||||||
if (af1.tbeg ~= af2.tbeg) then
|
|
||||||
err("tile starts differ: "..af1.tbeg.." "..af2.tbeg)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (af1.tend ~= af2.tend) then
|
|
||||||
err("tile ends differ: "..af1.tend.." "..af2.tend)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Compare the two ART files
|
|
||||||
|
|
||||||
local SIZEOF_PICANM = ffi.sizeof(af1.picanm[0])
|
|
||||||
|
|
||||||
for i=0,af1.numtiles-1 do
|
|
||||||
local sx1, sx2 = af1.sizx[i], af2.sizx[i]
|
|
||||||
local sxdif = (sx1 ~= sx2)
|
|
||||||
|
|
||||||
local sy1, sy2 = af1.sizy[i], af2.sizy[i]
|
|
||||||
local sydif = (sy1 ~= sy2)
|
|
||||||
|
|
||||||
local sizedif = (sxdif or sydif)
|
|
||||||
local picanmdif = (C.memcmp(af1.picanm[i], af2.picanm[i], SIZEOF_PICANM) ~= 0)
|
|
||||||
|
|
||||||
local datadif = false
|
|
||||||
-- compare data
|
|
||||||
if (not sizedif) then
|
|
||||||
local pic1 = af1:getpic(i)
|
|
||||||
local pic2 = af2:getpic(i)
|
|
||||||
|
|
||||||
if (pic1 and pic2 and C.memcmp(pic1, pic2, sx1*sy1) ~= 0) then
|
|
||||||
datadif = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (sizedif or picanmdif or datadif) then
|
|
||||||
local strbuf = {}
|
|
||||||
|
|
||||||
local s1str = sx1*sy1==0 and "none" or format("(%d,%d)", sx1, sy1)
|
|
||||||
local s2str = sx2*sy2==0 and "none" or format("(%d,%d)", sx2, sy2)
|
|
||||||
if ((sxdif or sydif) and (not noOnlyLeft or s2str ~= "none")) then
|
|
||||||
strbuf[#strbuf+1] = format("size %s %s", s1str, s2str)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (picanmdif) then
|
|
||||||
strbuf[#strbuf+1] = "picanm"
|
|
||||||
end
|
|
||||||
|
|
||||||
if (not sizedif and datadif) then
|
|
||||||
strbuf[#strbuf+1] = "data"
|
|
||||||
end
|
|
||||||
|
|
||||||
if (#strbuf > 0) then
|
|
||||||
io.stdout:write(i..": "..table.concat(strbuf, ", "), "\n")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,104 +0,0 @@
|
||||||
|
|
||||||
-- Search for enemies with nonzero pal.
|
|
||||||
-- foreachmap module.
|
|
||||||
|
|
||||||
local build = require("build")
|
|
||||||
local D = build.readdefs("../../names.h") or error("Need ../../names.h")
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local print = print
|
|
||||||
local string = require("string")
|
|
||||||
|
|
||||||
local io = require("io")
|
|
||||||
|
|
||||||
local function printf(fmt, ...)
|
|
||||||
print(string.format(fmt, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
local ENEMY_NAME = {
|
|
||||||
--[[
|
|
||||||
[D.BOSS1] = "BOSS1",
|
|
||||||
[D.BOSS1STAYPUT] = "BOSS1STAYPUT",
|
|
||||||
--]]
|
|
||||||
[D.BOSS2] = "BOSS2",
|
|
||||||
[D.BOSS3] = "BOSS3",
|
|
||||||
[D.BOSS4] = "BOSS4",
|
|
||||||
[D.BOSS4STAYPUT] = "BOSS4STAYPUT",
|
|
||||||
[D.COMMANDER] = "COMMANDER",
|
|
||||||
[D.COMMANDERSTAYPUT] = "COMMANDERSTAYPUT",
|
|
||||||
[D.DRONE] = "DRONE",
|
|
||||||
[D.GREENSLIME] = "GREENSLIME",
|
|
||||||
[D.LIZMAN] = "LIZMAN",
|
|
||||||
[D.LIZMANFEEDING] = "LIZMANFEEDING",
|
|
||||||
[D.LIZMANJUMP] = "LIZMANJUMP",
|
|
||||||
[D.LIZMANSPITTING] = "LIZMANSPITTING",
|
|
||||||
[D.LIZMANSTAYPUT] = "LIZMANSTAYPUT",
|
|
||||||
[D.LIZTROOP] = "LIZTROOP",
|
|
||||||
[D.LIZTROOPDUCKING] = "LIZTROOPDUCKING",
|
|
||||||
[D.LIZTROOPJETPACK] = "LIZTROOPJETPACK",
|
|
||||||
[D.LIZTROOPJUSTSIT] = "LIZTROOPJUSTSIT",
|
|
||||||
[D.LIZTROOPONTOILET] = "LIZTROOPONTOILET",
|
|
||||||
[D.LIZTROOPRUNNING] = "LIZTROOPRUNNING",
|
|
||||||
[D.LIZTROOPSHOOT] = "LIZTROOPSHOOT",
|
|
||||||
[D.LIZTROOPSTAYPUT] = "LIZTROOPSTAYPUT",
|
|
||||||
[D.OCTABRAIN] = "OCTABRAIN",
|
|
||||||
[D.OCTABRAINSTAYPUT] = "OCTABRAINSTAYPUT",
|
|
||||||
[D.ORGANTIC] = "ORGANTIC",
|
|
||||||
[D.PIGCOP] = "PIGCOP",
|
|
||||||
[D.PIGCOPDIVE] = "PIGCOPDIVE",
|
|
||||||
[D.PIGCOPSTAYPUT] = "PIGCOPSTAYPUT",
|
|
||||||
[D.RAT] = "RAT",
|
|
||||||
[D.ROTATEGUN] = "ROTATEGUN",
|
|
||||||
[D.SHARK] = "SHARK",
|
|
||||||
}
|
|
||||||
|
|
||||||
local uniq = false
|
|
||||||
|
|
||||||
function init(args)
|
|
||||||
if (#args == 1) then
|
|
||||||
local wr = function(s) io.stderr:write(s) end
|
|
||||||
wr("Usage: "..args[0].." "..args[1].." [-u] *.map ...\n")
|
|
||||||
wr(" -u: print only one pal-x-tilenum combination\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
if (args[2]:sub(1,1)=="-") then
|
|
||||||
assert(args[2]=="-u", "Unknown option "..args[2])
|
|
||||||
uniq = true
|
|
||||||
return 3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function success(map, fn)
|
|
||||||
-- Have one at all?
|
|
||||||
local haveone = false
|
|
||||||
|
|
||||||
-- Have pal-x-tile combination?
|
|
||||||
-- [tile*256 + pal] = true
|
|
||||||
local havept = {}
|
|
||||||
|
|
||||||
for i=0,map.numsprites-1 do
|
|
||||||
local spr = map.sprite[i]
|
|
||||||
local picnum, pal = spr.picnum, spr.pal
|
|
||||||
local name = ENEMY_NAME[picnum]
|
|
||||||
|
|
||||||
if (name and pal~=0) then
|
|
||||||
if (not (name:match("^LIZTROOP") and pal==21)) then -- those are handled by CON
|
|
||||||
if (not uniq or not havept[picnum*256 + pal]) then
|
|
||||||
if (not haveone) then
|
|
||||||
printf("%s:", fn)
|
|
||||||
haveone = true
|
|
||||||
end
|
|
||||||
printf("%5d %3d %s", i, pal, name)
|
|
||||||
havept[picnum*256 + pal] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if (haveone) then
|
|
||||||
print("")
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,34 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
ok=yes
|
|
||||||
if [ -z "$1" ]; then
|
|
||||||
ok=
|
|
||||||
fi
|
|
||||||
if [ -z "$2" ]; then
|
|
||||||
ok=
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$ok" ]; then
|
|
||||||
echo "Usage: $0 <dir> <some_foreachmap_module.lua | code for foreachmap.lua -e>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
LOPT=-L
|
|
||||||
idx=$(expr match `uname -s` '[mM][iI][nN][gG][wW]')
|
|
||||||
if [ "$idx" != 0 ]; then
|
|
||||||
LOPT=
|
|
||||||
fi
|
|
||||||
|
|
||||||
FN="$1"
|
|
||||||
ARG="$2"
|
|
||||||
|
|
||||||
idx=$(expr match "$ARG" '.*lua$')
|
|
||||||
if [ "$idx" == 0 ]; then
|
|
||||||
ARG="-e$ARG"
|
|
||||||
find $LOPT "$FN" -iname '*.map' -print0 | xargs -0 ./foreachmap.lua "$ARG"
|
|
||||||
else
|
|
||||||
shift
|
|
||||||
# So that you can e.g. do
|
|
||||||
# ./findmaps.sh ~/.eduke32 ./colenemy.lua -u
|
|
||||||
find $LOPT "$FN" -iname '*.map' -print0 | xargs -0 ./foreachmap.lua "$@"
|
|
||||||
fi
|
|
|
@ -1,177 +0,0 @@
|
||||||
#!/usr/bin/env luajit
|
|
||||||
|
|
||||||
-- Generic map iterator.
|
|
||||||
|
|
||||||
-- The first cmdline arg is a name of a lua file (may be sans .lua) which must
|
|
||||||
-- be a module and is `require'd into this script, e.g. "stats" or "stats.lua".
|
|
||||||
-- First, a key named .init is looked up in the loaded module and if it exists,
|
|
||||||
-- it is run like .init(arg) (thus allowing it to parse the command-line
|
|
||||||
-- arguments and then potentially remove the ones it used).
|
|
||||||
-- If .init returns non-nil, this script aborts.
|
|
||||||
-- Otherwise, for each 2nd and following argument, if map loading succeeds,
|
|
||||||
-- .success(map, filename) is run, otherwise
|
|
||||||
-- .failure(filename, errmsg) is run if that key exists, or a standard error
|
|
||||||
-- message is printed to stderr.
|
|
||||||
-- Finally, if there is a .finish field in the module, it is run with no args.
|
|
||||||
|
|
||||||
-- forxcode example: print sprite numbers with lotag < -1 (lotag is signed for us),
|
|
||||||
-- and for each matching sprite also print its lotag and picnum:
|
|
||||||
-- $ ./findmaps.sh ~/.eduke32/ "sprite: .lotag < -1 :: io.write(', '.. .lotag .. ' ' .. .picnum)"
|
|
||||||
|
|
||||||
-- The local 'd' provides defs loaded from ../../names.h, example:
|
|
||||||
-- $ ./findmaps.sh ~/.eduke32/ "sprite: .picnum>=d.CRACK1 and .picnum<=d.CRACK4"
|
|
||||||
-- (Now: no space between "d." and "CRACK" is necessary ".xxx" is translated to
|
|
||||||
-- "sprite[<current>].xxx" only if it's the name of a sprite member.)
|
|
||||||
-- The local 'rdefs' provides the reverse mapping: from picnums to tile names
|
|
||||||
-- (or nil).
|
|
||||||
|
|
||||||
-- Print all V9 maps along with their number of bunches and max(ceilings of a bunch)
|
|
||||||
-- $ prog='if (map.version==9) then print(map.numbunches.." ".. math.max(unpack(map.sectsperbunch[0],0)) .." "..fn) end'
|
|
||||||
-- $ ./findmaps.sh ~/.eduke32 "$prog" |sort -n -k 2
|
|
||||||
|
|
||||||
-- Print all MUSICANDSFX sprites that play sounds with bit 1 set.
|
|
||||||
-- ./findmaps.sh /g/Games/Eduke32c/grp 'sprite: .picnum==5 and eq(.lotag, {170, 186, 187, 279, 382, 347}) :: io.write(" ".. tostring(.lotag))'
|
|
||||||
|
|
||||||
-- Print all maps that have floor-aligned blockable sprites in underwater sectors.
|
|
||||||
-- ./findmaps.sh ~/.eduke32 'sprite:: bit.band(.cstat,49)==33 and .sectnum>=0 and .sectnum < map.numsectors and sector[.sectnum].lotag==2
|
|
||||||
|
|
||||||
local B = require "build"
|
|
||||||
local string = require "string"
|
|
||||||
local io = require "io"
|
|
||||||
local os = require "os"
|
|
||||||
|
|
||||||
if (#arg < 1) then
|
|
||||||
local wr = function(s) io.stdout:write(s) end
|
|
||||||
wr("Usage: ./foreachmap.lua <module[.lua]> [init args...] <filename.map> ...\n")
|
|
||||||
wr(" ./foreachmap.lua -e\"some_lua_code ...\" <filename.map> ...\n")
|
|
||||||
wr(" ./foreachmap.lua -e\"[sector|wall|sprite]: <condition on .<field>>\" <fn.map> ...\n\n")
|
|
||||||
wr("In the second form, the code is run as body of a function(map, fn)\n")
|
|
||||||
wr("and num{sectors,walls,sprites} and {sector,wall,sprite} do not\n")
|
|
||||||
wr("need to be qualified with the \"map.\" prefix.\n")
|
|
||||||
wr("The third form is a shortcut for quickly finding sectors/walls/sprites\n")
|
|
||||||
wr("satisfying a certain condition (see example below)\n\n")
|
|
||||||
wr("Examples: ./foreachmap.lua -e\"if map.numbunches==1 then print(fn) end\" ~/.eduke32/*.map\n")
|
|
||||||
wr(" ./foreachmap.lua -e\"sprite: .picnum==10 and .lotag==2563\" *.map\n")
|
|
||||||
wr(" ./foreachmap.lua -e\"sprite:: ... -- (only prints the file names)\n")
|
|
||||||
wr("(See foreachmap.lua for an example of the \"forxcode\" feature.)\n\n")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local modname = string.gsub(arg[1], "%.lua$", "")
|
|
||||||
|
|
||||||
function sum(tab, initidx)
|
|
||||||
local s = 0
|
|
||||||
for i=(initidx or 1),#tab do
|
|
||||||
s = s + tab[i]
|
|
||||||
end
|
|
||||||
return s
|
|
||||||
end
|
|
||||||
|
|
||||||
local g_what
|
|
||||||
-- Maybe replace e.g. .nextwall --> wall[i].nextwall.
|
|
||||||
-- <what>: one of "sector", "wall" or "sprite"
|
|
||||||
-- <maybechar>: the char before <maybememb>, or ""
|
|
||||||
-- <maybememb>: a potential member name prefixed by "."
|
|
||||||
local function maybe_complete_member(maybechar, maybememb)
|
|
||||||
if (maybechar~="]" and B.ismember(g_what, maybememb:sub(2))) then
|
|
||||||
return maybechar..g_what.."[i]"..maybememb
|
|
||||||
else
|
|
||||||
return maybechar..maybememb
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local mod
|
|
||||||
if (modname:sub(1,2) == "-e") then
|
|
||||||
local body = modname:sub(3)
|
|
||||||
|
|
||||||
-- sector/wall/sprite finder shortcut
|
|
||||||
local b, e, what = body:find("^([a-z]+)::?")
|
|
||||||
if (what) then
|
|
||||||
g_what = what
|
|
||||||
local onlyfiles = (body:sub(e-1,e)=="::") -- "::" means "only list files" (like grep -l)
|
|
||||||
body = body:sub(e+1) -- clip off "bla::"
|
|
||||||
body = body:gsub("(.?)(%.[a-z][a-z0-9]*)", maybe_complete_member) -- e.g. .lotag --> sprite[i].lotag
|
|
||||||
|
|
||||||
local perxcode
|
|
||||||
-- look for additional "print" code to be executed for each match
|
|
||||||
b, e, perxcode = body:find("::(.*)")
|
|
||||||
if (perxcode) then
|
|
||||||
body = body:sub(1,b-1)
|
|
||||||
if (onlyfiles) then
|
|
||||||
error("Per-x code can only be used with the ':' syntax (list each match)")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
perxcode = ""
|
|
||||||
end
|
|
||||||
|
|
||||||
assert(what=="sector" or what=="wall" or what=="sprite")
|
|
||||||
|
|
||||||
body =
|
|
||||||
"for i=0,num"..what.."s-1 do\n"..
|
|
||||||
" if ("..body..") then\n"..
|
|
||||||
(onlyfiles and "io.write(fn); return\n" or "io.write(fn..': '..i)\n") ..
|
|
||||||
perxcode .. "io.write('\\n')\n"..
|
|
||||||
" end\n"..
|
|
||||||
"end\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
local successfunc, errmsg = loadstring([[
|
|
||||||
local function eq(x, tab)
|
|
||||||
for i=1,#tab do
|
|
||||||
if (x==tab[i]) then return true end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local d,rdefs = require('build').readdefs('../../names.h',true)
|
|
||||||
if (not d) then error('Need ../../names.h') end -- XXX
|
|
||||||
local numsectors, numwalls, numsprites, sector, wall, sprite
|
|
||||||
return function (map, fn)
|
|
||||||
numsectors, numwalls, numsprites = map.numsectors, map.numwalls, map.numsprites
|
|
||||||
sector, wall, sprite = map.sector, map.wall, map.sprite
|
|
||||||
]]..body
|
|
||||||
.." end")
|
|
||||||
|
|
||||||
if (successfunc==nil) then
|
|
||||||
io.stderr:write("Error loading string: "..errmsg.."\n")
|
|
||||||
io.stderr:write("Function body:\n", body)
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
mod = { success=successfunc() }
|
|
||||||
else
|
|
||||||
mod = require(modname)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- The return value from the module's .init().
|
|
||||||
local initret
|
|
||||||
|
|
||||||
if (mod.init) then
|
|
||||||
initret = mod.init(arg)
|
|
||||||
if (type(initret)=="number" and initret < 0) then
|
|
||||||
-- A negative return value from .init() is taken as a request to exit,
|
|
||||||
-- e.g. because an error occurred.
|
|
||||||
os.exit(1)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- A positive return value from .init() is taken to start counting map names
|
|
||||||
-- from that 'arg' index.
|
|
||||||
local startargi = (type(initret)=="number" and initret > 0 and initret) or 2
|
|
||||||
|
|
||||||
for i=startargi,#arg do
|
|
||||||
local fn = arg[i]
|
|
||||||
local map, errmsg = B.loadboard(fn)
|
|
||||||
|
|
||||||
if (map ~= nil) then
|
|
||||||
mod.success(map, fn)
|
|
||||||
else
|
|
||||||
if (mod.failure) then
|
|
||||||
mod.failure(fn, errmsg)
|
|
||||||
else
|
|
||||||
io.stderr:write(string.format("--- %s: %s\n", fn, errmsg))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (mod.finish) then
|
|
||||||
mod.finish()
|
|
||||||
end
|
|
|
@ -1,110 +0,0 @@
|
||||||
#!/usr/bin/env luajit
|
|
||||||
|
|
||||||
local B = require("build")
|
|
||||||
local string = require("string")
|
|
||||||
|
|
||||||
local opt, fn
|
|
||||||
local do_canonicalize_sprite
|
|
||||||
|
|
||||||
if (arg[1] and arg[1]:sub(1,1)=="-") then
|
|
||||||
opt = arg[1]
|
|
||||||
fn = arg[2]
|
|
||||||
else
|
|
||||||
fn = arg[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
if (opt) then
|
|
||||||
if (opt=="-c" or opt=="-C") then
|
|
||||||
-- -C means to canonicalize but without adding the new->old mapping
|
|
||||||
-- comment
|
|
||||||
do_canonicalize_sprite = opt
|
|
||||||
else
|
|
||||||
print("Error: unrecognized option "..opt)
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (fn==nil) then
|
|
||||||
print("Usage: map2text [-c/-C] <somefile.map>")
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local function printf(fmt, ...)
|
|
||||||
print(string.format(fmt, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
local map, errmsg = B.loadboard(fn, do_canonicalize_sprite)
|
|
||||||
if (map == nil) then
|
|
||||||
printf("Couldn't load \"%s\": %s", fn, errmsg)
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
printf("numsectors = %d", map.numsectors)
|
|
||||||
printf("numwalls = %d", map.numwalls)
|
|
||||||
printf("numsprites = %d", map.numsprites)
|
|
||||||
|
|
||||||
printf("startpos = { %d, %d, %d }", map.start.x, map.start.y, map.start.z)
|
|
||||||
printf("startang = %d", map.start.ang)
|
|
||||||
printf("startsectnum = %d", map.start.sectnum)
|
|
||||||
|
|
||||||
local sector_members = {
|
|
||||||
"wallptr", "wallnum",
|
|
||||||
"ceilingz", "floorz",
|
|
||||||
"ceilingstat", "floorstat",
|
|
||||||
"ceilingpicnum", "ceilingheinum",
|
|
||||||
"ceilingshade",
|
|
||||||
"ceilingpal", "ceilingxpanning", "ceilingypanning",
|
|
||||||
"floorpicnum", "floorheinum",
|
|
||||||
"floorshade",
|
|
||||||
"floorpal", "floorxpanning", "floorypanning",
|
|
||||||
"visibility", "fogpal",
|
|
||||||
"lotag", "hitag", "extra",
|
|
||||||
}
|
|
||||||
|
|
||||||
local wall_members = {
|
|
||||||
"x", "y",
|
|
||||||
"point2", "nextwall", "nextsector",
|
|
||||||
"cstat",
|
|
||||||
"picnum", "overpicnum",
|
|
||||||
"shade",
|
|
||||||
"pal", "xrepeat", "yrepeat", "xpanning", "ypanning",
|
|
||||||
"lotag", "hitag", "extra",
|
|
||||||
}
|
|
||||||
|
|
||||||
local sprite_members = {
|
|
||||||
"x", "y", "z",
|
|
||||||
"cstat", "picnum",
|
|
||||||
"shade",
|
|
||||||
"pal", "clipdist", "blend",
|
|
||||||
"xrepeat", "yrepeat",
|
|
||||||
"xoffset", "yoffset",
|
|
||||||
"sectnum", "statnum",
|
|
||||||
"ang", "owner", "xvel", "yvel", "zvel",
|
|
||||||
"lotag", "hitag", "extra",
|
|
||||||
}
|
|
||||||
|
|
||||||
local function print_members(map, struct, members)
|
|
||||||
printf("%s = {", struct)
|
|
||||||
for i=0,map["num"..struct.."s"]-1 do
|
|
||||||
local comment = ""
|
|
||||||
if (struct=="sprite" and do_canonicalize_sprite=="-c") then
|
|
||||||
comment = " --"..tostring(map.spriten2o[i])
|
|
||||||
end
|
|
||||||
|
|
||||||
printf("[%d]={%s", i, comment)
|
|
||||||
|
|
||||||
for j=1,#members do
|
|
||||||
local member = members[j]
|
|
||||||
printf("%s = %d", member, map[struct][i][member])
|
|
||||||
end
|
|
||||||
|
|
||||||
print("}")
|
|
||||||
end
|
|
||||||
|
|
||||||
print("}")
|
|
||||||
end
|
|
||||||
|
|
||||||
print_members(map, "sector", sector_members)
|
|
||||||
print_members(map, "wall", wall_members)
|
|
||||||
print_members(map, "sprite", sprite_members)
|
|
|
@ -1,46 +0,0 @@
|
||||||
|
|
||||||
-- Print out some aggregate statistics for passed BUILD maps,
|
|
||||||
-- foreachmap module.
|
|
||||||
|
|
||||||
local string = require "string"
|
|
||||||
local math = require "math"
|
|
||||||
|
|
||||||
local print = print
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
local stat = require "stat"
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
local function printf(fmt, ...)
|
|
||||||
print(string.format(fmt, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
local sumnumsectors = 0
|
|
||||||
local sumnumwalls = 0
|
|
||||||
|
|
||||||
local s = stat.new()
|
|
||||||
|
|
||||||
function success(map, fn)
|
|
||||||
local ns = map.numsectors
|
|
||||||
local nw = map.numwalls
|
|
||||||
|
|
||||||
s:add(nw/ns)
|
|
||||||
|
|
||||||
sumnumsectors = sumnumsectors+ns
|
|
||||||
sumnumwalls = sumnumwalls+nw
|
|
||||||
end
|
|
||||||
|
|
||||||
function finish()
|
|
||||||
res = s:getstats()
|
|
||||||
|
|
||||||
printf("%d maps\n", res.n)
|
|
||||||
printf("total sectors: %d", sumnumsectors)
|
|
||||||
printf("total walls: %d", sumnumwalls)
|
|
||||||
printf("total walls / total sectors: %.02f", sumnumwalls/sumnumsectors)
|
|
||||||
printf("")
|
|
||||||
printf("Walls/sector")
|
|
||||||
print(res)
|
|
||||||
end
|
|
|
@ -1,46 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
DIFF="git diff -U2 --no-index --color-words"
|
|
||||||
CMD="/usr/bin/env luajit ./map2text.lua"
|
|
||||||
|
|
||||||
opt=""
|
|
||||||
|
|
||||||
# Name of the 'tempfile' or 'mktemp' command (or full path).
|
|
||||||
tempfile_cmd=tempfile
|
|
||||||
|
|
||||||
tempfile_path=`which "$tempfile_cmd"`
|
|
||||||
if [ -z "$tempfile_path" ]; then
|
|
||||||
echo "Error: tempfile_cmd ($tempfile_cmd) must be the name of existing 'tempfile' or 'mktemp' executable."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$1" = "-c" -o "$1" = "-C" ]; then
|
|
||||||
opt="$1"
|
|
||||||
shift
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$1" -o -z "$2" ]; then
|
|
||||||
echo "Usage: ./mapdiff.sh [-c] <file.map> <file2.map>"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
tf1=`"$tempfile_cmd"`
|
|
||||||
if [ -z "$tf1" ]; then
|
|
||||||
echo Failed creating temp file
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
tf2=`"$tempfile_cmd"`
|
|
||||||
if [ -z "$tf2" ]; then
|
|
||||||
rm "$tf1"
|
|
||||||
echo Failed creating temp file
|
|
||||||
exit 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
$CMD $opt "$1" > "$tf1"
|
|
||||||
$CMD $opt "$2" > "$tf2"
|
|
||||||
|
|
||||||
$DIFF "$tf1" "$tf2"
|
|
||||||
|
|
||||||
rm "$tf1"
|
|
||||||
rm "$tf2"
|
|
|
@ -1,25 +0,0 @@
|
||||||
|
|
||||||
-- Print out some statistics for a BUILD map,
|
|
||||||
-- foreachmap module.
|
|
||||||
|
|
||||||
local string = require "string"
|
|
||||||
local print = print
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
local function printf(fmt, ...)
|
|
||||||
print(string.format(fmt, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function success(map, fn)
|
|
||||||
printf("--- %s:", fn)
|
|
||||||
|
|
||||||
printf(" version: %d", map.version)
|
|
||||||
printf(" numsectors: %d\n numwalls: %d\n numsprites: %d",
|
|
||||||
map.numsectors, map.numwalls, map.numsprites)
|
|
||||||
printf(" walls/sector: %.02f\n sprites/sector: %.02f",
|
|
||||||
map.numwalls/map.numsectors, map.numsprites/map.numsectors)
|
|
||||||
printf("")
|
|
||||||
end
|
|
|
@ -1,172 +0,0 @@
|
||||||
|
|
||||||
-- Display information about problematic wall ypannings,
|
|
||||||
-- foreachmap module.
|
|
||||||
|
|
||||||
local string = require "string"
|
|
||||||
local table = require "table"
|
|
||||||
local bit = require "bit"
|
|
||||||
|
|
||||||
local print = print
|
|
||||||
local pairs = pairs
|
|
||||||
local rawget = rawget
|
|
||||||
local setmetatable = setmetatable
|
|
||||||
|
|
||||||
local B = require "build"
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
local function printf(fmt, ...)
|
|
||||||
print(string.format(fmt, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local tile
|
|
||||||
|
|
||||||
function init(arg)
|
|
||||||
local artargend = nil
|
|
||||||
for i=2,#arg do
|
|
||||||
if (arg[i]=="--") then
|
|
||||||
artargend = i
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (artargend==nil or artargend==0) then
|
|
||||||
printf("Usage: luajit ./foreachmap.lua %s <tilesXXX.ART> [, ...] -- <filename1.map> ...\n", arg[1])
|
|
||||||
return -1
|
|
||||||
end
|
|
||||||
|
|
||||||
local artfns = {}
|
|
||||||
for i=2,artargend-1 do
|
|
||||||
artfns[#artfns+1] = arg[i]
|
|
||||||
end
|
|
||||||
|
|
||||||
local i = 2
|
|
||||||
local j = artargend+1
|
|
||||||
|
|
||||||
while (arg[j]) do
|
|
||||||
arg[i] = arg[j]
|
|
||||||
arg[j] = nil
|
|
||||||
|
|
||||||
i = i+1
|
|
||||||
j = j+1
|
|
||||||
end
|
|
||||||
|
|
||||||
local tile_, errmsg = B.loadarts(artfns)
|
|
||||||
if (tile_ == nil) then
|
|
||||||
printf("%s", errmsg)
|
|
||||||
return -2
|
|
||||||
end
|
|
||||||
|
|
||||||
tile = tile_ -- set 'tile' file-scope var
|
|
||||||
end
|
|
||||||
|
|
||||||
local table_default0_mt = {
|
|
||||||
__index = function(tab, idx)
|
|
||||||
if (rawget(tab, idx)==nil) then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
return rawget(tab, idx)
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
local sector, wall, sprite
|
|
||||||
|
|
||||||
function success(map, fn)
|
|
||||||
-- set file-scope vars for convenience
|
|
||||||
sector = map.sector
|
|
||||||
wall = map.wall
|
|
||||||
sprite = map.sprite
|
|
||||||
|
|
||||||
-- counts of non-pow2 tiles, per tilenum
|
|
||||||
local np2tile = setmetatable({}, table_default0_mt)
|
|
||||||
-- walls/overwall indices with non-pow2 tiles, [walidx]=true
|
|
||||||
local np2wall = {}
|
|
||||||
-- [i]=wallidx
|
|
||||||
local np2walls = {}
|
|
||||||
|
|
||||||
local badoverpicnum = false
|
|
||||||
|
|
||||||
for i=0,map.numsectors-1 do
|
|
||||||
local startwall = sector[i].wallptr
|
|
||||||
local endwall = startwall+sector[i].wallnum-1
|
|
||||||
|
|
||||||
for w=startwall,endwall do
|
|
||||||
for n=1,2 do
|
|
||||||
local pic, ysiz
|
|
||||||
|
|
||||||
if (wall[w].nextwall < 0) then
|
|
||||||
-- We don't care for white walls
|
|
||||||
elseif (n==1) then
|
|
||||||
pic = wall[w].picnum
|
|
||||||
ysiz = tile.sizy[pic]
|
|
||||||
else
|
|
||||||
pic = wall[w].overpicnum
|
|
||||||
if (pic < 0 or pic > 30720) then -- MAXTILES
|
|
||||||
badoverpicnum = true
|
|
||||||
else
|
|
||||||
if (bit.band(wall[w].cstat, 16+32)==0) then
|
|
||||||
-- we don't care about non-masked/1-way walls
|
|
||||||
ysiz = nil
|
|
||||||
else
|
|
||||||
ysiz = tile.sizy[pic]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (pic == 560) then -- Don't care for MIRROR
|
|
||||||
ysiz = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if (ysiz~=nil and ysiz > 0 and bit.band(ysiz, bit.bnot(ysiz-1))~=ysiz) then
|
|
||||||
-- non-pow2 ysize
|
|
||||||
|
|
||||||
np2tile[pic] = np2tile[pic]+1
|
|
||||||
|
|
||||||
if (not np2wall[w]) then
|
|
||||||
np2wall[w] = true
|
|
||||||
np2walls[#np2walls+1] = w
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- report our findings
|
|
||||||
|
|
||||||
if (#np2walls == 0) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- sort in wall index order
|
|
||||||
table.sort(np2walls)
|
|
||||||
|
|
||||||
printf("--- %s:", fn)
|
|
||||||
|
|
||||||
--[[
|
|
||||||
printf(" Walls:")
|
|
||||||
for i=1,#np2walls do
|
|
||||||
printf(" %d", np2walls[i])
|
|
||||||
end
|
|
||||||
printf("")
|
|
||||||
--]]
|
|
||||||
printf(" %d red walls with non-pow2 ysize tiles", #np2walls)
|
|
||||||
if (badoverpicnum) then
|
|
||||||
printf(" (some red walls have out-of-bounds overpicnums)")
|
|
||||||
end
|
|
||||||
|
|
||||||
local np2tiles = {}
|
|
||||||
for tilenum,_ in pairs(np2tile) do
|
|
||||||
np2tiles[#np2tiles+1] = tilenum
|
|
||||||
end
|
|
||||||
table.sort(np2tiles)
|
|
||||||
|
|
||||||
printf(" Tiles:")
|
|
||||||
for i=1,#np2tiles do
|
|
||||||
printf(" %d", np2tiles[i])
|
|
||||||
end
|
|
||||||
|
|
||||||
printf("")
|
|
||||||
end
|
|
|
@ -1,146 +0,0 @@
|
||||||
|
|
||||||
-- Search for maps that have parallaxed ceilings with different tile numbers.
|
|
||||||
-- foreachmap module.
|
|
||||||
|
|
||||||
local bit = require "bit"
|
|
||||||
local string = require "string"
|
|
||||||
local table = require "table"
|
|
||||||
local io = require "io"
|
|
||||||
|
|
||||||
local print = print
|
|
||||||
local tostring = tostring
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
local function printf(fmt, ...)
|
|
||||||
print(string.format(fmt, ...))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
local MULTI_PSKY_TILES = { 80, 84, 89 }
|
|
||||||
|
|
||||||
local IS_MULTI_PSKY_TILE = {
|
|
||||||
[80] = true, -- MOONSKY1
|
|
||||||
[84] = true, -- BIGORBIT1
|
|
||||||
[89] = true, -- LA
|
|
||||||
}
|
|
||||||
|
|
||||||
local tilecnt
|
|
||||||
|
|
||||||
-- foreachmap initialization
|
|
||||||
local opts = { p=false, s=false } -- pretty? simple?
|
|
||||||
|
|
||||||
function init(args)
|
|
||||||
if (#args == 1) then
|
|
||||||
local wr = function(s) io.stderr:write(s) end
|
|
||||||
wr("Usage: "..args[0].." "..args[1].." [-s] [-p] *.map ...\n")
|
|
||||||
wr(" -s: short format\n")
|
|
||||||
wr(" -p: pretty-print using ANSI escapes\n\n")
|
|
||||||
wr("The long format is as follows:\n")
|
|
||||||
wr(" <mapname.map>: <num-distinct-psky-tiles> (<of-those-multi-pskies>): <item1> <item2> ...\n")
|
|
||||||
wr("Each <item> reads\n")
|
|
||||||
wr(" #<tilenum>:<num-occurrences>(s<sectnum>)\n")
|
|
||||||
wr("where <sectnum> is the index of an arbitrary sector containing such a psky ceiling.\n")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=2,#args do
|
|
||||||
local arg = args[i]
|
|
||||||
|
|
||||||
if (arg:sub(1,1)=="-") then
|
|
||||||
local letter = arg:sub(2,2)
|
|
||||||
|
|
||||||
if (#arg==2 and type(opts[letter])=="boolean") then
|
|
||||||
opts[letter] = true
|
|
||||||
else
|
|
||||||
io.stderr:write("Unrecognized option "..arg.."\n")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Sorting function for tiles, true if <tile1> "less-than" <tile2>.
|
|
||||||
-- "less-than" here means appearing earlier in the resulting table.
|
|
||||||
local function sortskytiles(tile1, tile2)
|
|
||||||
local ismulti1 = IS_MULTI_PSKY_TILE[tile1]
|
|
||||||
local ismulti2 = IS_MULTI_PSKY_TILE[tile2]
|
|
||||||
|
|
||||||
-- First, check if <tile1> is a multi-psky tile and <tile2> not.
|
|
||||||
if (ismulti1 and not ismulti2) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Next, check if <tile1> appears more often in the map.
|
|
||||||
if (tilecnt[tile1] > tilecnt[tile2]) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Now, (tilecnt[tile1] >= tilecnt[tile2]) and (not ismulti1 or ismulti2).
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function bold(s)
|
|
||||||
return opts.p and ("\x1b[1m"..s.."\x1b[0m") or tostring(s)
|
|
||||||
end
|
|
||||||
|
|
||||||
function success(map, fn)
|
|
||||||
-- [<tilenum>] = <count of psky ceilings with that tile number>
|
|
||||||
tilecnt = {}
|
|
||||||
|
|
||||||
-- [<idx>] = <tilenum>
|
|
||||||
local tiles = {}
|
|
||||||
-- [<tilenum>] = <arbitrary sectnum with that psky ceiling tile>
|
|
||||||
local tilesect = {}
|
|
||||||
|
|
||||||
for i=0,map.numsectors-1 do
|
|
||||||
local sec = map.sector[i]
|
|
||||||
|
|
||||||
if (bit.band(sec.ceilingstat, 1) ~= 0) then
|
|
||||||
local tile = sec.ceilingpicnum
|
|
||||||
|
|
||||||
if (tilecnt[tile] == nil) then
|
|
||||||
tiles[#tiles+1] = tile
|
|
||||||
end
|
|
||||||
|
|
||||||
tilecnt[tile] = 1 + (tilecnt[tile] or 0)
|
|
||||||
tilesect[tile] = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (#tiles > 0) then
|
|
||||||
-- Do an in-place sort.
|
|
||||||
table.sort(tiles, sortskytiles)
|
|
||||||
|
|
||||||
local strbuf = {}
|
|
||||||
|
|
||||||
for i=1,#tiles do
|
|
||||||
local tile = tiles[i]
|
|
||||||
local ismulti = IS_MULTI_PSKY_TILE[tile]
|
|
||||||
|
|
||||||
if (i == 4) then
|
|
||||||
strbuf[#strbuf+1] = "..."
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
strbuf[#strbuf+1] = (ismulti and "*" or "#")..tile..
|
|
||||||
":"..tilecnt[tile]..
|
|
||||||
(opts.s and "" or ("(s"..tilesect[tile]..")"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local nummultiskies = 0
|
|
||||||
for i=1,#MULTI_PSKY_TILES do
|
|
||||||
if (tilecnt[MULTI_PSKY_TILES[i]]) then
|
|
||||||
nummultiskies = nummultiskies+1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- DCPT: distinct ceiling-psky tiles
|
|
||||||
printf("%s: %s%sDCPT: %s", fn, bold(#tiles),
|
|
||||||
opts.s and " " or bold(" ("..nummultiskies..") "),
|
|
||||||
table.concat(strbuf," "))
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,167 +0,0 @@
|
||||||
----------------------------------------------------------------------------
|
|
||||||
-- Verbose mode of the LuaJIT compiler.
|
|
||||||
--
|
|
||||||
-- Copyright (C) 2005-2015 Mike Pall. All rights reserved.
|
|
||||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
|
||||||
----------------------------------------------------------------------------
|
|
||||||
--
|
|
||||||
-- This module shows verbose information about the progress of the
|
|
||||||
-- JIT compiler. It prints one line for each generated trace. This module
|
|
||||||
-- is useful to see which code has been compiled or where the compiler
|
|
||||||
-- punts and falls back to the interpreter.
|
|
||||||
--
|
|
||||||
-- Example usage:
|
|
||||||
--
|
|
||||||
-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
|
|
||||||
-- luajit -jv=myapp.out myapp.lua
|
|
||||||
--
|
|
||||||
-- Default output is to stderr. To redirect the output to a file, pass a
|
|
||||||
-- filename as an argument (use '-' for stdout) or set the environment
|
|
||||||
-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
|
|
||||||
-- module is started.
|
|
||||||
--
|
|
||||||
-- The output from the first example should look like this:
|
|
||||||
--
|
|
||||||
-- [TRACE 1 (command line):1 loop]
|
|
||||||
-- [TRACE 2 (1/3) (command line):1 -> 1]
|
|
||||||
--
|
|
||||||
-- The first number in each line is the internal trace number. Next are
|
|
||||||
-- the file name ('(command line)') and the line number (':1') where the
|
|
||||||
-- trace has started. Side traces also show the parent trace number and
|
|
||||||
-- the exit number where they are attached to in parentheses ('(1/3)').
|
|
||||||
-- An arrow at the end shows where the trace links to ('-> 1'), unless
|
|
||||||
-- it loops to itself.
|
|
||||||
--
|
|
||||||
-- In this case the inner loop gets hot and is traced first, generating
|
|
||||||
-- a root trace. Then the last exit from the 1st trace gets hot, too,
|
|
||||||
-- and triggers generation of the 2nd trace. The side trace follows the
|
|
||||||
-- path along the outer loop and *around* the inner loop, back to its
|
|
||||||
-- start, and then links to the 1st trace. Yes, this may seem unusual,
|
|
||||||
-- if you know how traditional compilers work. Trace compilers are full
|
|
||||||
-- of surprises like this -- have fun! :-)
|
|
||||||
--
|
|
||||||
-- Aborted traces are shown like this:
|
|
||||||
--
|
|
||||||
-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
|
|
||||||
--
|
|
||||||
-- Don't worry -- trace aborts are quite common, even in programs which
|
|
||||||
-- can be fully compiled. The compiler may retry several times until it
|
|
||||||
-- finds a suitable trace.
|
|
||||||
--
|
|
||||||
-- Of course this doesn't work with features that are not-yet-implemented
|
|
||||||
-- (NYI error messages). The VM simply falls back to the interpreter. This
|
|
||||||
-- may not matter at all if the particular trace is not very high up in
|
|
||||||
-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
|
|
||||||
--
|
|
||||||
-- Also check out the -jdump module, which prints all the gory details.
|
|
||||||
--
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Cache some library functions and objects.
|
|
||||||
local jit = require("jit")
|
|
||||||
assert(jit.version_num == 20003, "LuaJIT core/library version mismatch")
|
|
||||||
local jutil = require("jit.util")
|
|
||||||
local vmdef = require("jit.vmdef")
|
|
||||||
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
|
|
||||||
local type, format = type, string.format
|
|
||||||
local stdout, stderr = io.stdout, io.stderr
|
|
||||||
|
|
||||||
-- Active flag and output file handle.
|
|
||||||
local active, out
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
local startloc, startex
|
|
||||||
|
|
||||||
local function fmtfunc(func, pc)
|
|
||||||
local fi = funcinfo(func, pc)
|
|
||||||
if fi.loc then
|
|
||||||
return fi.loc
|
|
||||||
elseif fi.ffid then
|
|
||||||
return vmdef.ffnames[fi.ffid]
|
|
||||||
elseif fi.addr then
|
|
||||||
return format("C:%x", fi.addr)
|
|
||||||
else
|
|
||||||
return "(?)"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Format trace error message.
|
|
||||||
local function fmterr(err, info)
|
|
||||||
if type(err) == "number" then
|
|
||||||
if type(info) == "function" then info = fmtfunc(info) end
|
|
||||||
err = format(vmdef.traceerr[err], info)
|
|
||||||
end
|
|
||||||
return err
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Dump trace states.
|
|
||||||
local function dump_trace(what, tr, func, pc, otr, oex)
|
|
||||||
if what == "start" then
|
|
||||||
startloc = fmtfunc(func, pc)
|
|
||||||
startex = otr and "("..otr.."/"..oex..") " or ""
|
|
||||||
else
|
|
||||||
if what == "abort" then
|
|
||||||
local loc = fmtfunc(func, pc)
|
|
||||||
if loc ~= startloc then
|
|
||||||
out:write(format("[TRACE --- %s%s -- %s at %s]\n",
|
|
||||||
startex, startloc, fmterr(otr, oex), loc))
|
|
||||||
else
|
|
||||||
out:write(format("[TRACE --- %s%s -- %s]\n",
|
|
||||||
startex, startloc, fmterr(otr, oex)))
|
|
||||||
end
|
|
||||||
elseif what == "stop" then
|
|
||||||
local info = traceinfo(tr)
|
|
||||||
local link, ltype = info.link, info.linktype
|
|
||||||
if ltype == "interpreter" then
|
|
||||||
out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
|
|
||||||
tr, startex, startloc))
|
|
||||||
elseif link == tr or link == 0 then
|
|
||||||
out:write(format("[TRACE %3s %s%s %s]\n",
|
|
||||||
tr, startex, startloc, ltype))
|
|
||||||
elseif ltype == "root" then
|
|
||||||
out:write(format("[TRACE %3s %s%s -> %d]\n",
|
|
||||||
tr, startex, startloc, link))
|
|
||||||
else
|
|
||||||
out:write(format("[TRACE %3s %s%s -> %d %s]\n",
|
|
||||||
tr, startex, startloc, link, ltype))
|
|
||||||
end
|
|
||||||
else
|
|
||||||
out:write(format("[TRACE %s]\n", what))
|
|
||||||
end
|
|
||||||
out:flush()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
-- Detach dump handlers.
|
|
||||||
local function dumpoff()
|
|
||||||
if active then
|
|
||||||
active = false
|
|
||||||
jit.attach(dump_trace)
|
|
||||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
|
||||||
out = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Open the output file and attach dump handlers.
|
|
||||||
local function dumpon(outfile)
|
|
||||||
if active then dumpoff() end
|
|
||||||
if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
|
|
||||||
if outfile then
|
|
||||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
|
||||||
else
|
|
||||||
out = stderr
|
|
||||||
end
|
|
||||||
jit.attach(dump_trace, "trace")
|
|
||||||
active = true
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Public module functions.
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
on = dumpon
|
|
||||||
off = dumpoff
|
|
||||||
start = dumpon -- For -j command line option.
|
|
||||||
|
|
|
@ -1,357 +0,0 @@
|
||||||
-- "Extended" math module for Lunatic.
|
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
|
|
||||||
local bit = require("bit")
|
|
||||||
local math = require("math")
|
|
||||||
|
|
||||||
local arshift = bit.arshift
|
|
||||||
local abs, sqrt = math.abs, math.sqrt
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local error = error
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
local OUR_REQUIRE_STRING = [[
|
|
||||||
local _xm=require'xmath'
|
|
||||||
local _v,_iv=_xm.vec3,_xm.ivec3
|
|
||||||
]]
|
|
||||||
local function our_get_require()
|
|
||||||
return OUR_REQUIRE_STRING
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
---=== TRIGONOMETRY ===---
|
|
||||||
|
|
||||||
local BANG2RAD = math.pi/1024
|
|
||||||
local isintab = ffi.new("int16_t [?]", 2048)
|
|
||||||
local dsintab = ffi.new("double [?]", 2048)
|
|
||||||
|
|
||||||
for a=0,511 do
|
|
||||||
local s = math.sin(a*BANG2RAD)
|
|
||||||
isintab[a] = 16384*s
|
|
||||||
dsintab[a] = s
|
|
||||||
end
|
|
||||||
|
|
||||||
isintab[512] = 16384
|
|
||||||
dsintab[512] = 1
|
|
||||||
|
|
||||||
for i=513,1023 do
|
|
||||||
isintab[i] = isintab[1024-i];
|
|
||||||
dsintab[i] = dsintab[1024-i];
|
|
||||||
end
|
|
||||||
|
|
||||||
for i=1024,2047 do
|
|
||||||
isintab[i] = -isintab[i-1024];
|
|
||||||
dsintab[i] = -dsintab[i-1024];
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local band = bit.band
|
|
||||||
|
|
||||||
local function ksc_common(ang)
|
|
||||||
ang = band(ang, 2047)
|
|
||||||
assert(ang >= 0 and ang < 2048) -- might have been passed NaN
|
|
||||||
return ang
|
|
||||||
end
|
|
||||||
|
|
||||||
-- k{sin,cos}: 16384-scaled output, 2048-based angle input
|
|
||||||
function ksin(ang)
|
|
||||||
return isintab[ksc_common(ang)]
|
|
||||||
end
|
|
||||||
|
|
||||||
function kcos(ang)
|
|
||||||
return isintab[ksc_common(ang+512)]
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local sin, cos = math.sin, math.cos
|
|
||||||
|
|
||||||
-- {sin,cos}b: [-1..1] output, 2048-based angle input
|
|
||||||
function sinb(ang)
|
|
||||||
return dsintab[ksc_common(ang)]
|
|
||||||
end
|
|
||||||
|
|
||||||
function cosb(ang)
|
|
||||||
return dsintab[ksc_common(ang+512)]
|
|
||||||
end
|
|
||||||
|
|
||||||
local cosb, sinb = cosb, sinb
|
|
||||||
|
|
||||||
|
|
||||||
---=== Approximations to 2D and 3D Euclidean distances ===---
|
|
||||||
-- (also see common.c)
|
|
||||||
|
|
||||||
local function dist_common(pos1, pos2)
|
|
||||||
local x = abs(pos1.x - pos2.x)
|
|
||||||
local y = abs(pos1.y - pos2.y)
|
|
||||||
if (x < y) then
|
|
||||||
x, y = y, x
|
|
||||||
end
|
|
||||||
return x, y
|
|
||||||
end
|
|
||||||
|
|
||||||
function ldist(pos1, pos2)
|
|
||||||
local x, y = dist_common(pos1, pos2)
|
|
||||||
|
|
||||||
local t = y + arshift(y,1)
|
|
||||||
return x - arshift(x,5) - arshift(x,7) + arshift(t,2) + arshift(t,6)
|
|
||||||
end
|
|
||||||
|
|
||||||
function dist(pos1, pos2)
|
|
||||||
local x, y = dist_common(pos1, pos2)
|
|
||||||
local z = abs(arshift(pos1.z - pos2.z, 4))
|
|
||||||
|
|
||||||
if (x < z) then
|
|
||||||
x, z = z, x
|
|
||||||
end
|
|
||||||
|
|
||||||
local t = y + z
|
|
||||||
return x - arshift(x,4) + arshift(t,2) + arshift(t,3)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
---=== VECTOR TYPES ===---
|
|
||||||
|
|
||||||
|
|
||||||
-- The integer 3-vector can be useful for calculations expecting integer
|
|
||||||
-- values, e.g. ivec3(x, y, z) is a reasonable way to round a vec3. It can also
|
|
||||||
-- be used as the RHS to the vec2/vec3 arithmetic methods.
|
|
||||||
-- NOTE: We must have a typedef with that exact name, because for Lunatic
|
|
||||||
-- (i.e. not stand-alone), the type was already declared in defs_common.lua.
|
|
||||||
ffi.cdef "typedef struct { int32_t x, y, z; } vec3_t;"
|
|
||||||
local ivec3_t = ffi.typeof("vec3_t")
|
|
||||||
|
|
||||||
|
|
||||||
local dvec2_t = ffi.typeof("struct { double x, y; }")
|
|
||||||
local dvec3_t = ffi.typeof("struct { double x, y, z; }")
|
|
||||||
|
|
||||||
local vec2_mt = {
|
|
||||||
__add = function(a, b) return dvec2_t(a.x+b.x, a.y+b.y) end,
|
|
||||||
__sub = function(a, b) return dvec2_t(a.x-b.x, a.y-b.y) end,
|
|
||||||
__unm = function(a) return dvec2_t(-a.x, -a.y) end,
|
|
||||||
|
|
||||||
__mul = function(a,b)
|
|
||||||
if (type(a)=="number") then
|
|
||||||
return dvec2_t(a*b.x, a*b.y)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (type(b)~="number") then
|
|
||||||
error("number expected in vec2 multiplication", 2)
|
|
||||||
end
|
|
||||||
return dvec2_t(a.x*b, a.y*b)
|
|
||||||
end,
|
|
||||||
|
|
||||||
__div = function(a,b)
|
|
||||||
if (type(b)~="number") then
|
|
||||||
error("number expected in vec2 division", 2)
|
|
||||||
end
|
|
||||||
return dvec2_t(a.x/b, a.y/b)
|
|
||||||
end,
|
|
||||||
|
|
||||||
__tostring = function(a) return "vec2("..a.x..", "..a.y..")" end,
|
|
||||||
|
|
||||||
__index = {
|
|
||||||
lensq = function(a) return a.x*a.x + a.y*a.y end,
|
|
||||||
|
|
||||||
mhlen = function(a) return abs(a.x)+abs(a.y) end,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
local l_rotate -- fwd-decl (XXX: could be the other way around)
|
|
||||||
|
|
||||||
-- The vec3 metatable is shared between the integer- and double-based 3-vector
|
|
||||||
-- types. However, some operations are slightly different.
|
|
||||||
local vec3_mt = {
|
|
||||||
-- Arithmetic operations. Note that they always return a dvec3.
|
|
||||||
__add = function(a, b) return dvec3_t(a.x+b.x, a.y+b.y, a.z+b.z) end,
|
|
||||||
__sub = function(a, b) return dvec3_t(a.x-b.x, a.y-b.y, a.z-b.z) end,
|
|
||||||
__unm = function(a) return dvec3_t(-a.x, -a.y, -a.z) end,
|
|
||||||
|
|
||||||
__mul = function(a,b)
|
|
||||||
if (type(a)=="number") then
|
|
||||||
return dvec3_t(a*b.x, a*b.y, a*b.z)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (type(b)~="number") then
|
|
||||||
error("number expected in vec3 multiplication", 2)
|
|
||||||
end
|
|
||||||
return dvec3_t(a.x*b, a.y*b, a.z*b)
|
|
||||||
end,
|
|
||||||
|
|
||||||
__div = function(a,b)
|
|
||||||
if (type(b)~="number") then
|
|
||||||
error("number expected in vec3 division", 2)
|
|
||||||
end
|
|
||||||
return dvec3_t(a.x/b, a.y/b, a.z/b)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- '^' is the "translate upwards" operator, returns same-typed vector.
|
|
||||||
__pow = function(v, zofs)
|
|
||||||
return v:_ctor(v.x, v.y, v.z-zofs)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Convenience for human-readable display.
|
|
||||||
__tostring = function(a)
|
|
||||||
return (a:_isi() and "i" or "").."vec3("..a.x..", "..a.y..", "..a.z..")"
|
|
||||||
end,
|
|
||||||
|
|
||||||
__index = {
|
|
||||||
-- Euclidean 3D length.
|
|
||||||
len = function(a) return sqrt(a.x*a.x + a.y*a.y + a.z*a.z) end,
|
|
||||||
-- Euclidean 3D squared length.
|
|
||||||
lensq = function(a) return a.x*a.x + a.y*a.y + a.z*a.z end,
|
|
||||||
|
|
||||||
-- Euclidean 2D length.
|
|
||||||
len2 = function(a) return sqrt(a.x*a.x + a.y*a.y) end,
|
|
||||||
-- Euclidean 2D squared length.
|
|
||||||
len2sq = function(a) return a.x*a.x + a.y*a.y end,
|
|
||||||
|
|
||||||
-- Manhattan-distance 3D length:
|
|
||||||
mhlen = function(a) return abs(a.x)+abs(a.y)+abs(a.z) end,
|
|
||||||
|
|
||||||
toivec3 = function(v) return ivec3_t(v.x, v.y, v.z) end,
|
|
||||||
|
|
||||||
-- BUILD-coordinate (z scaled by 16) <-> uniform conversions.
|
|
||||||
touniform = function(v)
|
|
||||||
return v:_isi()
|
|
||||||
and v:_ctor(v.x, v.y, arshift(v.z, 4))
|
|
||||||
or v:_ctor(v.x, v.y, v.z/16)
|
|
||||||
end,
|
|
||||||
|
|
||||||
tobuild = function(v) return v:_ctor(v.x, v.y, 16*v.z) end,
|
|
||||||
|
|
||||||
rotate = function(v, ang, pivot) return l_rotate(v, ang, pivot) end,
|
|
||||||
|
|
||||||
-- PRIVATE methods --
|
|
||||||
|
|
||||||
-- Get the type constructor for this vector.
|
|
||||||
_ctor = function(v, ...)
|
|
||||||
return v:_isi() and ivec3_t(...) or dvec3_t(...)
|
|
||||||
end,
|
|
||||||
-- Is <v> integer vec3? INTERNAL.
|
|
||||||
_isi = function(v)
|
|
||||||
return ffi.istype(ivec3_t, v)
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Serialization ---
|
|
||||||
_get_require = our_get_require,
|
|
||||||
|
|
||||||
_serialize = function(v)
|
|
||||||
return (v:_isi() and "_iv" or "_v").."("..v.x..","..v.y..","..v.z..")"
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi.metatype(dvec2_t, vec2_mt)
|
|
||||||
ffi.metatype(dvec3_t, vec3_mt)
|
|
||||||
ffi.metatype(ivec3_t, vec3_mt)
|
|
||||||
|
|
||||||
-- VEC2 user data constructor.
|
|
||||||
-- * vec2([x [, y]]), assuming that x and y are numbers. Vacant positions are
|
|
||||||
-- assumed to be 0.
|
|
||||||
-- * vec2(<compound>), <compound> can be anything indexable with "x" and "y"
|
|
||||||
function vec2(...)
|
|
||||||
local x, y = ...
|
|
||||||
if (type(x)=="number" or x==nil) then
|
|
||||||
return dvec2_t(...)
|
|
||||||
else
|
|
||||||
return dvec2_t(x.x, x.y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- VEC3 user data constructor.
|
|
||||||
-- Analogous to VEC2.
|
|
||||||
function vec3(...)
|
|
||||||
local x, y, z = ...
|
|
||||||
if (type(x)=="number" or x==nil) then
|
|
||||||
return dvec3_t(...)
|
|
||||||
else
|
|
||||||
return dvec3_t(x.x, x.y, x.z)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- IVEC3 user data constructor.
|
|
||||||
function ivec3(...)
|
|
||||||
local x, y, z = ...
|
|
||||||
if (type(x)=="number" or x==nil) then
|
|
||||||
return ivec3_t(...)
|
|
||||||
else
|
|
||||||
return ivec3_t(x.x, x.y, x.z)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local vec2, vec3 = vec2, vec3
|
|
||||||
|
|
||||||
|
|
||||||
---=== MISCELLANEOUS MATH ===---
|
|
||||||
|
|
||||||
local intarg = ffi.new("int32_t [1]")
|
|
||||||
function bangvec(bang)
|
|
||||||
intarg[0] = bang -- round towards zero
|
|
||||||
return dvec3_t(cosb(intarg[0]), sinb(intarg[0]))
|
|
||||||
end
|
|
||||||
|
|
||||||
function kangvec(bang, z)
|
|
||||||
intarg[0] = bang -- round towards zero
|
|
||||||
return ivec3_t(kcos(intarg[0]), ksin(intarg[0]), z or 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function angvec(ang)
|
|
||||||
return dvec3_t(cos(ang), sin(ang))
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local zerovec = vec3()
|
|
||||||
-- Point rotation. Note the different order of arguments from engine function.
|
|
||||||
-- XXX: passing mixed vec2/vec3 is problematic. Get rid of vec2?
|
|
||||||
-- <ang>: BUILD angle (0-2047 based)
|
|
||||||
function rotate(pos, ang, pivot)
|
|
||||||
pivot = pivot or zerovec
|
|
||||||
local p = vec3(pos)-pivot
|
|
||||||
local c, s = cosb(ang), sinb(ang)
|
|
||||||
local x, y = p.x, p.y
|
|
||||||
p.x = pivot.x + (c*x - s*y)
|
|
||||||
p.y = pivot.y + (c*y + s*x)
|
|
||||||
return p
|
|
||||||
end
|
|
||||||
|
|
||||||
l_rotate = rotate
|
|
||||||
|
|
||||||
|
|
||||||
-- Two-element vector cross product.
|
|
||||||
-- Anti-commutative, distributive.
|
|
||||||
local function cross2(v, w)
|
|
||||||
return v.y*w.x - v.x*w.y
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
-- Finds the intersection point of two lines given by
|
|
||||||
-- point a and vector v
|
|
||||||
-- and
|
|
||||||
-- point b and vector w
|
|
||||||
--
|
|
||||||
-- Returns:
|
|
||||||
-- if <TODO>, nil
|
|
||||||
-- if retpoint_p evaluates to a non-true value, coefficients cv and cw such that <TODO>
|
|
||||||
-- else, the intersection point
|
|
||||||
function intersect(a,v, b,w, retpoint_p)
|
|
||||||
local vxw = cross2(v,w)
|
|
||||||
|
|
||||||
if (vxw ~= 0) then
|
|
||||||
local btoa = vec2(a) - vec2(b)
|
|
||||||
local cv, cw = cross2(w, btoa)/vxw, cross2(v, btoa)/vxw
|
|
||||||
|
|
||||||
if (retpoint_p) then
|
|
||||||
return vec2(a) + cv*vec2(v)
|
|
||||||
else
|
|
||||||
return cv, cw
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- return nil if v and w parallel (or either of them is a point), or if
|
|
||||||
-- they contain NaNs
|
|
||||||
end
|
|
|
@ -29,10 +29,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
#include "vfs.h"
|
#include "vfs.h"
|
||||||
|
|
||||||
#ifdef LUNATIC
|
|
||||||
# include "lunatic_game.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static uint8_t precachehightile[2][(MAXTILES+7)>>3];
|
static uint8_t precachehightile[2][(MAXTILES+7)>>3];
|
||||||
static int32_t g_precacheCount;
|
static int32_t g_precacheCount;
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
#include "premap.h"
|
#include "premap.h"
|
||||||
#include "prlights.h"
|
#include "prlights.h"
|
||||||
#include "savegame.h"
|
#include "savegame.h"
|
||||||
#ifdef LUNATIC
|
|
||||||
# include "lunatic_game.h"
|
|
||||||
static int32_t g_savedOK;
|
|
||||||
const char *g_failedVarname;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "vfs.h"
|
#include "vfs.h"
|
||||||
|
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
-- INTERNAL
|
|
||||||
-- definitions of BUILD and game types for the Lunatic Interpreter
|
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local ffiC = ffi.C
|
|
||||||
|
|
||||||
ffi.cdef[[
|
|
||||||
enum {
|
|
||||||
LUNATIC_CLIENT_MAPSTER32 = 0,
|
|
||||||
LUNATIC_CLIENT_EDUKE32 = 1,
|
|
||||||
|
|
||||||
LUNATIC_CLIENT = LUNATIC_CLIENT_MAPSTER32
|
|
||||||
}
|
|
||||||
]]
|
|
||||||
|
|
||||||
--== First, load the definitions common to the game's and editor's Lua interface.
|
|
||||||
decl = ffi.cdef
|
|
||||||
local defs_c = require("defs_common")
|
|
||||||
defs_c.finish_spritetype({})
|
|
||||||
|
|
||||||
defs_c.create_globals(_G)
|
|
||||||
|
|
||||||
-- TODO: provide access to only a subset, restict access to ffiC?
|
|
||||||
gv = ffiC
|
|
||||||
|
|
||||||
--== Mapster32-specific initialization
|
|
||||||
|
|
||||||
ffi.cdef "char *listsearchpath(int32_t initp);"
|
|
||||||
|
|
||||||
-- Add the search path directories to the Lua load path.
|
|
||||||
local initp = 1
|
|
||||||
while (true) do
|
|
||||||
local sp_c = ffiC.listsearchpath(initp)
|
|
||||||
|
|
||||||
if (sp_c == nil) then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
local sp = ffi.string(sp_c)
|
|
||||||
assert(sp:sub(-1)=='/')
|
|
||||||
package.path = sp..'?.lua;'..package.path
|
|
||||||
|
|
||||||
initp = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Helper functions
|
|
||||||
local package = package
|
|
||||||
local require = require
|
|
||||||
|
|
||||||
function reload(modname)
|
|
||||||
package.loaded[modname] = nil
|
|
||||||
return require(modname)
|
|
||||||
end
|
|
||||||
|
|
||||||
--print('Lua load path: '..package.path)
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,124 +0,0 @@
|
||||||
-- Implementation of a bound-checked array type factory for LuaJIT 2.0 or later.
|
|
||||||
--
|
|
||||||
-- Usage example:
|
|
||||||
--
|
|
||||||
-- > bcarray.new("int8_t", 3, "test", "three_pigs")
|
|
||||||
-- > a = ffi.new("struct { int32_t a; three_pigs p; int16_t b; }")
|
|
||||||
-- > =ffi.sizeof(a) --> 12
|
|
||||||
-- > b = ffi.new("__attribute__((packed)) struct { int32_t a; three_pigs p; int16_t b; }")
|
|
||||||
-- > =ffi.sizeof(b) --> 9
|
|
||||||
|
|
||||||
local ffi = require("ffi")
|
|
||||||
|
|
||||||
local string = require("string")
|
|
||||||
local table = require("table")
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local error = error
|
|
||||||
local pairs = pairs
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
|
|
||||||
local bcarray = {}
|
|
||||||
|
|
||||||
|
|
||||||
-- Generate C decl for a sequence of <nelts> const struct members.
|
|
||||||
-- For example, for 4 elements,
|
|
||||||
-- "const $ _r1, _f2, _u3, _n4;"
|
|
||||||
local function flatten_array(nelts, rng)
|
|
||||||
local strtab = { "$ " }
|
|
||||||
|
|
||||||
if (rng and rng.getu32==nil) then
|
|
||||||
assert(type(rng)=="table")
|
|
||||||
|
|
||||||
for i=1,#rng do
|
|
||||||
strtab[i+1] = rng[i]..((i<#rng) and "," or ";")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for i=1,nelts do
|
|
||||||
local ch = 97 + (rng and (rng:getu32() % 25) or 0) -- 'a'..'z'
|
|
||||||
strtab[i+1] = string.format("_%c%x%s", ch, i, (i<nelts) and "," or ";")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.concat(strtab)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ctype = bcarray.new(basetype, numelts, showname [, typename] [, rng] [, mtadd])
|
|
||||||
-- (optional fields may be nil)
|
|
||||||
--
|
|
||||||
-- <numelts>: Number of elements in array (small number)
|
|
||||||
-- <showname>: The name to be shown for the derived type in error messages
|
|
||||||
-- <typename>: If non-nil, the name under which the derived type is typedef'd
|
|
||||||
-- <rng>: Random generator state + method :getu32(). If nil, then members are
|
|
||||||
-- named _a1, _a2, ...
|
|
||||||
-- It also may be a table containing member names at numeric indices 1..#rng.
|
|
||||||
-- <mtadd>: A table containing functions __index and/or __newindex. They are
|
|
||||||
-- called first and the bound-checking ones are tail-called then. If the
|
|
||||||
-- custom __index one returns something, it is returned by the composite one.
|
|
||||||
function bcarray.new(basetype, numelts, showname, typename, rng, mtadd)
|
|
||||||
local eltptr_t = ffi.typeof("$ *", ffi.typeof(basetype))
|
|
||||||
|
|
||||||
local mt = {
|
|
||||||
__index = function(ar, idx)
|
|
||||||
if (not (idx >= 0 and idx < numelts)) then
|
|
||||||
error("out-of-bounds "..showname.." read access", 2)
|
|
||||||
end
|
|
||||||
return ffi.cast(eltptr_t, ar)[idx]
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- NOTE: this function will be dead code if the prefixed __newindex
|
|
||||||
-- errors out unconditionally or the bcarray is declared 'const'.
|
|
||||||
__newindex = function(ar, idx, val)
|
|
||||||
if (not (idx >= 0 and idx < numelts)) then
|
|
||||||
error("out-of-bounds "..showname.." write access", 2)
|
|
||||||
end
|
|
||||||
ffi.cast(eltptr_t, ar)[idx] = val
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mtadd ~= nil) then
|
|
||||||
local curindexf, curnewindexf = mt.__index, mt.__newindex
|
|
||||||
local addindexf, addnewindexf = mtadd.__index, mtadd.__newindex
|
|
||||||
|
|
||||||
if (addindexf) then
|
|
||||||
-- Additional __index metamethod given.
|
|
||||||
mt.__index = function(ar, idx)
|
|
||||||
local sth = addindexf(ar, idx)
|
|
||||||
if (sth ~= nil) then
|
|
||||||
return sth
|
|
||||||
end
|
|
||||||
return curindexf(ar, idx)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if (addnewindexf) then
|
|
||||||
-- Additional __newindex metamethod given.
|
|
||||||
mt.__newindex = function(ar, idx, val)
|
|
||||||
addnewindexf(ar, idx, val)
|
|
||||||
return curnewindexf(ar, idx, val)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local cdeclstr = "struct {"..flatten_array(numelts, rng).."}"
|
|
||||||
local bcarray_t = ffi.typeof(cdeclstr, ffi.typeof(basetype));
|
|
||||||
|
|
||||||
bcarray_t = ffi.metatype(bcarray_t, mt)
|
|
||||||
if (not (rng and rng.getu32==nil)) then
|
|
||||||
-- When passing a member name table, it is allowed to have a different
|
|
||||||
-- number of named members than array elements.
|
|
||||||
assert(ffi.sizeof(bcarray_t) == ffi.sizeof(basetype)*numelts)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (typename ~= nil) then
|
|
||||||
-- Register the type name in the global namespace.
|
|
||||||
assert(type(typename)=="string")
|
|
||||||
ffi.cdef("typedef $ $;", bcarray_t, typename)
|
|
||||||
end
|
|
||||||
|
|
||||||
return bcarray_t
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
return bcarray
|
|
|
@ -1,126 +0,0 @@
|
||||||
-- Bound-checking functions for engine and game "things".
|
|
||||||
|
|
||||||
local ffiC = require("ffi").C
|
|
||||||
local type = type
|
|
||||||
local error = error
|
|
||||||
|
|
||||||
local bcheck = {}
|
|
||||||
|
|
||||||
--== ENGINE ==--
|
|
||||||
|
|
||||||
function bcheck.sector_idx(sectnum)
|
|
||||||
if (not (sectnum >= 0 and sectnum < ffiC.numsectors)) then
|
|
||||||
error("invalid sector number "..sectnum, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.wall_idx(wallnum)
|
|
||||||
if (not (wallnum >= 0 and wallnum < ffiC.numwalls)) then
|
|
||||||
error("invalid wall number "..wallnum, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO: Provide another function that also checks whether the sprite exists in
|
|
||||||
-- the game world (statnum != MAXSTATUS).
|
|
||||||
function bcheck.sprite_idx(spritenum)
|
|
||||||
-- if (not (spritenum >= 0 and spritenum < ffiC.MAXSPRITES)) then
|
|
||||||
if (not (spritenum >= 0 and spritenum < ffiC.MAXSPRITES)) then
|
|
||||||
error("invalid sprite number "..spritenum, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.tile_idx(tilenum)
|
|
||||||
if (not (tilenum >= 0 and tilenum < ffiC.MAXTILES)) then
|
|
||||||
error("invalid tile number "..tilenum, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--== HELPERS ==--
|
|
||||||
|
|
||||||
function bcheck.number(val, errlev)
|
|
||||||
if (type(val)~="number" or val~=val) then
|
|
||||||
error("invalid argument: must be a non-NaN number", errlev or 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.type(val, typestr, errlev)
|
|
||||||
if (type(val)~=typestr) then
|
|
||||||
error("invalid argument: must be a "..typestr, errlev or 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--== GAME ==--
|
|
||||||
if (ffiC.LUNATIC_CLIENT == ffiC.LUNATIC_CLIENT_MAPSTER32) then
|
|
||||||
return bcheck
|
|
||||||
end
|
|
||||||
|
|
||||||
local con_lang = require("con_lang")
|
|
||||||
|
|
||||||
function bcheck.player_idx(snum)
|
|
||||||
if (not (snum >= 0 and snum < ffiC.g_mostConcurrentPlayers)) then
|
|
||||||
error("invalid player number "..snum, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.sound_idx(sndidx)
|
|
||||||
if (not (sndidx >= 0 and sndidx < con_lang.MAXSOUNDS)) then
|
|
||||||
error("invalid sound number "..sndidx, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.weapon_idx(weap)
|
|
||||||
if (not (weap >= 0 and weap < ffiC.MAX_WEAPONS)) then
|
|
||||||
error("Invalid weapon ID "..weap, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.inventory_idx(inv)
|
|
||||||
if (not (inv >= 0 and inv < ffiC.GET_MAX)) then
|
|
||||||
error("Invalid inventory ID "..inv, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.volume_idx(volume)
|
|
||||||
if (not (volume >= 0 and volume < con_lang.MAXVOLUMES)) then
|
|
||||||
error("invalid volume number "..volume, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.level_idx(level)
|
|
||||||
if (not (level >= 0 and level < con_lang.MAXLEVELS)) then
|
|
||||||
error("invalid level number "..level, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.linear_map_idx(idx)
|
|
||||||
if (not (idx >= 0 and idx <= con_lang.MAXLEVELS * con_lang.MAXVOLUMES)) then
|
|
||||||
error("invalid linear map index "..idx, 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.quote_idx(qnum, onlyidx)
|
|
||||||
if (not (qnum >= 0 and qnum < con_lang.MAXQUOTES)) then
|
|
||||||
error("invalid quote number "..qnum, 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
local cstr = ffiC.apStrings[qnum]
|
|
||||||
if (onlyidx) then
|
|
||||||
return cstr
|
|
||||||
end
|
|
||||||
|
|
||||||
if (cstr == nil) then
|
|
||||||
error("null quote "..qnum, 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
return cstr
|
|
||||||
end
|
|
||||||
|
|
||||||
function bcheck.top_level(funcname, errlev)
|
|
||||||
if (ffiC.g_elCallDepth > 0) then
|
|
||||||
error("Invalid use of "..funcname..": must be called from top level", errlev or 3)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return bcheck
|
|
|
@ -1,293 +0,0 @@
|
||||||
|
|
||||||
-- "Bit array" module based on LuaJIT's BitOp.
|
|
||||||
|
|
||||||
local bit = require "bit"
|
|
||||||
local math = require "math"
|
|
||||||
|
|
||||||
local ffi = require "ffi"
|
|
||||||
|
|
||||||
local assert = assert
|
|
||||||
local error = error
|
|
||||||
local type = type
|
|
||||||
|
|
||||||
local tostring = tostring
|
|
||||||
|
|
||||||
|
|
||||||
module(...)
|
|
||||||
|
|
||||||
|
|
||||||
local bitar_ct = ffi.typeof[[
|
|
||||||
struct {
|
|
||||||
const double maxbidx; // last permissible bit index
|
|
||||||
const double maxidx; // last permissible int32_t index
|
|
||||||
const intptr_t arptr; // address of the int32_t array
|
|
||||||
}]]
|
|
||||||
local ptr_to_int = ffi.typeof("int32_t *")
|
|
||||||
|
|
||||||
local anchor = {}
|
|
||||||
|
|
||||||
-- population count of a nibble
|
|
||||||
local nibpop = ffi.new("double [?]", 16,
|
|
||||||
{ 0, 1, 1, 2, 1, 2, 2, 3,
|
|
||||||
1, 2, 2, 3, 2, 3, 3, 4 })
|
|
||||||
-- ...and of a byte
|
|
||||||
local bytepop = ffi.new("double [?]", 256)
|
|
||||||
for i=0,255 do
|
|
||||||
bytepop[i] = nibpop[bit.band(i, 15)] + nibpop[bit.rshift(i, 4)]
|
|
||||||
end
|
|
||||||
nibpop = nil
|
|
||||||
|
|
||||||
local function bitar_from_intar(maxbidx, maxidx, ar)
|
|
||||||
-- We need to have the int32_t[?] array be reachable so that it will not be
|
|
||||||
-- garbage collected
|
|
||||||
local ar_intptr = ffi.cast("intptr_t", ar)
|
|
||||||
anchor[tostring(ar_intptr)] = ar
|
|
||||||
|
|
||||||
-- Leaving the (potential) high trailing bits at 0 lets us not worry
|
|
||||||
-- about them in the population count calculation (__len metamethod).
|
|
||||||
-- Also, this is correct for maxbidx%32 == 0, since BitOp's shifts
|
|
||||||
-- mask the 5 lower bits of the counts.
|
|
||||||
local numremain = bit.band(maxbidx+1, 31)
|
|
||||||
ar[maxidx] = bit.band(ar[maxidx], bit.rshift(-1, 32-numremain))
|
|
||||||
|
|
||||||
return bitar_ct(maxbidx, maxidx, ar_intptr)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setop_common_rel(s1, s2)
|
|
||||||
if (s1.maxbidx ~= s2.maxbidx) then
|
|
||||||
error("bad arguments to bit array set op: must be of same length", 4)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ar1 = ffi.cast(ptr_to_int, s1.arptr)
|
|
||||||
local ar2 = ffi.cast(ptr_to_int, s2.arptr)
|
|
||||||
|
|
||||||
return ar1, ar2
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setop_common(s1, s2)
|
|
||||||
if (not ffi.istype(bitar_ct, s1) or not ffi.istype(bitar_ct, s2)) then
|
|
||||||
error("bad arguments to bit array set op: both must be 'bitar' types", 3)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ar1, ar2 = setop_common_rel(s1, s2)
|
|
||||||
local ar = ffi.new("int32_t [?]", s1.maxidx+1)
|
|
||||||
|
|
||||||
return ar, ar1, ar2
|
|
||||||
end
|
|
||||||
|
|
||||||
local mt = {
|
|
||||||
--- Operational methods
|
|
||||||
|
|
||||||
__add = function(s1, s2) -- set union
|
|
||||||
local ar, ar1, ar2 = setop_common(s1, s2)
|
|
||||||
for i=0,s1.maxidx do
|
|
||||||
ar[i] = bit.bor(ar1[i], ar2[i])
|
|
||||||
end
|
|
||||||
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
|
|
||||||
end,
|
|
||||||
|
|
||||||
__mul = function(s1, s2) -- set intersection
|
|
||||||
local ar, ar1, ar2 = setop_common(s1, s2)
|
|
||||||
for i=0,s1.maxidx do
|
|
||||||
ar[i] = bit.band(ar1[i], ar2[i])
|
|
||||||
end
|
|
||||||
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
|
|
||||||
end,
|
|
||||||
|
|
||||||
__sub = function(s1, s2) -- set difference
|
|
||||||
local ar, ar1, ar2 = setop_common(s1, s2)
|
|
||||||
for i=0,s1.maxidx do
|
|
||||||
ar[i] = bit.band(ar1[i], bit.bnot(ar2[i]))
|
|
||||||
end
|
|
||||||
return bitar_from_intar(s1.maxbidx, s1.maxidx, ar)
|
|
||||||
end,
|
|
||||||
|
|
||||||
__unm = function(s) -- bitwise NOT
|
|
||||||
local newar = ffi.new("int32_t [?]", s.maxidx+1)
|
|
||||||
local oldar = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
for i=0,s.maxidx do
|
|
||||||
newar[i] = bit.bnot(oldar[i])
|
|
||||||
end
|
|
||||||
return bitar_from_intar(s.maxbidx, s.maxidx, newar)
|
|
||||||
end,
|
|
||||||
|
|
||||||
|
|
||||||
--- Additional operations
|
|
||||||
|
|
||||||
__index = {
|
|
||||||
-- TODO: Rename to 'testi', 'seti', 'cleari'; add 'flipi'?
|
|
||||||
|
|
||||||
-- Is bit i set?
|
|
||||||
isset = function(s, i)
|
|
||||||
if (not (i >= 0 and i<=s.maxbidx)) then
|
|
||||||
error("bad bit index for isset: must be in [0.."..s.maxbidx.."]", 2)
|
|
||||||
end
|
|
||||||
s = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
return (bit.band(s[bit.rshift(i, 5)], bit.lshift(1, i)) ~= 0)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Clear bit i.
|
|
||||||
set0 = function(s, i)
|
|
||||||
if (not (i >= 0 and i<=s.maxbidx)) then
|
|
||||||
error("bad bit index for set0: must be in [0.."..s.maxbidx.."]", 2)
|
|
||||||
end
|
|
||||||
s = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
local jx = bit.rshift(i, 5)
|
|
||||||
s[jx] = bit.band(s[jx], bit.rol(0xfffffffe, i))
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- Set bit i.
|
|
||||||
set1 = function(s, i)
|
|
||||||
if (not (i >= 0 and i<=s.maxbidx)) then
|
|
||||||
error("bad bit index for set1: must be in [0.."..s.maxbidx.."]", 2)
|
|
||||||
end
|
|
||||||
s = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
local jx = bit.rshift(i, 5)
|
|
||||||
s[jx] = bit.bor(s[jx], bit.rol(0x00000001, i))
|
|
||||||
end
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
--- Relational methods
|
|
||||||
|
|
||||||
__eq = function(s1, s2) -- set identity
|
|
||||||
local ar1, ar2 = setop_common_rel(s1, s2)
|
|
||||||
for i=0,s1.maxidx do
|
|
||||||
if (bit.bxor(ar1[i], ar2[i]) ~= 0) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
|
|
||||||
__le = function(s1, s2)
|
|
||||||
local ar1, ar2 = setop_common_rel(s1, s2)
|
|
||||||
for i=0,s1.maxidx do
|
|
||||||
if (bit.band(ar1[i], bit.bnot(ar2[i])) ~= 0) then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end,
|
|
||||||
|
|
||||||
__lt = function(s1, s2)
|
|
||||||
return s1 <= s2 and not (s2 == s1)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- The length operator gets the population count of the bit array, i.e. the
|
|
||||||
-- number of set bits.
|
|
||||||
__len = function(s)
|
|
||||||
local ar = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
local popcnt = 0
|
|
||||||
for i=0,s.maxidx do
|
|
||||||
popcnt = popcnt + bytepop[bit.band(ar[i], 255)] +
|
|
||||||
bytepop[bit.band(bit.rshift(ar[i], 8), 255)] +
|
|
||||||
bytepop[bit.band(bit.rshift(ar[i], 16), 255)] +
|
|
||||||
bytepop[bit.rshift(ar[i], 24)]
|
|
||||||
end
|
|
||||||
return popcnt
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- serialization
|
|
||||||
__tostring = function(s)
|
|
||||||
local size=s.maxidx+1
|
|
||||||
local ar = ffi.cast(ptr_to_int, s.arptr)
|
|
||||||
|
|
||||||
local hdr = "bitar.new("..s.maxbidx..", '"
|
|
||||||
local ofs = #hdr
|
|
||||||
local totalstrlen = ofs+8*size+2
|
|
||||||
local str = ffi.new("char [?]", totalstrlen)
|
|
||||||
|
|
||||||
ffi.copy(str, hdr, ofs)
|
|
||||||
|
|
||||||
for i=0,s.maxidx do
|
|
||||||
-- 'a' is ASCII 97
|
|
||||||
for nib=0,7 do
|
|
||||||
str[ofs + 8*i + nib] = 97 + bit.band(bit.rshift(ar[i], 4*nib), 0x0000000f)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ffi.copy(str+totalstrlen-2, "')", 2)
|
|
||||||
|
|
||||||
return ffi.string(str, totalstrlen)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-- On garbage collection of the bitar, clear the array's anchor so that it
|
|
||||||
-- can be collected too.
|
|
||||||
__gc = function(s)
|
|
||||||
anchor[tostring(s.arptr)] = nil
|
|
||||||
end,
|
|
||||||
|
|
||||||
__metatable = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi.metatype(bitar_ct, mt)
|
|
||||||
|
|
||||||
|
|
||||||
-- Create new bit array.
|
|
||||||
function new(maxbidx, initval)
|
|
||||||
if (type(maxbidx) ~= "number" or not (maxbidx >= 0 and maxbidx <= (2^31)-2)) then
|
|
||||||
-- NOTE: Uh-oh, we can't write '2^31' because that would be interpreted
|
|
||||||
-- as color by OSD_Printf.
|
|
||||||
error("bad argument #1 to bitar.new (must be a number in [0..(2**31)-2])", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if (math.floor(maxbidx) ~= maxbidx) then
|
|
||||||
error("bad argument #1 to bitar.new (must be an integral number)")
|
|
||||||
end
|
|
||||||
|
|
||||||
if (ffi.istype(ptr_to_int, initval)) then
|
|
||||||
-- Initialization from an array on the C side. INTERNAL.
|
|
||||||
-- Cannot be reached by user code since there's no access to the FFI
|
|
||||||
-- (and thus no way to create pointers).
|
|
||||||
|
|
||||||
return bitar_from_intar(maxbidx, (maxbidx+1)/32-1, initval)
|
|
||||||
|
|
||||||
elseif (type(initval)=="string") then
|
|
||||||
-- String containing hex digits (a..p) given, for INTERNAL use only.
|
|
||||||
-- XXX: Can be reached by user code.
|
|
||||||
|
|
||||||
local lstr = initval
|
|
||||||
|
|
||||||
local numnibs = #lstr
|
|
||||||
assert(numnibs%8 == 0)
|
|
||||||
|
|
||||||
local size = numnibs/8
|
|
||||||
local maxidx = size-1
|
|
||||||
local ar = ffi.new("int32_t [?]", size)
|
|
||||||
|
|
||||||
local str = ffi.new("char [?]", numnibs)
|
|
||||||
ffi.copy(str, lstr, numnibs)
|
|
||||||
|
|
||||||
for i=0,maxidx do
|
|
||||||
ar[i] = 0
|
|
||||||
|
|
||||||
for nib=0,7 do
|
|
||||||
local hexdig = str[8*i + nib]
|
|
||||||
assert(hexdig >= 97 and hexdig < 97+16)
|
|
||||||
ar[i] = bit.bor(ar[i], bit.lshift(hexdig-97, 4*nib))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- NOTE: <maxbidx> cannot be extracted from the string, use the passed one.
|
|
||||||
return bitar_from_intar(maxbidx, maxidx, ar)
|
|
||||||
|
|
||||||
else
|
|
||||||
-- User-requested bitar creation.
|
|
||||||
|
|
||||||
if (initval ~= 0 and initval ~= 1) then
|
|
||||||
error("bad argument #2 to bitar.new (must be either 0 or 1)", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local maxidx = math.floor(maxbidx/32)
|
|
||||||
local size = maxidx+1
|
|
||||||
|
|
||||||
local ar = ffi.new("int32_t [?]", size)
|
|
||||||
|
|
||||||
if (initval==1) then
|
|
||||||
ffi.fill(ar, size*4, -1)
|
|
||||||
end
|
|
||||||
|
|
||||||
return bitar_from_intar(maxbidx, maxidx, ar)
|
|
||||||
end
|
|
||||||
end
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue