diff --git a/polymer/eduke32/source/game.c b/polymer/eduke32/source/game.c index a66829735..a1924b052 100644 --- a/polymer/eduke32/source/game.c +++ b/polymer/eduke32/source/game.c @@ -7051,6 +7051,7 @@ static void G_DoEventAnimSprites(int32_t j) if ((unsigned)ow < MAXSPRITES && spriteext[ow].flags & SPREXT_TSPRACCESS) { spriteext[ow].tspr = &tsprite[j]; + // XXX: wouldn't screenpeek be more meaningful as current player? VM_OnEvent(EVENT_ANIMATESPRITES, ow, myconnectindex, -1, 0); spriteext[ow].tspr = NULL; } @@ -7884,6 +7885,11 @@ skip: for (j = spritesortcnt-1; j>=0; j--) G_DoEventAnimSprites(j); } + +#ifdef LUNATIC + if (G_HaveEvent(EVENT_ANIMATEALLSPRITES)) + VM_OnEvent(EVENT_ANIMATEALLSPRITES, -1, -1, -1, 0); +#endif } diff --git a/polymer/eduke32/source/gamedef.c b/polymer/eduke32/source/gamedef.c index 5c27d5871..ffd72a11e 100644 --- a/polymer/eduke32/source/gamedef.c +++ b/polymer/eduke32/source/gamedef.c @@ -694,7 +694,10 @@ const char *EventNames[MAXEVENTS] = "EVENT_LOADGAME", "EVENT_SAVEGAME", "EVENT_PREGAME", - "EVENT_CHANGEMENU" + "EVENT_CHANGEMENU", +#ifdef LUNATIC + "EVENT_ANIMATEALLSPRITES", +#endif }; #if !defined LUNATIC diff --git a/polymer/eduke32/source/gameexec.h b/polymer/eduke32/source/gameexec.h index 6e1f48c6e..9d750e539 100644 --- a/polymer/eduke32/source/gameexec.h +++ b/polymer/eduke32/source/gameexec.h @@ -125,6 +125,9 @@ enum GameEvent_t { EVENT_SAVEGAME, EVENT_PREGAME, EVENT_CHANGEMENU, +#ifdef LUNATIC + EVENT_ANIMATEALLSPRITES, // 95 +#endif MAXEVENTS }; diff --git a/polymer/eduke32/source/lunatic/con_lang.lua b/polymer/eduke32/source/lunatic/con_lang.lua index a566e9c04..da0972c86 100644 --- a/polymer/eduke32/source/lunatic/con_lang.lua +++ b/polymer/eduke32/source/lunatic/con_lang.lua @@ -168,6 +168,7 @@ EVENT = { EVENT_SAVEGAME = 92, EVENT_PREGAME = 93, EVENT_CHANGEMENU = 94, +-- EVENT_ANIMATEALLSPRITES = 95, -- internal } -- 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 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 SX = function(memb) return "spriteext[%s]"..memb end diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua index c752aa5fb..377e42398 100644 --- a/polymer/eduke32/source/lunatic/defs.ilua +++ b/polymer/eduke32/source/lunatic/defs.ilua @@ -1928,8 +1928,31 @@ local g_firstRun = (ffiC.g_elCONSize == 0) -- Actor functions, saved for actor chaining 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 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} -- Every arg may be positional OR key=val (with the name indicated above as key), -- but not both. @@ -2018,6 +2041,24 @@ local function our_gameactor(args) 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! -- XXX: Need to see if some other state living on the C side is clobbered -- by Lua state recreation. @@ -2040,10 +2081,7 @@ local function our_gameactor(args) actor_funcs[tilenum] = newfunc end --- Event functions, saved for event chaining -local event_funcs = {} -local gameevent_internal = gameevent_internal -- included in lunatic.c -- gameevent{ [, flags], } local function our_gameevent(args) 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 defs_c.create_globals(_G) - -- REMOVE this for release DBG_ = {} DBG_.debug = require("debug") diff --git a/polymer/eduke32/source/lunatic/defs_common.lua b/polymer/eduke32/source/lunatic/defs_common.lua index cd751b140..d2d8f54fe 100644 --- a/polymer/eduke32/source/lunatic/defs_common.lua +++ b/polymer/eduke32/source/lunatic/defs_common.lua @@ -155,6 +155,7 @@ string.format([[ local SPRITE_STRUCT = [[ struct { + // TODO: transparent union with vec3_t pos? int32_t x, y, z; ]]..bitint_member("UBit16", "cstat")..[[ const int16_t picnum; @@ -597,6 +598,7 @@ ffi.metatype("walltype", walltype_mt) local spriteext_mt = { __index = { -- Enable EVENT_ANIMATESPRITES for this sprite. + -- XXX: unused? make_animated = function(sx) sx.flags = bor(sx.flags, 16) end, @@ -669,22 +671,40 @@ local function get_sprite_idx(spr) return i end --- Methods that are specific to sprites +---=== Methods that are specific to sprites ===--- + function spritetype_mt.__index.setpos(spr, pos) -- setsprite() clone spr.x, spr.y, spr.z = pos.x, pos.y, pos.z - local newsect = updatesector(spr, spr.sectnum) - if (newsect < 0) then - return -1 - end - if (spr.sectnum ~= newsect) then + if (newsect >= 0 and spr.sectnum ~= newsect) then ffiC.changespritesect(get_sprite_idx(spr), newsect) end + + return newsect +end + + +---=== Methods that are specific to tsprites ===--- + +function tspritetype_mt.__index.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 end --- Methods that are specific to tsprites function tspritetype_mt.__index.dup(tspr) if (ffiC.spritesortcnt >= ffiC.MAXSPRITESONSCREEN+0ULL) then return nil @@ -698,9 +718,11 @@ function tspritetype_mt.__index.dup(tspr) end function tspritetype_mt.__index.getspr(tspr) - return sprite[tspr.owner] + check_sprite_idx(tspr.owner) + return ffiC.sprite[tspr.owner] end +---======--- -- The user of this module can insert additional "spritetype" index -- methods and register them with "ffi.metatype". @@ -923,7 +945,7 @@ sector = setmtonce({}, sector_mt) wall = setmtonce({}, wall_mt) sprite = setmtonce({}, sprite_mt) spriteext = creategtab(ffiC.spriteext, ffiC.MAXSPRITES, 'spriteext[]') -atsprite = setmtonce({}, atsprite_mt) +_atsprite = setmtonce({}, atsprite_mt) local function iter_wallsofsec(endwall, w) w = w+1 @@ -1039,7 +1061,7 @@ end -- returns a hitdata_ct -- 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) check_sector_idx(sectnum) local vec = vec3_ct(pos.x, pos.y, pos.z) diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index 13c48c932..0176ed1f1 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -182,7 +182,7 @@ local function new_initial_codetab() "local _xmath = require'xmath'", -- 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 gameactor, gameevent, _gv = gameactor, gameevent, gv", "local updatesector, updatesectorz, cansee = updatesector, updatesectorz, cansee", diff --git a/polymer/eduke32/source/lunatic/test.elua b/polymer/eduke32/source/lunatic/test.elua index f91bdba35..34d794090 100644 --- a/polymer/eduke32/source/lunatic/test.elua +++ b/polymer/eduke32/source/lunatic/test.elua @@ -418,10 +418,8 @@ gameactor action = AC.TROOPSTAND, func = function(i, playeri, dist) - spriteext[i]:make_animated() - 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) local spr = sprite[i] @@ -459,7 +457,36 @@ gameactor actr.movflagsbits:flip(actor.MOVFLAGS.spin) 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 + + -- 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 @@ -539,24 +566,6 @@ gameactor 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) return bit.band(num,b)~=0 and 1 or 0 end diff --git a/polymer/eduke32/source/lunatic/test/helixspawner.lua b/polymer/eduke32/source/lunatic/test/helixspawner.lua index 6f1499e7c..35f3bf8d7 100644 --- a/polymer/eduke32/source/lunatic/test/helixspawner.lua +++ b/polymer/eduke32/source/lunatic/test/helixspawner.lua @@ -31,14 +31,8 @@ local starPal = con.actorvar(0) require("end_gamevars") -local function bangvec(bang) - bang = math.floor(bang) - 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 bangvec = xmath.bangvec +local angvec = xmath.angvec local D = require("CON.DEFS") diff --git a/polymer/eduke32/source/lunatic/xmath.lua b/polymer/eduke32/source/lunatic/xmath.lua index 499e323e8..1b6181bf1 100644 --- a/polymer/eduke32/source/lunatic/xmath.lua +++ b/polymer/eduke32/source/lunatic/xmath.lua @@ -285,6 +285,17 @@ end ---=== 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() -- Point rotation. Note the different order of arguments from engine function. -- XXX: passing mixed vec2/vec3 is problematic. Get rid of vec2?