Retire the "suckables list" + some fixes.

This commit is contained in:
Mari the Deer 2023-07-29 14:44:34 +02:00
commit 09f5bfd2f6
9 changed files with 135 additions and 231 deletions

View file

@ -1,3 +1,3 @@
[default]
SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r812 \cu(Sat 29 Jul 15:02:24 CEST 2023)\c-";
SWWM_SHORTVER="\cw1.3pre r812 \cu(2023-07-29 15:02:24)\c-";
SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r813 \cu(Sat 29 Jul 15:03:26 CEST 2023)\c-";
SWWM_SHORTVER="\cw1.3pre r813 \cu(2023-07-29 15:03:26)\c-";

View file

@ -228,25 +228,6 @@ extend Class SWWMHandler
}
}
}
else if ( e.Name ~== "swwmtrimsuckables" )
{
if ( multiplayer && (e.player != Net_Arbitrator) )
{
if ( e.player == consoleplayer )
Console.Printf("Only the net arbitrator can call this event.");
return;
}
int n = 0;
for ( int i=0; i<suckableactors.Size(); i++ )
{
if ( suckableactors[i] && (suckableactors[i].bSHOOTABLE || suckableactors[i].bMISSILE) ) continue;
suckableactors.Delete(i);
i--;
n++;
}
Console.Printf("%d suckable actors trimmed.",n);
return;
}
else if ( e.Name ~== "swwmcleareffects" )
{
if ( multiplayer && (e.player != Net_Arbitrator) )

View file

@ -5,12 +5,6 @@ extend Class SWWMHandler
// prevents revived monsters from spawning in more golden shells
Array<Actor> alreadygold;
// attempt to optimize Ynykron singularity suction
Array<Actor> suckableactors;
// for displaying beam-type projectiles
Array<Actor> beams;
// legendary monster markers (for the "has mutated" message)
Array<Inventory> legtrack;
@ -214,20 +208,6 @@ extend Class SWWMHandler
// for gibber throttling
if ( e.Thing is 'mkBloodDrop' ) blods_realcnt--;
else if ( e.Thing is 'mkFlyingGib' ) meats_realcnt--;
if ( e.Thing.default.bSHOOTABLE || (e.Thing is 'Inventory') || SWWMUtility.ValidProjectile(e.Thing) )
{
// remove from suckables
int pos = suckableactors.Find(e.Thing);
if ( pos < suckableactors.Size() )
suckableactors.Delete(pos);
}
else if ( SWWMUtility.IsBeamProj(e.Thing) )
{
// remove from beams
int pos = beams.Find(e.Thing);
if ( pos < beams.Size() )
beams.Delete(pos);
}
if ( profiling ) ProfileTock(PT_WORLDTHINGDESTROYED);
}
@ -389,11 +369,6 @@ extend Class SWWMHandler
let hp = Actor.Spawn("HeadpatTracker",e.Thing.pos);
hp.target = e.Thing;
}
// Ynykron vortex optimization (faster than a thinker iterator)
if ( e.Thing.bSHOOTABLE || (e.Thing is 'Inventory') || SWWMUtility.ValidProjectile(e.Thing) )
SuckableActors.Push(e.Thing);
else if ( SWWMUtility.IsBeamProj(e.Thing) )
Beams.Push(e.Thing);
// vanilla blood color changes
if ( (e.Thing.GetClass() == "BaronOfHell") || (e.Thing.GetClass() == "HellKnight") || (e.Thing.GetClass() == "Bishop") || (e.Thing.GetClass() == "Korax") )
{

View file

@ -278,16 +278,31 @@ extend Class SWWMHandler
// update trackers for anything around the player
double viewdist = SWWMStatusBar.MAPVIEWDIST*swwm_mm_zoom;
// still about as expensive as using a BlockThingsIterator, but without the need to allocate one every tic
int thisgroup = players[consoleplayer].Camera.CurSector.portalgroup;
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
Vector2 rv = a.pos.xy-players[consoleplayer].Camera.pos.xy;
Vector2 rv;
if ( s.portalgroup != thisgroup )
{
Vector2 relpos = players[consoleplayer].Camera.pos.xy+level.GetDisplacement(thisgroup,s.portalgroup);
rv = a.pos.xy-relpos;
}
else rv = a.pos.xy-players[consoleplayer].Camera.pos.xy;
double rad;
bool isproj = a.bMISSILE;
if ( SWWMUtility.IsBeamProj(a) )
{
isproj = true;
rad = SWWMUtility.IsYBeam(a)?(a.scale.y*cos(a.pitch-90)):(a.speed*cos(a.pitch));
}
else rad = a.radius;
if ( max(abs(rv.x)-a.radius,abs(rv.y)-a.radius) > viewdist )
continue;
if ( a == players[consoleplayer].Camera )
continue;
if ( a is 'GhostTarget' )
continue;
if ( !a.player && !a.bSOLID && !a.bSHOOTABLE && !a.bISMONSTER && !a.bFRIENDLY && !(a is 'Inventory') && !(a is 'Chancebox') )
if ( !a.player && !a.bSOLID && !a.bSHOOTABLE && !a.bISMONSTER && !a.bFRIENDLY && !(a is 'Inventory') && !(a is 'Chancebox') && !isproj )
continue;
if ( !level.allmap && !(deathmatch && (a is 'Inventory') && !a.bDROPPED) && !(a.IsFriend(players[consoleplayer].mo) && !(a.player && (a.player.mo != a))) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
@ -297,81 +312,10 @@ extend Class SWWMHandler
continue;
if ( (a is 'Chancebox') && (a.CurState != a.SpawnState) )
continue;
SWWMSimpleTracker.Track(self,a);
}
// we need to refer to the suckables array to find missiles
foreach ( a:suckableactors )
{
if ( !a || !a.bMISSILE ) continue;
Vector2 rv = a.pos.xy-players[consoleplayer].Camera.pos.xy;
if ( max(abs(rv.x)-a.radius,abs(rv.y)-a.radius) > viewdist )
continue;
if ( !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
if ( isproj && !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
SWWMSimpleTracker.Track(self,a);
}
foreach ( a:beams )
{
if ( !a ) continue;
Vector2 rv = a.pos.xy-players[consoleplayer].Camera.pos.xy;
double rad = SWWMUtility.IsYBeam(a)?(a.scale.y*cos(a.pitch-90)):(a.speed*cos(a.pitch));
if ( max(abs(rv.x)-rad,abs(rv.y)-rad) > viewdist )
continue;
if ( !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
SWWMSimpleTracker.Track(self,a);
}
// oh boy here we go
int thisgroup = players[consoleplayer].Camera.CurSector.portalgroup;
for ( int i=0; i<level.GetPortalGroupCount(); i++ )
{
if ( i == thisgroup ) continue;
Vector2 relpos = players[consoleplayer].Camera.pos.xy+level.GetDisplacement(thisgroup,i);
// still about as expensive as using a BlockThingsIterator, but without the need to allocate one every tic
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
Vector2 rv = a.pos.xy-relpos;
if ( max(abs(rv.x)-a.radius,abs(rv.y)-a.radius) > viewdist )
continue;
if ( a == players[consoleplayer].Camera )
continue;
if ( a is 'GhostTarget' )
continue;
if ( !a.player && !a.bSOLID && !a.bSHOOTABLE && !a.bISMONSTER && !a.bFRIENDLY && !(a is 'Inventory') && !(a is 'Chancebox') )
continue;
if ( !level.allmap && !(deathmatch && (a is 'Inventory') && !a.bDROPPED) && !a.IsFriend(players[consoleplayer].mo) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
if ( a.bKILLED || (a.Health <= 0) || a.bUnmorphed )
continue;
if ( (a is 'Inventory') && (!a.bSPECIAL || Inventory(a).Owner) )
continue;
if ( (a is 'Chancebox') && (a.CurState != a.SpawnState) )
continue;
SWWMSimpleTracker.Track(self,a);
}
// we need to refer to the suckables array to find missiles
foreach ( a:suckableactors )
{
if ( !a || !a.bMISSILE ) continue;
Vector2 rv = a.pos.xy-relpos;
if ( max(abs(rv.x)-a.radius,abs(rv.y)-a.radius) > viewdist )
continue;
if ( !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
SWWMSimpleTracker.Track(self,a);
}
foreach ( a:beams )
{
if ( !a ) continue;
Vector2 rv = a.pos.xy-relpos;
double rad = SWWMUtility.IsYBeam(a)?(a.scale.y*cos(a.pitch-90)):(a.speed*cos(a.pitch));
if ( max(abs(rv.x)-rad,abs(rv.y)-rad) > viewdist )
continue;
if ( !level.allmap && !(a.target && a.target.IsFriend(players[consoleplayer].mo)) && !a.CheckSight(players[consoleplayer].Camera,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
SWWMSimpleTracker.Track(self,a);
}
}
SWWMSimpleTracker trk = strackers;
SWWMSimpleTracker prev = null, next;
while ( trk )

View file

@ -323,10 +323,9 @@ Class GhostTarget : Actor
// player made noise or is visible again
if ( !master || (LastHeard == master) || !master.FindInventory("GhostPower") )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( hnd ) foreach ( a:hnd.suckableactors )
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
if ( !a || !a.bISMONSTER || a.player || !a.IsHostile(master) || (a.Health <= 0) ) continue;
if ( !a.bISMONSTER || a.player || !a.IsHostile(master) || (a.Health <= 0) ) continue;
if ( a.target == self ) a.target = master;
}
Destroy();
@ -384,11 +383,10 @@ Class GhostPower : Powerup
Super.DoEffect();
if ( !Owner ) return;
// are any enemies targetting us? if so, make them focus on a fake target located where we currently are standing
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
Actor gt = null;
if ( hnd ) foreach ( a:hnd.suckableactors )
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
if ( !a || !a.bISMONSTER || a.player || !a.IsHostile(Owner) || (a.Health <= 0) ) continue;
if ( !a.bISMONSTER || a.player || !a.IsHostile(Owner) || (a.Health <= 0) ) continue;
// make them forget the ghost if we make noise
if ( (a.LastHeard == Owner) && (a.target is 'GhostTarget') && (a.target.master == Owner) )
{

View file

@ -773,10 +773,6 @@ Class Demolitionist : PlayerPawn
player.SetPSprite(PSP_WEAPON,player.ReadyWeapon.ResolveState("Deselect"));
}
}
// re-add ourselves to the "suckable list" (otherwise the Ynykron Singularity won't hurt us)
if ( !hnd ) hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( hnd && (hnd.SuckableActors.Find(self) >= hnd.SuckableActors.Size()) )
hnd.SuckableActors.Push(self);
}
override bool PreTeleport( Vector3 destpos, double destangle, int flags )

View file

@ -559,6 +559,9 @@ extend Class Demolitionist
foreach ( a:hitlist )
{
if ( spd <= 0 ) break;
// we have to recalculate these
Vector3 diff = level.Vec3Diff(pos,a.pos);
Vector3 dirto = diff.unit();
// large monsters will stop the player (unless hit from above if we're going at ground pound speed)
A_QuakeEx(4,4,4,10,0,128,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D);
A_AlertMonsters(swwm_uncapalert?0:800);

View file

@ -69,16 +69,20 @@ Class SWWMGesture : SWWMWeapon
private void CheckWave()
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( !hnd ) return;
foreach ( a:hnd.suckableactors )
bool breakout = false;
foreach ( s:level.Sectors )
{
if ( !a || (a.Health > 0) || (a.tics == -1) || !(a.bISMONSTER || a.player) || !a.IsHostile(Owner) ) continue;
// check if we can see it
if ( !SWWMUtility.InPlayerFOV(Owner.player,a) ) continue;
// someone's dying
SWWMUtility.MarkAchievement("wave",Owner.player);
break;
for ( Actor a=s.thinglist; a; a=a.snext )
{
if ( (a.Health > 0) || (a.tics == -1) || !(a.bISMONSTER || a.player) || !a.IsHostile(Owner) ) continue;
// check if we can see it
if ( !SWWMUtility.InPlayerFOV(Owner.player,a) ) continue;
// someone's dying
SWWMUtility.MarkAchievement("wave",Owner.player);
breakout = true;
break;
}
if ( breakout ) break;
}
}

View file

@ -1090,109 +1090,112 @@ Class YnykronSingularity : SWWMNonInteractiveActor
beamers[i].master = self;
}
// suck in reachable targets
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));
if ( hnd )
Array<Actor> candidates;
candidates.Clear();
// gather first
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
foreach ( a:hnd.SuckableActors )
if ( !a.bSHOOTABLE && !(a is 'Inventory') && !SWWMUtility.ValidProjectile(a) )
continue;
if ( a.bDORMANT || (a.Health <= 0) || (a == self) || !SWWMUtility.SphereIntersect(a,pos,20000.*scale.x) || !CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
if ( (a is 'Inventory') && !a.bDROPPED ) // must be a dropped pickup
continue;
if ( a.player && (a.player.cheats&CF_NOCLIP2) ) // for safety
continue;
if ( (a is 'YnykronSingularity') && a.bNOBLOCKMAP ) // dead singularity, ignore
continue;
candidates.Push(a);
}
foreach ( a:candidates )
{
double capmass = a.mass;
// succ
if ( SWWMUtility.SphereIntersect(a,pos,32.*scale.x) )
{
if ( !a ) continue;
if ( a.bDORMANT || (a.Health <= 0) || (a == self) || !SWWMUtility.SphereIntersect(a,pos,20000.*scale.x) || !CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
if ( (a is 'Inventory') && !a.bDROPPED ) // must be a dropped pickup
continue;
if ( a.player && (a.player.cheats&CF_NOCLIP2) ) // for safety
continue;
if ( (a is 'YnykronSingularity') && a.bNOBLOCKMAP ) // dead singularity, ignore
continue;
double capmass = a.mass;
// succ
if ( SWWMUtility.SphereIntersect(a,pos,32.*scale.x) )
// merge with other vortices
if ( a is 'YnykronSingularity' )
{
// merge with other vortices
if ( a is 'YnykronSingularity' )
critmass += YnykronSingularity(a).critmass;
specialf2 += a.specialf2;
a.Destroy();
continue;
}
// don't delet inventory
if ( a is 'Inventory' )
continue;
// delet missile
if ( a.bMISSILE )
{
specialf2 += 5.;
a.Destroy();
continue;
}
// voodoo dolls just get erased (how convenient)
// otherwise instantly vaporize the poor sap
if ( a.player && (a.player.mo != a) ) a.Destroy();
else if ( a.CountInv("GrilledCheeseSandwich") > 0 )
{
// force use the sandwich, warp to safe spot
let gc = GrilledCheeseSandwich(a.FindInventory("GrilledCheeseSandwich"));
gc.SafeTeleport(true);
a.A_StartSound(gc.UseSound,CHAN_ITEMEXTRA);
gc.DoTheThing(true);
gc.Amount--;
}
else if ( !a.FindInventory("GrilledCheeseSafeguard") )
a.DamageMobj(self,target,int.max,'Ynykron',DMG_FORCED|DMG_THRUSTLESS);
if ( a && (a.Health <= 0) )
{
if ( a.player )
{
critmass += YnykronSingularity(a).critmass;
specialf2 += a.specialf2;
a.Destroy();
continue;
}
// don't delet inventory
if ( a is 'Inventory' )
continue;
// delet missile
if ( a.bMISSILE )
{
specialf2 += 5.;
a.Destroy();
continue;
}
// voodoo dolls just get erased (how convenient)
// otherwise instantly vaporize the poor sap
if ( a.player && (a.player.mo != a) ) a.Destroy();
else if ( a.CountInv("GrilledCheeseSandwich") > 0 )
{
// force use the sandwich, warp to safe spot
let gc = GrilledCheeseSandwich(a.FindInventory("GrilledCheeseSandwich"));
gc.SafeTeleport(true);
a.A_StartSound(gc.UseSound,CHAN_ITEMEXTRA);
gc.DoTheThing(true);
gc.Amount--;
}
else if ( !a.FindInventory("GrilledCheeseSafeguard") )
a.DamageMobj(self,target,int.max,'Ynykron',DMG_FORCED|DMG_THRUSTLESS);
if ( a && (a.Health <= 0) )
{
if ( a.player )
if ( a == target )
{
if ( a == target )
{
SWWMUtility.MarkAchievement("oopsie",a.player);
target = PlayerGone.FeckOff(a);
}
else PlayerGone.FeckOff(a);
SWWMUtility.MarkAchievement("oopsie",a.player);
target = PlayerGone.FeckOff(a);
}
if ( a.FindState("YnykronAltDeath",true) )
a.SetStateLabel("YnykronAltDeath"); // dedicated state
else
{
// poof away manually
a.bINVISIBLE = true;
a.A_ChangeLinkFlags(false); // remove from blockmap, should guarantee archviles not raising this
IDontFeelSoGood.DeletThis(a,true); // ensures corpse is deleted too
}
if ( target && a.FindInventory("EndgameBossMarker") )
SWWMUtility.MarkAchievement("ligma",target.player);
else PlayerGone.FeckOff(a);
}
if ( !a || (a.Health <= 0) )
specialf2 += min(100.,capmass*.6); // partial absorption
continue;
}
capmass = max(50.,a.mass);
Vector3 dirto = level.Vec3Diff(a.Vec3Offset(0,0,a.Height/2),pos);
double dist = dirto.length();
dirto /= dist;
// rip
if ( a && a.bSHOOTABLE && SWWMUtility.SphereIntersect(a,pos,200.*scale.x) )
a.DamageMobj(self,target,int(clamp(10.-dist*.05,0.,5.)),'YnykronAlt',DMG_FORCED|DMG_THRUSTLESS);
if ( !a ) continue;
// weak gravitational force as per v-field (approximate)
double grav = (3.*YNON_CONST*specialf2*capmass)/(dist**.5);
// strong gravitational force as per u-field (approximate)
grav += (4.*AXAN_CONST*specialf2*capmass)/(dist**2.);
// account for ground friction
if ( a.pos.z <= a.floorz )
{
double frict, movef;
[frict, movef] = a.GetFriction();
grav *= movef;
if ( !a.player ) dirto.z += .1;
}
if ( a.mass < LARGE_MASS )
{
// v-force field compression (very rough approximation)
a.vel *= max(.0,1.-.03*(grav*grav)/(MION_CONST*MION_CONST));
a.vel += dirto*grav/(capmass*GameTicRate);
if ( a.FindState("YnykronAltDeath",true) )
a.SetStateLabel("YnykronAltDeath"); // dedicated state
else
{
// poof away manually
a.bINVISIBLE = true;
a.A_ChangeLinkFlags(false); // remove from blockmap, should guarantee archviles not raising this
IDontFeelSoGood.DeletThis(a,true); // ensures corpse is deleted too
}
if ( target && a.FindInventory("EndgameBossMarker") )
SWWMUtility.MarkAchievement("ligma",target.player);
}
if ( !a || (a.Health <= 0) )
specialf2 += min(100.,capmass*.6); // partial absorption
continue;
}
capmass = max(50.,a.mass);
Vector3 dirto = level.Vec3Diff(a.Vec3Offset(0,0,a.Height/2),pos);
double dist = dirto.length();
dirto /= dist;
// rip
if ( a && a.bSHOOTABLE && SWWMUtility.SphereIntersect(a,pos,200.*scale.x) )
a.DamageMobj(self,target,int(clamp(10.-dist*.05,0.,5.)),'YnykronAlt',DMG_FORCED|DMG_THRUSTLESS);
if ( !a ) continue;
// weak gravitational force as per v-field (approximate)
double grav = (3.*YNON_CONST*specialf2*capmass)/(dist**.5);
// strong gravitational force as per u-field (approximate)
grav += (4.*AXAN_CONST*specialf2*capmass)/(dist**2.);
// account for ground friction
if ( a.pos.z <= a.floorz )
{
let [frict, movef] = a.GetFriction();
grav *= movef;
if ( !a.player ) dirto.z += .1;
}
if ( a.mass < LARGE_MASS )
{
// v-force field compression (very rough approximation)
a.vel *= max(.0,1.-.03*(grav*grav)/(MION_CONST*MION_CONST));
a.vel += dirto*grav/(capmass*GameTicRate);
}
}
// push away from nearby geometry