diff --git a/polymer/eduke32/source/gamedef.c b/polymer/eduke32/source/gamedef.c index 6b65a3a47..3c7edf42a 100644 --- a/polymer/eduke32/source/gamedef.c +++ b/polymer/eduke32/source/gamedef.c @@ -695,6 +695,7 @@ const char *EventNames[MAXEVENTS] = "EVENT_SAVEGAME", "EVENT_PREGAME", "EVENT_CHANGEMENU", + "EVENT_DAMAGEHPLANE", #ifdef LUNATIC "EVENT_ANIMATEALLSPRITES", #endif diff --git a/polymer/eduke32/source/gameexec.h b/polymer/eduke32/source/gameexec.h index 9d750e539..234a11f49 100644 --- a/polymer/eduke32/source/gameexec.h +++ b/polymer/eduke32/source/gameexec.h @@ -125,8 +125,9 @@ enum GameEvent_t { EVENT_SAVEGAME, EVENT_PREGAME, EVENT_CHANGEMENU, + EVENT_DAMAGEHPLANE, // 95 #ifdef LUNATIC - EVENT_ANIMATEALLSPRITES, // 95 + EVENT_ANIMATEALLSPRITES, #endif MAXEVENTS }; diff --git a/polymer/eduke32/source/lunatic/con_lang.lua b/polymer/eduke32/source/lunatic/con_lang.lua index 67c9d3fb4..536ce2f99 100644 --- a/polymer/eduke32/source/lunatic/con_lang.lua +++ b/polymer/eduke32/source/lunatic/con_lang.lua @@ -17,7 +17,7 @@ MAXGAMETYPES = 16 MAXSKILLS = 7 -MAXEVENTS = 95 -- KEEPINSYNC with EVENT_* list +MAXEVENTS = 96 -- KEEPINSYNC with EVENT_* list MAXSOUNDS = 4096 MAXSESSIONVARS = 8 -- KEEPINSYNC lunatic_game.c @@ -170,7 +170,8 @@ EVENT = { EVENT_SAVEGAME = 92, EVENT_PREGAME = 93, EVENT_CHANGEMENU = 94, --- EVENT_ANIMATEALLSPRITES = 95, -- internal + EVENT_DAMAGEHPLANE = 95, +-- EVENT_ANIMATEALLSPRITES = 96, -- internal } -- NOTE: negated values are not exported to the ffi.C namespace or CON. diff --git a/polymer/eduke32/source/lunatic/control.lua b/polymer/eduke32/source/lunatic/control.lua index 357cadda4..95750e060 100644 --- a/polymer/eduke32/source/lunatic/control.lua +++ b/polymer/eduke32/source/lunatic/control.lua @@ -272,6 +272,8 @@ end function insertsprite(tab_or_tilenum, ...) local tilenum, pos, sectnum -- mandatory -- optional with defaults: + -- TODO: swap order of owner and statnum? + -- XXX: owner -1 valid??? local owner, statnum local shade, xrepeat, yrepeat, ang, xvel, zvel = 0, 48, 48, 0, 0, 0 diff --git a/polymer/eduke32/source/lunatic/defs.ilua b/polymer/eduke32/source/lunatic/defs.ilua index 58abf56b5..e79f81e4f 100644 --- a/polymer/eduke32/source/lunatic/defs.ilua +++ b/polymer/eduke32/source/lunatic/defs.ilua @@ -2091,7 +2091,7 @@ local function our_gameactor(args) -- 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 + gameevent_internal(96, animate_all_sprites) -- EVENT_ANIMATEALLSPRITES end -- All good, set the tile bits in initial run and register the actor! diff --git a/polymer/eduke32/source/lunatic/defs_common.lua b/polymer/eduke32/source/lunatic/defs_common.lua index 00cb8c49e..c7e0f75ce 100644 --- a/polymer/eduke32/source/lunatic/defs_common.lua +++ b/polymer/eduke32/source/lunatic/defs_common.lua @@ -884,7 +884,7 @@ static_members.sector.STAT = conststruct TRANS2 = 128, TRANS1 = 256, BLOCK = 512, - HITSCAN = 1024, + HITSCAN = 2048, FLIP_BITMASK = 16+32, ORIENT_BITMASK = 4+16+32, @@ -936,6 +936,8 @@ static_members.sprite.CSTAT = conststruct ALIGN_BITMASK = 16+32, TRANS_BITMASK = 2+512, + + INVISIBLE = 32768, } local bitar = require("bitar") @@ -944,6 +946,19 @@ local bitar = require("bitar") -- machines. This sucks. static_members.sector.showbitmap = bitar.new(ffiC.MAXSECTORS-1, ffi.cast("int32_t *", ffiC.show2dsector)) +static_members.sector.DAMAGEHPLANE = conststruct +{ + SUPPRESS = -1, + DEFAULT = 0, + GLASSBREAK = 2^20, +} + +function static_members.sector.damagehplane_whatsect(RETURN) + local what = (band(RETURN, 65536)~=0) and "ceiling" or "floor" + local sectnum = band(RETURN, ffiC.MAXSECTORS-1) + return what, sectnum +end + local function iter_allsprites(_, curi) for i=curi+1,ffiC.MAXSPRITES-1 do if (ffiC.sprite[i].statnum ~= ffiC.MAXSTATUS) then diff --git a/polymer/eduke32/source/lunatic/doc/lunatic.txt b/polymer/eduke32/source/lunatic/doc/lunatic.txt index fa9d7e91d..43ca7e222 100644 --- a/polymer/eduke32/source/lunatic/doc/lunatic.txt +++ b/polymer/eduke32/source/lunatic/doc/lunatic.txt @@ -942,7 +942,7 @@ sprite by `zofs` units''. Provides a mapping of symbolic names to values applicable to <>. These name single bits: `BLOCK`, `TRANS1`, `XFLIP`, `YFLIP`, `ALIGNWALL`, `ALIGNFLOOR`, `ONESIDE`, -`CENTER`, `HITSCAN`, `TRANS2`, +`CENTER`, `HITSCAN`, `TRANS2`, `INVISIBLE`, while the following denote _bit masks_: `ALIGN_BITMASK`, `TRANS_BITMASK`. ===== `spriteext` @@ -2232,6 +2232,45 @@ cheat give a different ``full'' amount. A negative value is ignored. `CHEATGETSTEROIDS`, `CHEATGETHEAT`, `CHEATGETBOOT`, `CHEATGETSHIELD`, `CHEATGETSCUBA`, `CHEATGETHOLODUKE`, `CHEATGETJETPACK`, `CHEATGETFIRSTAID`. +[float] +`EVENT_DAMAGEHPLANE` +~~~~~~~~~~~~~~~~~~~~ + +Triggered when a ceiling or a floor (collectively called ``hplane'') is +determined as being damaged. The event receives `RETURN` as a value that can be +decoded into two parts by passing it to `sector.damagehplane_whatsect`: + +[source] +---------- +local what, sectnum = sector.damagehplane_whatsect(gv.RETURN) +---------- +Then, + +* `what` is one of the strings `'ceiling'` or `'floor'` and +* `sectnum` is the sector whose hplane is considered to be damaged. + +When `EVENT_DAMAGEHPLANE` is left, `RETURN` is examined to determine the +further action. It may be one of three values given by `sector.DAMAGEHPLANE` +(abbreviated `DHP` in the following): + +* `DHP.SUPPRESS`: the hard-wired code that would subsequently be run is + suppressed entirely + +* `DHP.DEFAULT`: The default code for hplane damaging is run. For floors, it + does nothing. For ceilings, it checks whether it has a tile number in a + hard-coded set of values depicting a breakable light. In that case, the tile + number is changed to the ``broken'' version and a ``glass breaking'' effect + consisting of playing a sound and spawning glass sprites is started. Also, + certain code related to SE3 and SE12 effects is run. + +* `DHP.GLASSBREAK`: The light-breaking effect described above is run + unconditionally, but *without* changing the hplane's tile number, which is + assumed to have been done by the event. + +If value last assigned to `RETURN` is not one in the above-mentioned set when +`EVENT_DAMAGEHPLANE` is left, the behavior is undefined. + + [float] TODO ~~~~ diff --git a/polymer/eduke32/source/lunatic/test/damagehplane.lua b/polymer/eduke32/source/lunatic/test/damagehplane.lua new file mode 100644 index 000000000..a30017c33 --- /dev/null +++ b/polymer/eduke32/source/lunatic/test/damagehplane.lua @@ -0,0 +1,105 @@ + +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] + + -- TODO: provide cf:set_picnum() + if (what=="ceiling") then + sector[sectnum]:set_ceilingpicnum(D.GLASS2 + cnt) + else + sector[sectnum]:set_floorpicnum(D.GLASS2 + cnt) + end + + 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 +{ + gv.EVENT_DAMAGEHPLANE, + + function(aci, pli, dist) + local what, sectnum = sector.damagehplane_whatsect(gv.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 + end + end + + -- 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, aci, 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 + end + + gv.RETURN = DHP.DEFAULT + end +} diff --git a/polymer/eduke32/source/sector.c b/polymer/eduke32/source/sector.c index 68d67a424..6b0bf4082 100644 --- a/polymer/eduke32/source/sector.c +++ b/polymer/eduke32/source/sector.c @@ -1874,13 +1874,27 @@ void A_DamageWall(int32_t spr,int32_t dawallnum,const vec3_t *pos,int32_t atwith } } +// NOTE: return value never examined in any of the callers. int32_t Sect_DamageCeilingOrFloor(int32_t floorp, int32_t sn) { int32_t i, j; + const int32_t RETURN_in = floorp ? 131072+sn : 65536+sn; + int32_t ret = VM_OnEvent(EVENT_DAMAGEHPLANE, g_player[screenpeek].ps->i, screenpeek, -1, RETURN_in); + + if (ret < 0) + return 0; + if (floorp) return 0; + if (ret == (1<<20)) + { + // Execute the hard-coded stuff without changing picnum (expected to + // have been done by the event). + goto GLASSBREAK_CODE; + } + switch (DYNAMICTILEMAP(sector[sn].ceilingpicnum)) { case WALLLIGHT1__STATIC: @@ -1890,9 +1904,6 @@ int32_t Sect_DamageCeilingOrFloor(int32_t floorp, int32_t sn) case TECHLIGHT2__STATIC: case TECHLIGHT4__STATIC: - A_SpawnCeilingGlass(g_player[myconnectindex].ps->i,sn,10); - A_PlaySound(GLASS_BREAKING,g_player[screenpeek].ps->i); - if (sector[sn].ceilingpicnum == WALLLIGHT1) sector[sn].ceilingpicnum = WALLLIGHTBUST1; @@ -1911,6 +1922,9 @@ int32_t Sect_DamageCeilingOrFloor(int32_t floorp, int32_t sn) if (sector[sn].ceilingpicnum == TECHLIGHT4) sector[sn].ceilingpicnum = TECHLIGHTBUST4; + GLASSBREAK_CODE: + A_SpawnCeilingGlass(g_player[myconnectindex].ps->i,sn,10); + A_PlaySound(GLASS_BREAKING,g_player[screenpeek].ps->i); if (sector[sn].hitag == 0) {