diff --git a/polymer/eduke32/source/lunatic/control.lua b/polymer/eduke32/source/lunatic/control.lua index c6d2451ca..e4043d217 100644 --- a/polymer/eduke32/source/lunatic/control.lua +++ b/polymer/eduke32/source/lunatic/control.lua @@ -11,6 +11,7 @@ local setmetatable = setmetatable local error = error local type = type +local unpack = unpack local player = assert(player) local defs_c = require("defs_common") @@ -73,6 +74,8 @@ local function action_or_move(what, numargs, tab, name, ...) tab[name] = ffi.new("const con_"..what.."_t", lastid[what], args) end +---=== ACTION / MOVE / AI ===--- + function action(name, ...) action_or_move("action", 5, def.action, name, ...) end @@ -124,9 +127,116 @@ end ---=== RUNTIME CON FUNCTIONS ===--- +-- TODO: also check whether sprite exists in the game world (statnum != MAXSTATUS) +local function check_sprite_idx(i) + if (i >= ffiC.MAXSPRITES+0ULL) then + error("invalid argument: must be a valid sprite index", 3) + end +end + +local function check_tile_idx(tilenum) + if (tilenum >= ffiC.MAXTILES+0ULL) then + error("invalid argument: must be a valid tile number", 3) + end +end + +local function krandand(mask) + return bit.band(ffiC.krand(), mask) +end + +-- Lunatic's "insertsprite" is a wrapper around the game "A_InsertSprite", not +-- the engine "insertsprite". +-- +-- Forms: +-- 1. table-call: insertsprite{tilenum, pos, sectnum [, owner [, statnum]] [, key=val...]} +-- valid keys are: owner, statnum, shade, xrepeat, yrepeat, xvel, zvel +-- 2. position-call: insertsprite(tilenum, pos, sectnum [, owner [, statnum]]) +function insertsprite(tab_or_tilenum, ...) + local tilenum, pos, sectnum -- mandatory + -- optional with defaults: + local owner, statnum + local shade, xrepeat, yrepeat, xvel, zvel = 0, 48, 48, 0, 0 + + if (type(tab_or_sectnum)=="table") then + local tab = tab_or_tilenum + tilenum, pos, sectnum = unpack(tab, 1, 3) + owner = tab[4] or tab.owner or -1 + statnum = tab[5] or tab.statnum or 0 + shade = shade and tab.shade + xrepeat = xrepeat and tab.xrepeat + yrepeat = yrepeat and tab.yrepeat + xvel = xvel and tab.xvel + zvel = zvel and tab.zvel + else + tilenum = table_or_tilenum + local args = {...} + pos, sectnum = unpack(args, 1, 2) + owner = args[3] or -1 + statnum = args[4] or 0 + end + + if (type(sectnum)~="number" or type(tilenum) ~= "number") then + error("invalid insertsprite call: 'sectnum' and 'tilenum' must be numbers", 2) + end + check_tile_idx(tilenum) + defs_c.check_sector_idx(sectnum) + if (statnum >= ffiC.MAXSTATUS) then + error("invalid 'statnum' argument to insertsprite: must be a status number (0 .. MAXSTATUS-1)", 2) + end + + return ffiC.A_InsertSprite(sectnum, pos.x, pos.y, pos.z, tilenum, + shade, xrepeat, yrepeat, ang, xvel, zvel, + owner, statnum) +end + +-- INTERNAL USE ONLY. +function _addtodelqueue(spritenum) + check_sprite_idx(spritenum) + ffiC.A_AddToDeleteQueue(spritenum) +end + +-- This corresponds to the first (spawn from parent sprite) form of A_Spawn(). +function spawn(parentspritenum, tilenum, addtodelqueue) + check_sprite_idx(parentspritenum) + check_tile_idx(tilenum) + + if (addtodelqueue and ffiC.g_spriteDeleteQueueSize == 0) then + return -1 + end + + local i = ffiC.A_Spawn(parentspritenum, tilenum) + if (addtodelqueue) then + ffiC.A_AddToDeleteQueue(i) + end + return i +end + +-- This is the second A_Spawn() form. INTERNAL USE ONLY. +function _spawnexisting(spritenum) + check_sprite_idx(spritenum) + return ffiC.A_Spawn(-1, spritenum) +end + +-- A_SpawnMultiple clone +-- ow: parent sprite number +function _spawnmany(ow, tilenum, n) + local spr = sprite[ow] + + for i=n,1, -1 do + local j = insertsprite{ tilenum, spr^(ffiC.krand()%(47*256)), spr.sectnum, ow, 5, + shade=-32, xrepeat=8, yrepeat=8, ang=krandand(2047) } + _spawnexisting(j) + sprite[j].cstat = krandand(8+4) + end +end + +function isenemytile(tilenum) + return (bit.band(ffiC.g_tile[tilenum], ffiC.SFLAG_BADGUY)~=0) +end + function rotatesprite(x, y, zoom, ang, tilenum, shade, pal, orientation, cx1, cy1, cx2, cy2) - if (type(tilenum) ~= "number" or not (tilenum >= 0 and tilenum < ffiC.MAXTILES)) then + if (type(tilenum) ~= "number" or tilenum >= ffiC.MAXTILES+0ULL) then error("bad argument #5 to rotatesprite: must be number in [0.."..ffiC.MAXTILES.."]", 2) end @@ -187,17 +297,68 @@ local D = { HEATSENSOR = 59, BOOTS = 61, HOLODUKE = 1348, + + GLASSPIECES = 1031, + COMMANDER = 1920, + JIBS2 = 2250, + SCRAP1 = 2400, + BLIMP = 3400, } -local function check_sprite_idx(i) - if (i >= ffiC.MAXSPRITES+0ULL) then - error("invalid argument: must be a valid sprite index", 3) +function _A_DoGuts(i, gutstile, n) + check_tile_idx(gutstile) + local spr = sprite[i] + local smallguts = spr.xrepeat < 16 and spr:isenemy() + local xsz = smallguts and 8 or 32 + local ysz = xsz + local z = math.min(spr, sector[spr.sectnum]:floorzat(spr)) - 8*256 + + if (spr.picnum == D.COMMANDER) then + z = z - (24*256) + end + + for i=n,1, -1 do + local pos = geom.vec3(spr.x+krandand(255)-128, spr.y+krandand(255)-128, z-krandand(8191)) + local j = insertsprite{ gutstile, pos, spr.sectnum, i, 5, shade=-32, xrepeat=xsz, yrepeat=ysz, + ang=krandand(2047), xvel=48+krandand(31), zvel=-512-krandand(2047) } + local newspr = sprite[j] + if (newspr.picnum==D.JIBS2) then + -- This looks silly, but EVENT_EGS code could have changed the size + -- between the insertion and here. + newspr.xrepeat = newspr.xrepeat/4 + newspr.yrepeat = newspr.yrepeat/4 + end + newspr.pal = spr.pal end end -local function check_tile_idx(tilenum) - if (tilenum >= ffiC.MAXTILES+0ULL) then - error("invalid argument: must be a valid tile number", 3) +function _debris(i, dtile, n) + local spr = sprite[i] + if (spr.sectnum >= ffiC.numsectors+0ULL) then + return + end + + for j=n-1,0, -1 do + local isblimpscrap = (spr.picnum==D.BLIMP and dtile==D.SCRAP1) + local picofs = isblimpscrap and 0 or krandand(3) + local pos = spr + geom.vec3(krandand(255)-128, krandand(255)-128, -(8*256)-krandand(8191)) + local jj = insertsprite{ dtile+picofs, pos, spr.sectnum, i, 5, + shade=spr.shade, xrepeat=32+krandand(15), yrepeat=32+krandand(15), + ang=krandand(2047), xvel=32+krandand(127), zvel=-krandand(2047) } + -- NOTE: BlimpSpawnSprites[14] (its array size if 15) will never be chosen + sprite[jj].yvel = isblimpscrap and ffiC.BlimpSpawnSprites[math.mod(jj, 14)] or -1 + sprite[jj].pal = spr.pal + end +end + +function _A_SpawnGlass(i, n) + local spr = sprite[i] + + for j=n,1, -1 do + local k = insertsprite{ D.GLASSPIECES+n%3, spr^(256*krandand(16)), spr.sectnum, i, 5, + shade=krandand(15), xrepeat=36, yrepeat=36, ang=krandand(2047), + xvel=32+krandand(63), zvel=-512-krandand(2047) } + sprite[k].pal = spr.pal end end @@ -403,10 +564,6 @@ function _awayfromwall(spr, d) return true end -local function krandand(mask) - return bit.band(ffiC.krand(), mask) -end - local BANG2RAD = math.pi/1024 local function cossinb(bang) diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua index dc274f5a6..b8a9793a1 100644 --- a/polymer/eduke32/source/lunatic/defs.ilua +++ b/polymer/eduke32/source/lunatic/defs.ilua @@ -454,6 +454,8 @@ playerdata_t g_player[MAXPLAYERS]; const int32_t playerswhenstarted; int32_t lastvisinc; +int16_t g_spriteDeleteQueueSize; +int16_t BlimpSpawnSprites[15]; int32_t A_IncurDamage(int32_t sn); // not bound-checked! void P_AddWeaponMaybeSwitch(DukePlayer_t *ps, int32_t weap); @@ -465,6 +467,10 @@ int32_t VM_ResetPlayer2(int32_t snum); void A_RadiusDamage(int32_t i, int32_t r, int32_t, int32_t, int32_t, int32_t); void G_OperateSectors(int32_t sn, int32_t ii); int32_t A_Dodge(spritetype *s); +int32_t A_InsertSprite(int32_t whatsect,int32_t s_x,int32_t s_y,int32_t s_z,int32_t s_pn,int32_t s_s, + int32_t s_xr,int32_t s_yr,int32_t s_a,int32_t s_ve,int32_t s_zv,int32_t s_ow,int32_t s_ss); +int32_t A_Spawn(int32_t j, int32_t pn); +void A_AddToDeleteQueue(int32_t i); ]] -- functions diff --git a/polymer/eduke32/source/lunatic/defs_common.lua b/polymer/eduke32/source/lunatic/defs_common.lua index 092e2489c..ac4f9e025 100644 --- a/polymer/eduke32/source/lunatic/defs_common.lua +++ b/polymer/eduke32/source/lunatic/defs_common.lua @@ -209,6 +209,11 @@ const int16_t headsectbunch[2][MAXBUNCHES], nextsectbunch[2][MAXSECTORS]; int16_t yax_getbunch(int16_t i, int16_t cf); +int32_t getceilzofslopeptr(const sectortype *sec, int32_t dax, int32_t day); +int32_t getflorzofslopeptr(const sectortype *sec, int32_t dax, int32_t day); +void getzsofslopeptr(const sectortype *sec, int32_t dax, int32_t day, + int32_t *ceilz, int32_t *florz); + int32_t hitscan(const vec3_t *sv, int16_t sectnum, int32_t vx, int32_t vy, int32_t vz, hitdata_t *hitinfo, uint32_t cliptype); int32_t cansee(int32_t x1, int32_t y1, int32_t z1, int16_t sect1, @@ -248,6 +253,19 @@ local ivec3_mt = { } ivec3_ = ffi.metatype("vec3_t", ivec3_mt) +local sectortype_mt = { + __index = { + ceilingzat = function(s, pos) + return ffiC.getceilzofslope(s, pos.x, pos.y) + end, + + floorzat = function(s, pos) + return ffiC.getflorzofslope(s, pos.x, pos.y) + end, + } +} +ffi.metatype("sectortype", sectortype_mt) + local walltype_mt = { __index = { isblocking = function(w) @@ -356,7 +374,7 @@ nextspritestat = creategtab(ffiC.nextspritestat, ffiC.MAXSPRITES, 'nextspritesta prevspritesect = creategtab(ffiC.prevspritesect, ffiC.MAXSPRITES, 'prevspritesect[]') prevspritestat = creategtab(ffiC.prevspritestat, ffiC.MAXSPRITES, 'prevspritestat[]') -local function check_sector_idx(sectnum) +function check_sector_idx(sectnum) if (sectnum >= ffiC.numsectors+0ULL) then error("passed out-of-bounds sector number "..sectnum, 3) end diff --git a/polymer/eduke32/source/lunatic/dynsymlist b/polymer/eduke32/source/lunatic/dynsymlist index 747ec660c..a10dd75f5 100644 --- a/polymer/eduke32/source/lunatic/dynsymlist +++ b/polymer/eduke32/source/lunatic/dynsymlist @@ -17,6 +17,10 @@ ydim; yax_getbunch; +getceilzofslopeptr; +getflorzofslopeptr; +getzsofslopeptr; + headspritesect; headspritestat; prevspritesect; @@ -63,6 +67,8 @@ g_player; playerswhenstarted; lastvisinc; +g_spriteDeleteQueueSize; +BlimpSpawnSprites; luaJIT_BC_lunacon; luaJIT_BC_con_lang; @@ -86,4 +92,7 @@ VM_ResetPlayer2; A_RadiusDamage; G_OperateSectors; A_Dodge; +A_InsertSprite; +A_Spawn; +A_AddToDeleteQueue; }; diff --git a/polymer/eduke32/source/lunatic/dynsymlist_m32 b/polymer/eduke32/source/lunatic/dynsymlist_m32 index 286d020c1..6bc366745 100644 --- a/polymer/eduke32/source/lunatic/dynsymlist_m32 +++ b/polymer/eduke32/source/lunatic/dynsymlist_m32 @@ -17,6 +17,10 @@ ydim; yax_getbunch; +getceilzofslopeptr; +getflorzofslopeptr; +getzsofslopeptr; + headspritesect; headspritestat; prevspritesect; diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index 46a78ac79..e2d408357 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -19,6 +19,7 @@ local print = print local tonumber = tonumber local tostring = tostring local type = type +local unpack = unpack if (string.dump) then require("strict") @@ -119,10 +120,7 @@ local function on_actor_end(usertype, tsamm, codetab) str = str .. tostring(tsamm[i]).."," end if (#tsamm==5) then - local flags = 0 - for i=5,#tsamm do - flags = bit.bor(flags, tsamm[i]) - end + local flags = bit.bor(unpack(tsamm, 5)) str = str .. flags.."," end @@ -690,7 +688,7 @@ local varvarop = cmd(W,R) -- Allow nesting... stuff like -- ifvarl actorvar[sprite[THISACTOR].owner].burning 0 -- is kinda breaking the classic "no array nesting" rules --- (if there ever were any) and making our life harder else. +-- (if there ever were any) but making our life harder else. local arraypat = sp0 * "[" * sp0 * t_rvar * sp0 * "]" -- Have to bite the bullet here and list actor/player members with second parameters, @@ -887,11 +885,11 @@ local Ci = { addweapon = cmd(D,D) -- NLCF / format("if (%s) then _con.longjmp() end", PLS":addweapon(%1,%2)"), debris = cmd(D,D) - / "", -- TODO + / "_con._debris(_aci, %1, %2)", addinventory = cmd(D,D) / PLS":addinventory(%1,%2)", guts = cmd(D,D) - / "", -- TODO + / "_con._A_DoGuts(_aci,%1,%2)", -- cont'd addkills = cmd(D) @@ -908,14 +906,16 @@ local Ci = { espawn = cmd(D), globalsound = cmd(D) / "", - lotsofglass = cmd(D), + lotsofglass = cmd(D) + / "_con._A_SpawnGlass(_aci,%1)", mail = cmd(D) - / "", -- TODO + / "_con._spawnmany(_aci,4410,%1)", -- TODO: dyntile money = cmd(D) - / "", -- TODO + / "_con._spawnmany(_aci,1233,%1)", -- TODO: dyntile paper = cmd(D) - / "", -- TODO - qspawn = cmd(D), + / "_con._spawnmany(_aci,4460,%1)", -- TODO: dyntile + qspawn = cmd(D) + / "_con.spawn(_aci,%1,true)", quote = cmd(D) / "", -- TODO savenn = cmd(D), @@ -925,7 +925,8 @@ local Ci = { soundonce = cmd(D), sound = cmd(D) / "", -- TODO: all things audio... - spawn = cmd(D), + spawn = cmd(D) + / "_con.spawn(_aci, %1)", stopsound = cmd(D) / "", @@ -943,7 +944,8 @@ local Ci = { / format("_con._flash(%s,%s)", ACS"", SPS""), getlastpal = cmd() / "_con._getlastpal(_aci)", - insertspriteq = cmd(), + insertspriteq = cmd() + / "_con._addtodelqueue(_aci)", killit = cmd() -- NLCF / "_con.killit()", mikesnd = cmd() @@ -1483,8 +1485,8 @@ local Grammar = Pat{ -- Some often-used terminals follow. These appear here because we're -- hitting a limit with LPeg else. - -- http://lua-users.org/lists/lua-l/2008-11/msg00462.html + -- NOTE: NW demo (NWSNOW.CON) contains a Ctrl-Z char (decimal 26) whitespace = Set(" \t\r\26") + newline + Set("(),;") + comment + linecomment, diff --git a/polymer/eduke32/source/lunatic/test.elua b/polymer/eduke32/source/lunatic/test.elua index 3aa0b6241..ce86dd093 100644 --- a/polymer/eduke32/source/lunatic/test.elua +++ b/polymer/eduke32/source/lunatic/test.elua @@ -303,6 +303,11 @@ require("test/test_rotspr") print('---=== END TEST SCRIPT ===---') +function check_sector_idx() + error("bla") +end +spritesofsect(0) + -- This will complain about wrong usage of 'error'. In particular, -- the nil must not propagate to C! error(nil)