From 1588a80442f11ec8de91d4ee510d668b435c7e1a Mon Sep 17 00:00:00 2001 From: helixhorned Date: Sun, 9 Dec 2012 13:24:39 +0000 Subject: [PATCH] Lunatic: drowning in code. git-svn-id: https://svn.eduke32.com/eduke32@3259 1a8010ca-5511-0410-912e-c29ae57300e0 --- polymer/eduke32/source/game.c | 6 +- polymer/eduke32/source/lunatic/con_lang.lua | 2 - polymer/eduke32/source/lunatic/control.lua | 253 +++++++++++++++++- polymer/eduke32/source/lunatic/defs.ilua | 43 ++- .../eduke32/source/lunatic/defs_common.lua | 27 +- polymer/eduke32/source/lunatic/defs_m32.ilua | 1 + polymer/eduke32/source/lunatic/dynsymlist | 1 + polymer/eduke32/source/lunatic/lunacon.lua | 39 +-- 8 files changed, 340 insertions(+), 32 deletions(-) diff --git a/polymer/eduke32/source/game.c b/polymer/eduke32/source/game.c index 81a60c612..1d4ea6b98 100644 --- a/polymer/eduke32/source/game.c +++ b/polymer/eduke32/source/game.c @@ -9910,10 +9910,12 @@ bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice); #ifdef LUNATIC const char *g_sizes_of_what[] = { "sectortype", "walltype", "spritetype", - "actor_t", "DukePlayer_t", "playerdata_t", "user_defs" }; + "actor_t", "DukePlayer_t", "playerdata_t", + "user_defs", "tiledata_t" }; int32_t g_sizes_of[] = { sizeof(sectortype), sizeof(walltype), sizeof(spritetype), - sizeof(actor_t), sizeof(DukePlayer_t), sizeof(playerdata_t), sizeof(user_defs) }; + sizeof(actor_t), sizeof(DukePlayer_t), sizeof(playerdata_t), + sizeof(user_defs), sizeof(tiledata_t) }; #endif int32_t app_main(int32_t argc, const char **argv) diff --git a/polymer/eduke32/source/lunatic/con_lang.lua b/polymer/eduke32/source/lunatic/con_lang.lua index c5de2a602..0a86477c8 100644 --- a/polymer/eduke32/source/lunatic/con_lang.lua +++ b/polymer/eduke32/source/lunatic/con_lang.lua @@ -19,8 +19,6 @@ MAXSKILLS = 7 MAXSOUNDS = 4096 -SLEEPTIME = 1536 - -- KEEPINSYNC with gamedef.c:C_AddDefaultDefinitions() and the respective -- defines. These are exported to the ffi.C namespace and as literal defines diff --git a/polymer/eduke32/source/lunatic/control.lua b/polymer/eduke32/source/lunatic/control.lua index c465c7946..8fb1ef015 100644 --- a/polymer/eduke32/source/lunatic/control.lua +++ b/polymer/eduke32/source/lunatic/control.lua @@ -171,6 +171,20 @@ end --- expose the functionality in a better fashion than merely giving access to --- the C functions.) +local D = { + -- TODO: dynamic tile remapping + ACTIVATOR = 2, + APLAYER = 1405, + + FIRSTAID = 53, + STEROIDS = 55, + AIRTANK = 56, + JETPACK = 57, + HEATSENSOR = 59, + BOOTS = 61, + HOLODUKE = 1348, +} + local function check_sprite_idx(i) if (i >= ffiC.MAXSPRITES+0ULL) then error("invalid argument: must be a valid sprite index", 3) @@ -186,7 +200,6 @@ end function _A_Shoot(i, atwith) check_sprite_idx(i) check_tile_idx(atwith) - return ffiC.A_Shoot(i, atwith) end @@ -235,6 +248,13 @@ function _pstomp(ps, i) end end +function _pkick(ps, spr) + -- TODO: multiplayer branch + if (spr.picnum~=D.APLAYER and ps.quick_kick==0) then + ps.quick_kick = 14 + end +end + function _VM_ResetPlayer2(snum) local bound_check = player[snum] return (ffiC.VM_ResetPlayer2(snum)~=0) @@ -272,10 +292,10 @@ end -- For GET_ACCESS: returns logical: whether player has card given by PAL -- Else: returns inventory amount -function _getinventory(ps, inv, pal) +function _getinventory(ps, inv, i) if (inv == ffiC.GET_ACCESS) then - if (PALBITS[pal]) then - return (bit.band(ps.got_access, PALBITS[pal])~=0) + if (PALBITS[sprite[i].pal]) then + return (bit.band(ps.got_access, PALBITS[sprite[i].pal])~=0) end return false else @@ -345,9 +365,7 @@ function _operate(spritenum) if (lotag==23 or sect.floorz==sect.ceilingz) then if (bit.band(lotag, 32768+16384) == 0) then for j in spritesofsect(tag.sector) do - if (sprite[j].picnum==2) then - -- TODO: ^^^ actually ACTIVATOR. Make the - -- dynamic name->tilenum mappings work. + if (sprite[j].picnum==D.ACTIVATOR) then return end end @@ -381,15 +399,144 @@ 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) + -- XXX: better use the precalc'd arrays instead? + local ang = BANG2RAD*(bang) + return 16384*math.cos(ang), 16384*math.sin(ang) +end + +local function manhatdist(v1, v2) + return math.abs(v1.x-v2.x) + math.abs(v1.y-v2.y) +end + +-- "otherspr" is either player or holoduke sprite +local function A_GetFurthestVisiblePoint(aci, otherspr) + if (bit.band(actor[aci].get_t_data(0), 63) ~= 0) then + return + end + + local angincs = (ud.player_skill < 3) and 1024 or 2048/(1+krandand(1)) + + local j = 0 + repeat + local c, s = cossinb(otherspr.ang + j) + local hit = hitscan(otherspr^(16*256), otherspr.sectnum, + c, s, 16384-krandand(32767), ffiC.CLIPMASK1) + local dother = manhatdist(hit.pos, otherspr) + local dactor = manhatdist(hit.pos, spr) + + if (dother < dactor and hit.sect >= 0) then + if (cansee(hit.pos, hit.sect, otherspr^(16*256), otherspr.sectnum)) then + return hit + end + end + + j = j + (angincs - krandand(511)) + until (j >= 2048) +end + +local SLEEPTIME = 1536 + +function _cansee(aci, ps) + -- Select sprite for monster to target. + local spr = sprite[aci] + local s = sprite[ps.i] + + if (ps.holoduke_on) then + -- If holoduke is on, let them target holoduke first. + local hs = sprite[ps.holoduke_on] + + if (cansee(spr^krandand(8191), spr.sectnum, s, s.sectnum)) then + s = hs + end + end + + -- Can they see player (or player's holoduke)? + local can = cansee(spr^krandand(47*256), spr.sectnum, s^(24*256), s.sectnum) + + if (not can) then + -- Search around for target player. + local hit = A_GetFurthestVisiblePoint(aci, s) + if (hit ~= nil) then + can = true + actor[aci].lastvx = hit.pos.x + actor[aci].lastvy = hit.pos.y + end + else + -- Else, they did see it. Save where we were looking... + actor[aci].lastvx = s.x + actor[aci].lastvy = s.y + end + + if (can and (spr.statnum==ffiC.STAT_ACTOR or spr.statnum==ffiC.STAT_STANDABLE)) then + actor[aci].timetosleep = SLEEPTIME + end + + return can +end + function _canseetarget(spr, ps) -- NOTE: &41 ? - return cansee(spr^(bit.band(ffiC.krand(),41)), spr.sectnum, + return cansee(spr^krandand(41), spr.sectnum, ps.pos, sprite[ps.i].sectnum) end +local function A_CheckHitSprite(spr, angadd) + local zoff = (spr:isenemy() and 42*256) or (spr.picnum==D.APLAYER and 39*256) or 0 + + local c, s = cossinb(spr.ang+angadd) + local hit = hitscan(spr^zoff, spr.sectnum, c, s, 0, ffiC.CLIPMASK1) + if (hit.wall >= 0 and wall[hit.wall]:ismasked() and spr:isenemy()) then + return -1, nil + end + + local dx = hit.pos.x-spr.x + local dy = hit.pos.y-spr.y + return hit.sprite, math.sqrt(dx*dx+dy*dy) -- TODO: use "ldist" approximation for authenticity +end + +function _canshoottarget(dist, aci) + if (dist > 1024) then + local spr = sprite[aci] + + local hitspr, hitdist = A_CheckHitSprite(spr, 0) + if (hitdist == nil) then + return true + end + + local bigenemy = (spr:isenemy() and spr.xrepeat > 56) + + local sclip = bigenemy and 3084 or 768 + local angdif = bigenemy and 48 or 16 + + local sclips = { sclip, sclip, 768 } + local angdifs = { 0, angdif, -angdif } + + for i=1,3 do + if (i > 1) then + hitspr, hitdist = A_CheckHitSprite(aci, angdifs[i]) + end + + if (hitspr >= 0 and sprite[hitspr].picnum == spr.picnum) then + if (hitdist > sclips[i]) then + return false + end + end + end + end + + return true +end + function _getlastpal(spritenum) local spr = sprite[spritenum] - if (spr.picnum == 1405) then -- TODO: APLAYER + if (spr.picnum == D.APLAYER) then spr.pal = player[spr.yvel].palookup else if (spr.pal == 1 and spr.extra == 0) then -- hack for frozen @@ -415,6 +562,94 @@ function _angdiffabs(a1, a2) return math.abs(a2-a1) end +local SK = { + CROUCH = 1, + RUN = 5, +} + +local function _ifp(flags, pli, aci) + local l = flags + local ps = player[pli] + local vel = sprite[ps.i].xvel + local band = bit.band + local j = false + + if (band(l,8)~=0 and ps.on_ground and _testkey(pli, SK.CROUCH)) then + j = true + elseif (band(l,16)~=0 and ps.jumping_counter == 0 and not ps.on_ground and ps.vel.z > 2048) then + j = true + elseif (band(l,32)~=0 and ps.jumping_counter > 348) then + j = true + elseif (band(l,1)~=0 and vel >= 0 and vel < 8) then + j = true + elseif (band(l,2)~=0 and vel >= 8 and not _testkey(pli, SK.RUN)) then + j = true + elseif (band(l,4)~=0 and vel >= 8 and _testkey(pli, SK.RUN)) then + j = true + elseif (band(l,64)~=0 and ps.pos.z < (sprite[_aci].z-(48*256))) then + j = true + elseif (band(l,128)~=0 and vel <= -8 and not _testkey(pli, SK.RUN)) then + j = true + elseif (band(l,256)~=0 and vel <= -8 and _testkey(pli, SK.RUN)) then + j = true + elseif (band(l,512)~=0 and (ps.quick_kick > 0 or (ps.curr_weapon == ffiC.KNEE_WEAPON and ps.kickback_pic > 0))) then + j = true + elseif (band(l,1024)~=0 and sprite[ps.i].xrepeat < 32) then + j = true + elseif (band(l,2048)~=0 and ps.jetpack_on) then + j = true + elseif (band(l,4096)~=0 and ps:get_inv_amount(ffiC.GET_STEROIDS) > 0 and ps:get_inv_amount(ffiC.GET_STEROIDS) < 400) then + j = true + elseif (band(l,8192)~=0 and ps.on_ground) then + j = true + elseif (band(l,16384)~=0 and sprite[ps.i].xrepeat > 32 and sprite[ps.i].extra > 0 and ps.timebeforeexit == 0) then + j = true + elseif (band(l,32768)~=0 and sprite[ps.i].extra <= 0) then + j = true + elseif (band(l,65536)~=0) then + -- TODO: multiplayer branch + if (_angdiffabs(ps.ang, ffiC.getangle(sprite[_aci].x-ps.pos.x, sprite[_aci].y-ps.pos.y)) < 128) then + j = true + end + end + + return j +end + +function _checkspace(sectnum, floorp) + local sect = sector[sectnum] + local picnum = floorp and sect.floorpicnum or sect.ceilingpicnum + local stat = floorp and sect.floorstat or sect.ceilingstat + return bit.band(stat,1)~=0 and sect.ceilingpal == 0 and + (picnum==D.MOONSKY1 or picnum==D.BIGORBIT1) +end + +function _flash(spr, ps) + spr.shade = -127 + ps.visibility = -127 + ffiC.lastvisinc = ffiC.totalclock+32 +end + +local INVENTILE = { + [D.FIRSTAID] = true, + [D.STEROIDS] = true, + [D.AIRTANK] = true, + [D.JETPACK] = true, + [D.HEATSENSOR] = true, + [D.BOOTS] = true, + [D.HOLODUKE] = true, +} + +function _checkrespawn(spr) + if (spr:isenemy()) then + return (ud.respawn_monsters~=0) + end + if (INVENTILE[spr.picnum]) then + return (ud.respawn_inventory~=0) + end + return (ud.respawn_items~=0) +end + --- Exported functions --- diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua index efeaebfdb..97d9d053e 100644 --- a/polymer/eduke32/source/lunatic/defs.ilua +++ b/polymer/eduke32/source/lunatic/defs.ilua @@ -305,6 +305,32 @@ typedef struct { char user_name[32]; uint32_t revision; } playerdata_t; + +typedef struct { + int32_t workslike, cstat; // 8b + int32_t hitradius, range, flashcolor; // 12b + int16_t spawns, sound, isound, vel; // 8b + int16_t decal, trail, tnum, drop; // 8b + int16_t offset, bounces, bsound; // 6b + int16_t toffset; // 2b + int16_t extra, extra_rand; // 4b + int8_t sxrepeat, syrepeat, txrepeat, tyrepeat; // 4b + int8_t shade, xrepeat, yrepeat, pal; // 4b + int8_t movecnt; // 1b + uint8_t clipdist; // 1b + int8_t filler[6]; // 6b +} projectile_t; + +typedef struct { + intptr_t *execPtr; + intptr_t *loadPtr; + + uint32_t flags; + + int16_t cacherange; + + projectile_t defproj; +} tiledata_t; #pragma pack(pop) enum @@ -425,6 +451,7 @@ user_defs ud; playerdata_t g_player[MAXPLAYERS]; const int32_t playerswhenstarted; +int32_t lastvisinc; int32_t A_IncurDamage(int32_t sn); // not bound-checked! void P_AddWeaponMaybeSwitch(DukePlayer_t *ps, int32_t weap); @@ -455,7 +482,7 @@ string.dump = nil -- sanity-check struct type sizes local good = true -for i=0,6 do +for i=0,7 do local what = ffi.string(ffiC.g_sizes_of_what[i]) local fsz = ffi.sizeof(what) local csz = ffiC.g_sizes_of[i] @@ -469,6 +496,12 @@ if (not good) then error("Some sizes don't match between C and LuaJIT/FFI.") end +-- Add game-side metamethods to "spritetype" and register it with "metatype" +defs_c.spritetype_mt.__index.isenemy = function(s) + return (bit.band(ffiC.g_tile[s.picnum], ffiC.SFLAG_BADGUY)~=0) +end +ffi.metatype("spritetype", defs_c.spritetype_mt) + -- "player" global, needed by the "control" module local tmpmt = { __index = function(tab, key) @@ -631,6 +664,14 @@ local actor_mt = { return (a.t_data[5]==ai) end end, + + -- Getters/setters. + get_t_data = function(a, idx) + if (idx >= 10ULL) then + error("Invalid t_data index "..idx, 2) + end + return ffi.cast(actor_ptr_ct, a).t_data[idx] + end, }, } ffi.metatype("actor_t", actor_mt) diff --git a/polymer/eduke32/source/lunatic/defs_common.lua b/polymer/eduke32/source/lunatic/defs_common.lua index a95980df1..a9fdcf560 100644 --- a/polymer/eduke32/source/lunatic/defs_common.lua +++ b/polymer/eduke32/source/lunatic/defs_common.lua @@ -6,6 +6,7 @@ local ffi = require("ffi") local ffiC = ffi.C +local bit = require("bit") local string = require("string") local error = error @@ -236,9 +237,30 @@ local ivec3_mt = { } ivec3_ = ffi.metatype("vec3_t", ivec3_mt) +local walltype_mt = { + __index = { + isblocking = function(w) + return (bit.band(w.cstat, 1)~=0) + end, + + ismasked = function(w) + return (bit.band(w.cstat, 16)~=0) + end, + + isoneway = function(w) + return (bit.band(w.cstat, 32)~=0) + end, + + ishittable = function(w) + return (bit.band(w.cstat, 64)~=0) + end, + } +} +ffi.metatype("walltype", walltype_mt) + local spritetype_ptr_ct = ffi.typeof("spritetype_u_t *") -local spritetype_mt = { +spritetype_mt = { __pow = function(s, zofs) return ivec3_(s.x, s.y, s.z-zofs) end, @@ -252,7 +274,8 @@ local spritetype_mt = { end }, } -ffi.metatype("spritetype", spritetype_mt) +-- The user of this module can insert additional "spritetype" metamethods and +-- register them with "ffi.metatype". ---=== Restricted access to C variables from Lunatic ===--- diff --git a/polymer/eduke32/source/lunatic/defs_m32.ilua b/polymer/eduke32/source/lunatic/defs_m32.ilua index a167d469f..f21976f42 100644 --- a/polymer/eduke32/source/lunatic/defs_m32.ilua +++ b/polymer/eduke32/source/lunatic/defs_m32.ilua @@ -7,6 +7,7 @@ local ffiC = ffi.C --== First, load the definitions common to the game's and editor's Lua interface. decl = ffi.cdef local defs_c = require("defs_common") +ffi.metatype("spritetype", defs_c.spritetype_mt) defs_c.create_globals(_G) diff --git a/polymer/eduke32/source/lunatic/dynsymlist b/polymer/eduke32/source/lunatic/dynsymlist index 74eb9ccea..c2336c7b7 100644 --- a/polymer/eduke32/source/lunatic/dynsymlist +++ b/polymer/eduke32/source/lunatic/dynsymlist @@ -60,6 +60,7 @@ ud; g_player; playerswhenstarted; +lastvisinc; luaJIT_BC_lunacon; luaJIT_BC_con_lang; diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index 6e19bc142..9b9e89e52 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -9,6 +9,7 @@ local math = require("math") local string = require("string") local table = require("table") + local arg = arg local assert = assert @@ -612,7 +613,8 @@ local Co = { setgamename = newline_term_string, precache = cmd(D,D,D), - scriptsize = cmd(D), -- unused + scriptsize = cmd(D) + / "", -- no-op cheatkeys = cmd(D,D), definecheat = newline_term_string, -- XXX: actually tricker syntax (TS) @@ -713,6 +715,11 @@ local function handle_palfrom(...) v[1] or 0, v[2] or 0, v[3] or 0, v[4] or 0) end +local function handle_move(mv, ...) + local flags = {...} + return format(ACS":set_move(%s,%d)", mv, (flags[1] and bit.bor(...)) or 0) +end + -- NOTE about prefixes: most is handled by all_alt_pattern(), however commands -- that have no arguments and that are prefixes of other commands MUST be -- suffixed with a "* #sp1" pattern. @@ -799,9 +806,8 @@ local Ci = { / ACS":set_action(%1)", ai = cmd(AI) / ACS":set_ai(%1)", - -- TODO: move's flags move = sp1 * t_move * (sp1 * t_define)^0 - / ACS":set_move(%1)", + / handle_move, cactor = cmd(D) / SPS":set_picnum(%1)", @@ -876,7 +882,8 @@ local Ci = { / (PLS".actors_killed="..PLS".actors_killed+%1;"..ACS".actorstayput=-1"), addphealth = cmd(D) / "", -- TODO - angoff = cmd(D), + angoff = cmd(D) + / "spritext[_aci].angoff=%1", debug = cmd(D) / "", -- TODO? endofgame = cmd(D) @@ -916,7 +923,8 @@ local Ci = { fall = cmd() / "_con._VM_FallSprite(_aci)", - flash = cmd(), + flash = cmd() + / format("_con._flash(%s,%s)", ACS"", SPS""), getlastpal = cmd() / "_con._getlastpal(_aci)", insertspriteq = cmd(), @@ -926,7 +934,7 @@ local Ci = { nullop = cmd() / "", -- NOTE: really generate no code pkick = cmd() - / "", -- TODO + / format("_con._pkick(%s,%s)", PLS"", ACS""), pstomp = cmd() / PLS":pstomp(_aci)", resetactioncount = cmd() @@ -1119,10 +1127,8 @@ local Cif = { ifsound = cmd(D) / "", -- vvv TODO: this is not correct for GET_ACCESS or GET_SHIELD. - -- Additionally, it accesses the current sprite unconditinally - -- (will throw error if invalid). ifpinventory = cmd(D,D) - / format("_con._getinventory(%s,%%1,%s)~=%%2", PLS"", SPS".pal"), + / format("_con._getinventory(%s,%%1,_aci)~=%%2", PLS""), ifvarl = cmd(R,D), ifvarg = cmd(R,D), @@ -1145,12 +1151,12 @@ local Cif = { ifactorsound = cmd(R,R), ifp = (sp1 * t_define)^1 - / "false", -- TODO + / function (...) return format("_con._ifp(%d,_pli,_aci)", bit.bor(...)) end, ifsquished = cmd() / "false", -- TODO ifserver = cmd(), ifrespawn = cmd() - / "false", -- TODO + / format("_con._checkrespawn(%s)", SPS""), ifoutside = cmd() / format("_bit.band(sector[%s].ceilingstat,1)~=0", SPS".sectnum"), ifonwater = cmd() @@ -1164,9 +1170,9 @@ local Cif = { ifinwater = cmd() / format("sector[%s].lotag==2", SPS".sectnum"), ifinspace = cmd() - / "false", -- TODO + / format("_con._checkspace(%s,false)", SPS".sectnum"), ifinouterspace = cmd() - / "false", -- TODO + / format("_con._checkspace(%s,true)", SPS".sectnum"), ifhitweapon = cmd() / "_con._A_IncurDamage(_aci)", ifhitspace = cmd() @@ -1175,10 +1181,11 @@ local Cif = { / SPS".extra<=0", ifclient = cmd(), ifcanshoottarget = cmd() - / "false", -- TODO + / "_con._canshoottarget(_dist,_aci)", ifcanseetarget = cmd() -- TODO: maybe set timetosleep afterwards / format("_con._canseetarget(%s,%s)", SPS"", PLS""), - ifcansee = cmd() * #sp1, + ifcansee = cmd() * #sp1 + / format("_con._cansee(_aci,%s)", PLS""), ifbulletnear = cmd() / "_con._bulletnear(_aci)", ifawayfromwall = cmd() @@ -1706,7 +1713,7 @@ if (string.dump) then end --[[ - local file = io.stdout + local file = require("io").stdout for filename,codetab in pairs(g_file_code) do file:write(format("-- GENERATED CODE for \"%s\":\n", filename)) file:write(table.concat(flatten_codetab(codetab), "\n"))