Lunatic: retire per-sprite tsprite access for per-tile animation callback reg.

Don't yet make this official API though, since there are unresolved issues
with newly created tsprites potentially being fed back to the animation loop.
Add xmath.angvec(), xmath.bangvec(), tspr:set_sectnum(), tspr:setpos().

git-svn-id: https://svn.eduke32.com/eduke32@3937 1a8010ca-5511-0410-912e-c29ae57300e0
This commit is contained in:
helixhorned 2013-07-07 20:59:10 +00:00
parent c243bab0e7
commit 7f2175fcec
10 changed files with 133 additions and 47 deletions

View file

@ -7051,6 +7051,7 @@ static void G_DoEventAnimSprites(int32_t j)
if ((unsigned)ow < MAXSPRITES && spriteext[ow].flags & SPREXT_TSPRACCESS) if ((unsigned)ow < MAXSPRITES && spriteext[ow].flags & SPREXT_TSPRACCESS)
{ {
spriteext[ow].tspr = &tsprite[j]; spriteext[ow].tspr = &tsprite[j];
// XXX: wouldn't screenpeek be more meaningful as current player?
VM_OnEvent(EVENT_ANIMATESPRITES, ow, myconnectindex, -1, 0); VM_OnEvent(EVENT_ANIMATESPRITES, ow, myconnectindex, -1, 0);
spriteext[ow].tspr = NULL; spriteext[ow].tspr = NULL;
} }
@ -7884,6 +7885,11 @@ skip:
for (j = spritesortcnt-1; j>=0; j--) for (j = spritesortcnt-1; j>=0; j--)
G_DoEventAnimSprites(j); G_DoEventAnimSprites(j);
} }
#ifdef LUNATIC
if (G_HaveEvent(EVENT_ANIMATEALLSPRITES))
VM_OnEvent(EVENT_ANIMATEALLSPRITES, -1, -1, -1, 0);
#endif
} }

View file

@ -694,7 +694,10 @@ const char *EventNames[MAXEVENTS] =
"EVENT_LOADGAME", "EVENT_LOADGAME",
"EVENT_SAVEGAME", "EVENT_SAVEGAME",
"EVENT_PREGAME", "EVENT_PREGAME",
"EVENT_CHANGEMENU" "EVENT_CHANGEMENU",
#ifdef LUNATIC
"EVENT_ANIMATEALLSPRITES",
#endif
}; };
#if !defined LUNATIC #if !defined LUNATIC

View file

@ -125,6 +125,9 @@ enum GameEvent_t {
EVENT_SAVEGAME, EVENT_SAVEGAME,
EVENT_PREGAME, EVENT_PREGAME,
EVENT_CHANGEMENU, EVENT_CHANGEMENU,
#ifdef LUNATIC
EVENT_ANIMATEALLSPRITES, // 95
#endif
MAXEVENTS MAXEVENTS
}; };

View file

@ -168,6 +168,7 @@ EVENT = {
EVENT_SAVEGAME = 92, EVENT_SAVEGAME = 92,
EVENT_PREGAME = 93, EVENT_PREGAME = 93,
EVENT_CHANGEMENU = 94, EVENT_CHANGEMENU = 94,
-- EVENT_ANIMATEALLSPRITES = 95, -- internal
} }
-- NOTE: negated values are not exported to the ffi.C namespace or CON. -- NOTE: negated values are not exported to the ffi.C namespace or CON.
@ -331,7 +332,7 @@ wdata_members =
local SP = function(memb) return "sprite[%s]"..memb end local SP = function(memb) return "sprite[%s]"..memb end
local ATSP = function(memb) return "atsprite[%s]"..memb end local ATSP = function(memb) return "_atsprite[%s]"..memb end
local AC = function(memb) return "actor[%s]"..memb end local AC = function(memb) return "actor[%s]"..memb end
local SX = function(memb) return "spriteext[%s]"..memb end local SX = function(memb) return "spriteext[%s]"..memb end

View file

@ -1928,8 +1928,31 @@ local g_firstRun = (ffiC.g_elCONSize == 0)
-- Actor functions, saved for actor chaining -- Actor functions, saved for actor chaining
local actor_funcs = {} local actor_funcs = {}
-- Event functions, saved for event chaining
local event_funcs = {}
-- Per-actor sprite animation callbacks
local animsprite_funcs = {}
local gameactor_internal = gameactor_internal -- included in lunatic.c local gameactor_internal = gameactor_internal -- included in lunatic.c
local gameevent_internal = gameevent_internal -- included in lunatic.c
local function animate_all_sprites()
for i=0,ffiC.spritesortcnt-1 do
local tspr = ffiC.tsprite[i]
if (tspr.owner < ffiC.MAXSPRITES+0ULL) then
local spr = tspr:getspr()
local animfunc = animsprite_funcs[spr.picnum]
if (animfunc) then
animfunc(tspr)
end
end
end
end
-- gameactor{tilenum [, flags [, strength [, action [, move [, movflags]]]]], func} -- gameactor{tilenum [, flags [, strength [, action [, move [, movflags]]]]], func}
-- Every arg may be positional OR key=val (with the name indicated above as key), -- Every arg may be positional OR key=val (with the name indicated above as key),
-- but not both. -- but not both.
@ -2018,6 +2041,24 @@ local function our_gameactor(args)
end end
end end
-- Register a potentially passed drawn sprite animation callback function.
-- TODO: allow registering without main actor execution callback.
local animfunc = args.animate
if (animfunc ~= nil) then
if (type(animfunc) ~= "function") then
error("invalid 'animate' argument to gameactor: must be a function", 2)
end
animsprite_funcs[tilenum] = replacep and func
or (chainflags==AF.chain_beg) and chain_func3(animfunc, animsprite_funcs[tilenum])
or (chainflags==AF.chain_end) and chain_func3(animsprite_funcs[tilenum], animfunc)
or assert(false)
-- Register our EVENT_ANIMATEALLSPRITES only now so that it is not
-- called if there are no 'animate' definitions.
gameevent_internal(95, animate_all_sprites) -- EVENT_ANIMATEALLSPRITES
end
-- All good, set the tile bits in initial run and register the actor! -- All good, set the tile bits in initial run and register the actor!
-- XXX: Need to see if some other state living on the C side is clobbered -- XXX: Need to see if some other state living on the C side is clobbered
-- by Lua state recreation. -- by Lua state recreation.
@ -2040,10 +2081,7 @@ local function our_gameactor(args)
actor_funcs[tilenum] = newfunc actor_funcs[tilenum] = newfunc
end end
-- Event functions, saved for event chaining
local event_funcs = {}
local gameevent_internal = gameevent_internal -- included in lunatic.c
-- gameevent{<event idx or string> [, flags], <event function>} -- gameevent{<event idx or string> [, flags], <event function>}
local function our_gameevent(args) local function our_gameevent(args)
bcheck.top_level("gameevent") bcheck.top_level("gameevent")
@ -2313,7 +2351,6 @@ gv = setmtonce(gv_, tmpmt)
-- This will create 'sprite', 'wall', etc. HERE, i.e. in the environment of this chunk -- This will create 'sprite', 'wall', etc. HERE, i.e. in the environment of this chunk
defs_c.create_globals(_G) defs_c.create_globals(_G)
-- REMOVE this for release -- REMOVE this for release
DBG_ = {} DBG_ = {}
DBG_.debug = require("debug") DBG_.debug = require("debug")

View file

@ -155,6 +155,7 @@ string.format([[
local SPRITE_STRUCT = [[ local SPRITE_STRUCT = [[
struct { struct {
// TODO: transparent union with vec3_t pos?
int32_t x, y, z; int32_t x, y, z;
]]..bitint_member("UBit16", "cstat")..[[ ]]..bitint_member("UBit16", "cstat")..[[
const int16_t picnum; const int16_t picnum;
@ -597,6 +598,7 @@ ffi.metatype("walltype", walltype_mt)
local spriteext_mt = { local spriteext_mt = {
__index = { __index = {
-- Enable EVENT_ANIMATESPRITES for this sprite. -- Enable EVENT_ANIMATESPRITES for this sprite.
-- XXX: unused?
make_animated = function(sx) make_animated = function(sx)
sx.flags = bor(sx.flags, 16) sx.flags = bor(sx.flags, 16)
end, end,
@ -669,22 +671,40 @@ local function get_sprite_idx(spr)
return i return i
end end
-- Methods that are specific to sprites ---=== Methods that are specific to sprites ===---
function spritetype_mt.__index.setpos(spr, pos) -- setsprite() clone function spritetype_mt.__index.setpos(spr, pos) -- setsprite() clone
spr.x, spr.y, spr.z = pos.x, pos.y, pos.z spr.x, spr.y, spr.z = pos.x, pos.y, pos.z
local newsect = updatesector(spr, spr.sectnum) local newsect = updatesector(spr, spr.sectnum)
if (newsect < 0) then
return -1
end
if (spr.sectnum ~= newsect) then if (newsect >= 0 and spr.sectnum ~= newsect) then
ffiC.changespritesect(get_sprite_idx(spr), newsect) ffiC.changespritesect(get_sprite_idx(spr), newsect)
end end
return newsect
end
---=== Methods that are specific to tsprites ===---
function tspritetype_mt.__index.set_sectnum(tspr, sectnum)
check_sector_idx(sectnum)
ffi.cast(spritetype_ptr_ct, tspr).sectnum = sectnum
end
-- TODO: flags (the same as sprite.UPDATE_FLAGS + "provide own sectnum",
-- e.g. for example because it's already there from "hitscan").
function tspritetype_mt.__index.setpos(tspr, pos)
tspr.x, tspr.y, tspr.z = pos.x, pos.y, pos.z
local newsect = updatesector(tspr, tspr.sectnum)
if (newsect >= 0 and tspr.sectnum ~= newsect) then
tspr:set_sectnum(newsect)
end
return newsect return newsect
end end
-- Methods that are specific to tsprites
function tspritetype_mt.__index.dup(tspr) function tspritetype_mt.__index.dup(tspr)
if (ffiC.spritesortcnt >= ffiC.MAXSPRITESONSCREEN+0ULL) then if (ffiC.spritesortcnt >= ffiC.MAXSPRITESONSCREEN+0ULL) then
return nil return nil
@ -698,9 +718,11 @@ function tspritetype_mt.__index.dup(tspr)
end end
function tspritetype_mt.__index.getspr(tspr) function tspritetype_mt.__index.getspr(tspr)
return sprite[tspr.owner] check_sprite_idx(tspr.owner)
return ffiC.sprite[tspr.owner]
end end
---======---
-- The user of this module can insert additional "spritetype" index -- The user of this module can insert additional "spritetype" index
-- methods and register them with "ffi.metatype". -- methods and register them with "ffi.metatype".
@ -923,7 +945,7 @@ sector = setmtonce({}, sector_mt)
wall = setmtonce({}, wall_mt) wall = setmtonce({}, wall_mt)
sprite = setmtonce({}, sprite_mt) sprite = setmtonce({}, sprite_mt)
spriteext = creategtab(ffiC.spriteext, ffiC.MAXSPRITES, 'spriteext[]') spriteext = creategtab(ffiC.spriteext, ffiC.MAXSPRITES, 'spriteext[]')
atsprite = setmtonce({}, atsprite_mt) _atsprite = setmtonce({}, atsprite_mt)
local function iter_wallsofsec(endwall, w) local function iter_wallsofsec(endwall, w)
w = w+1 w = w+1
@ -1039,7 +1061,7 @@ end
-- returns a hitdata_ct -- returns a hitdata_ct
-- TODO: make v[xyz] be passed as one aggregate, too? -- TODO: make v[xyz] be passed as one aggregate, too?
-- Additionally, permit different coordinates? (ang&horiz, ...) -- TODO: make cliptype optional? What should be the default?
function hitscan(pos, sectnum, vx,vy,vz, cliptype) function hitscan(pos, sectnum, vx,vy,vz, cliptype)
check_sector_idx(sectnum) check_sector_idx(sectnum)
local vec = vec3_ct(pos.x, pos.y, pos.z) local vec = vec3_ct(pos.x, pos.y, pos.z)

View file

@ -182,7 +182,7 @@ local function new_initial_codetab()
"local _xmath = require'xmath'", "local _xmath = require'xmath'",
-- Cache globals into locals. -- Cache globals into locals.
"local sector, sprite, wall, spriteext, atsprite = sector, sprite, wall, spriteext, atsprite", "local sector, sprite, wall, spriteext, _atsprite = sector, sprite, wall, spriteext, _atsprite",
"local actor, player, projectile, g_tile = actor, player, projectile, g_tile", "local actor, player, projectile, g_tile = actor, player, projectile, g_tile",
"local gameactor, gameevent, _gv = gameactor, gameevent, gv", "local gameactor, gameevent, _gv = gameactor, gameevent, gv",
"local updatesector, updatesectorz, cansee = updatesector, updatesectorz, cansee", "local updatesector, updatesectorz, cansee = updatesector, updatesectorz, cansee",

View file

@ -418,8 +418,6 @@ gameactor
action = AC.TROOPSTAND, action = AC.TROOPSTAND,
func = function(i, playeri, dist) func = function(i, playeri, dist)
spriteext[i]:make_animated()
sprite[i].pal = math.random(32) sprite[i].pal = math.random(32)
-- sprite[i].ang = bit.band(sprite[i].ang-20, 2047) -- sprite[i].ang = bit.band(sprite[i].ang-20, 2047)
@ -459,7 +457,36 @@ gameactor
actr.movflagsbits:flip(actor.MOVFLAGS.spin) actr.movflagsbits:flip(actor.MOVFLAGS.spin)
end end
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 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.x, aimv.y, 0, 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)
aimtspr:set_sectnum(hit.sect)
end
end
end,
} }
gameactor gameactor
@ -539,24 +566,6 @@ gameactor
end end
} }
gameevent
{
"ANIMATESPRITES",
function(aci)
local tspr = atsprite[aci]
if (tspr:getspr().picnum==D.LIZTROOP) then
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
end
end
}
local function testbit(num, b) local function testbit(num, b)
return bit.band(num,b)~=0 and 1 or 0 return bit.band(num,b)~=0 and 1 or 0
end end

View file

@ -31,14 +31,8 @@ local starPal = con.actorvar(0)
require("end_gamevars") require("end_gamevars")
local function bangvec(bang) local bangvec = xmath.bangvec
bang = math.floor(bang) local angvec = xmath.angvec
return xmath.vec3(xmath.cosb(bang), xmath.sinb(bang))
end
local function angvec(ang)
return xmath.vec3(math.cos(ang), math.sin(ang))
end
local D = require("CON.DEFS") local D = require("CON.DEFS")

View file

@ -285,6 +285,17 @@ end
---=== MISCELLANEOUS MATH ===--- ---=== MISCELLANEOUS MATH ===---
local intarg = ffi.new("int32_t [1]")
function bangvec(bang)
intarg[0] = bang -- round towards zero
return vec3(cosb(intarg[0]), sinb(intarg[0]))
end
function angvec(ang)
return vec3(cos(ang), sin(ang))
end
local zerovec = vec3() local zerovec = vec3()
-- Point rotation. Note the different order of arguments from engine function. -- Point rotation. Note the different order of arguments from engine function.
-- XXX: passing mixed vec2/vec3 is problematic. Get rid of vec2? -- XXX: passing mixed vec2/vec3 is problematic. Get rid of vec2?