Replace usage of ThinkerIterator and BlockThingsIterator in various places where we can instead loop through sector thing lists.

While in the latter case this may result in longer loops, it also reduces GC thrashing by not needing to allocate an iterator every time.
This also simplifies the DoBlast code as there is no longer a need to manually traverse portals vertically.
This commit is contained in:
Mari the Deer 2023-07-29 13:15:34 +02:00
commit 2bd1cb0657
17 changed files with 113 additions and 220 deletions

View file

@ -1036,14 +1036,18 @@ Class MisterGrenade : Actor
if ( bNoProx ) return;
// "safe delay" for main grenade
if ( !bAMBUSH && (ReactionTime > default.ReactionTime-20) ) return;
let bt = BlockThingsIterator.Create(self,120);
while ( bt.Next() )
bool breakout = false;
foreach ( s:level.Sectors )
{
let t = bt.Thing;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || !SWWMUtility.SphereIntersect(t,level.Vec3Offset(pos,vel),bAMBUSH?80:120) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
special1++;
tracer = t;
break;
for ( Actor t=s.thinglist; t; t=t.snext )
{
if ( !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || !SWWMUtility.SphereIntersect(t,level.Vec3Offset(pos,vel),bAMBUSH?80:120) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
special1++;
tracer = t;
breakout = true;
break;
}
if ( breakout ) break;
}
}
@ -1129,13 +1133,11 @@ Class MisterGrenade : Actor
}
// gather seekable targets
Array<Actor> candidates;
let bt = BlockThingsIterator.Create(self,2000);
let tt = new("TargetTracer");
tt.target = target;
while ( bt.Next() )
foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext )
{
let t = bt.Thing;
if ( !t || (t == tracer) || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain')) || (t.Health <= 0) || (target && t.IsFriend(target)) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
if ( (t == tracer) || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain')) || (t.Health <= 0) || (target && t.IsFriend(target)) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
// don't seek if enemy is too close to shooter
if ( target && SWWMUtility.SphereIntersect(t,target.pos,250) ) continue;
// don't seek if shooter is between us and the enemy

View file

@ -3,7 +3,6 @@
extend Class SWWMHandler
{
ui SWWMProjectionData projdata;
transient ui ThinkerIterator dbgti;
private ui void DrawWorldLine( RenderEvent e, Vector3 apos, Vector3 bpos, Color col )
{
@ -103,10 +102,7 @@ extend Class SWWMHandler
if ( !swwm_debugview ) return;
// prepare projection data, we're going to need this
SWWMUtility.PrepareProjData(projdata,e.ViewPos,e.ViewAngle,e.ViewPitch,e.ViewRoll,players[consoleplayer].fov);
if ( !dbgti ) dbgti = ThinkerIterator.Create("Actor");
else dbgti.Reinit();
Actor a;
while ( a = Actor(dbgti.Next()) )
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
if ( (a == players[consoleplayer].Camera) && !(players[consoleplayer].cheats&CF_CHASECAM) ) continue;
if ( a.bINVISIBLE && !(a is 'DynamicLight') ) continue;

View file

@ -178,14 +178,18 @@ extend Class SWWMHandler
// copies the floatbob of overlapping identical items, so it doesn't look weird
private void CopyFloatBob( Actor a )
{
let bt = BlockThingsIterator.Create(a,16);
while ( bt.Next() )
bool breakout = false;
foreach ( s:level.Sectors )
{
let t = bt.Thing;
if ( !t || (t == a) || !(t is 'Inventory') || !(t.spawnpoint ~== a.spawnpoint) ) continue;
a.floatbobphase = t.floatbobphase;
a.angle = t.angle; // also copy angle
break;
for ( Actor t=s.thinglist; t; t=t.snext )
{
if ( (t == a) || !(t is 'Inventory') || !(t.spawnpoint ~== a.spawnpoint) ) continue;
a.floatbobphase = t.floatbobphase;
a.angle = t.angle; // also copy angle
breakout = true;
break;
}
if ( breakout ) break;
}
}

View file

@ -98,11 +98,9 @@ extend Class SWWMHandler
}
bool enteredcombat = false;
// add new entries
if ( !cti ) cti = ThinkerIterator.Create("Actor");
else cti.Reinit();
Actor a, keyactor = null;
bool bossfound = false;
while ( a = Actor(cti.Next()) )
// we can use this instead of a thinker iterator as only actors that EXIST physically could count as combatants
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
if ( !a.player && !a.bISMONSTER ) continue;
// ignore the dead
@ -206,12 +204,12 @@ extend Class SWWMHandler
}
mapclearagain++;
if ( !iwantdie ) return;
let ti = ThinkerIterator.Create("SWWMStats",Thinker.STAT_STATIC);
SWWMStats s;
while ( s = SWWMStats(ti.Next()) )
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( s.deaths > 0 )
return;
if ( !playeringame[i] ) continue;
let demo = Demolitionist(players[i].mo);
if ( !demo || !demo.mystats ) continue;
if ( demo.mystats.deaths > 0 ) return;
}
SWWMUtility.MarkAchievement("wantdie",players[consoleplayer]);
}
@ -279,11 +277,9 @@ extend Class SWWMHandler
}
// update trackers for anything around the player
double viewdist = SWWMStatusBar.MAPVIEWDIST*swwm_mm_zoom;
BlockThingsIterator bt = BlockThingsIterator.Create(players[consoleplayer].Camera,viewdist);
while ( bt.Next() )
// 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 )
{
let a = bt.Thing;
if ( !a ) 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;
@ -325,19 +321,15 @@ extend Class SWWMHandler
continue;
SWWMSimpleTracker.Track(self,a);
}
bt.Destroy();
// 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);
if ( bt ) bt.Destroy();
bt = BlockThingsIterator.CreateFromPos(relpos.x,relpos.y,players[consoleplayer].Camera.pos.z,players[consoleplayer].Camera.pos.z+players[consoleplayer].Camera.height,viewdist,false);
while ( bt.Next() )
// 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 )
{
let a = bt.Thing;
if ( !a ) continue;
Vector2 rv = a.pos.xy-relpos;
if ( max(abs(rv.x)-a.radius,abs(rv.y)-a.radius) > viewdist )
continue;

View file

@ -523,15 +523,19 @@ Class HammerspaceEmbiggener : Inventory
// since backpacks are taller in Doom
if ( gameinfo.gametype&GAME_DoomChex )
A_SetSize(-1,26);
let bt = BlockThingsIterator.Create(self,16);
int tamount = Amount;
while ( bt.Next() )
foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; )
{
let t = bt.Thing;
if ( !t || (t == self) || !(t is 'HammerspaceEmbiggener') || !(t.spawnpoint ~== spawnpoint) ) continue;
let next = t.snext;
if ( (t == self) || !(t is 'HammerspaceEmbiggener') || !(t.spawnpoint ~== spawnpoint) )
{
t = next;
continue;
}
tamount += HammerspaceEmbiggener(t).Amount;
t.ClearCounters();
t.Destroy();
t = next;
}
if ( tamount <= 1 ) return;
tamount -= tamount%2; // always even numbered

View file

@ -58,12 +58,10 @@ Class LampMoth : Actor
if ( !lamp )
{
// look for nearby lamps
let bi = BlockThingsIterator.Create(self,250);
double mindist = 62500.;
while ( bi.Next() )
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
if ( !bi.Thing || !(bi.Thing is 'CompanionLamp') ) continue;
Actor a = bi.Thing;
if ( !(a is 'CompanionLamp') ) continue;
double dist = Distance3DSquared(a);
if ( (a.frame == 0) || (dist > mindist) && !CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
mindist = dist;

View file

@ -310,11 +310,9 @@ Class GhostTarget : Actor
}
if ( isFrozen() ) return;
if ( diedie ) A_FadeOut(.05);
let bt = BlockThingsIterator.Create(self,300);
while ( bt.Next() )
foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext )
{
let t = bt.Thing;
if ( !t || !t.bIsMonster || t.player || !t.IsHostile(master) || (t.target != self) ) continue;
if ( !t.bIsMonster || t.player || !t.IsHostile(master) || (t.target != self) ) continue;
if ( SWWMUtility.BoxIntersect(self,t,pad:16) || t.CheckMeleeRange() )
{
// they found out, there's no one here

View file

@ -510,9 +510,7 @@ Class Mykradvo : Inventory
{
targets.Clear();
// search all actively hostile enemies within 50m
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a=Actor(ti.Next()) )
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
// must be an active, shootable live monster
if ( !a.bISMONSTER || !a.bSHOOTABLE || a.bDORMANT || (a.Health <= 0) ) continue;

View file

@ -5,18 +5,15 @@ extend Class Demolitionist
{
if ( player.cmd.buttons&BT_USER3 )
{
let bt = BlockThingsIterator.Create(self,800);
while ( bt.Next() )
foreach ( s:level.Sectors ) for ( Actor i=s.thinglist; i; i=i.snext )
{
let i = bt.Thing;
if ( !i || (!(i is 'Inventory') && !(i is 'Chancebox') && !(i is 'SWWMRespawnTimer')) ) continue;
if ( !(i is 'Inventory') && !(i is 'Chancebox') && !(i is 'SWWMRespawnTimer') ) continue;
if ( (i is 'Inventory') && (i.bINVISIBLE || !i.bSPECIAL || Inventory(i).Owner) ) continue;
if ( (i is 'Chancebox') && (i.CurState != i.SpawnState) ) continue;
if ( !SWWMUtility.SphereIntersect(i,pos,800) ) continue;
if ( !level.allmap && !i.CheckSight(self,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
SWWMItemSense.Spawn(self,i);
}
bt.Destroy();
}
SWWMItemsense itm = itemsense;
SWWMItemsense prev = null, next;
@ -53,11 +50,9 @@ extend Class Demolitionist
}
if ( (magitem_cnt < 8) && !swwm_usetopickup )
{
let bt = BlockThingsIterator.Create(self,500);
while ( bt.Next() )
foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext )
{
let t = bt.Thing;
if ( !t || !(t is 'Inventory') || !t.bSPECIAL || !t.bDROPPED || t.bINVISIBLE || Inventory(t).Owner || !SWWMUtility.SphereIntersect(t,pos,500) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
if ( !(t is 'Inventory') || !t.bSPECIAL || !t.bDROPPED || t.bINVISIBLE || Inventory(t).Owner || !SWWMUtility.SphereIntersect(t,pos,500) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
let i = Inventory(t);
Class<Inventory> cls = i.GetClass();
@ -87,7 +82,6 @@ extend Class Demolitionist
magitem = nmi;
magitem_cnt++;
}
bt.Destroy();
}
SWWMMagItem itm = magitem;
SWWMMagItem prev = null, next;
@ -529,14 +523,13 @@ extend Class Demolitionist
Vector3 viewdir = SWWMUtility.Vec3FromAngles(angle,pitch);
// look for things we could potentially bump into
bool bumped = false;
let bi = BlockThingsIterator.Create(self,500);
let raging = RagekitPower(FindInventory("RagekitPower"));
double maxmass = max(mass*spd/40.,200);
if ( raging ) maxmass *= 2;
while ( (spd > 0) && bi.Next() )
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
a = bi.Thing;
if ( !a || (a == self) || (!a.bSOLID && !a.bSHOOTABLE) || a.bTHRUACTORS || a.bCORPSE || !CanCollideWith(a,false) || !a.CanCollideWith(self,true) ) continue;
if ( spd <= 0 ) break;
if ( (a == self) || (!a.bSOLID && !a.bSHOOTABLE) || a.bTHRUACTORS || a.bCORPSE || !CanCollideWith(a,false) || !a.CanCollideWith(self,true) ) continue;
if ( !SWWMUtility.ExtrudeIntersect(self,a,dir*(spd+radius),8) ) continue;
if ( (a.pos.z <= a.floorz) && (a.height <= MaxStepHeight) ) continue;
Vector3 diff = level.Vec3Diff(pos,a.pos);

View file

@ -63,17 +63,12 @@ extend Class SWWMUtility
else brange = 1./(ExplosionRadius-FullDamageRadius);
Actor Instigator = realsource?realsource:(flags&DE_NOTMISSILE)?Source:Source.target;
int dflg = ((flags&DE_NONEXPLOSIVE)?0:DMG_EXPLOSION)|dmgflags;
BlockThingsIterator bi = BlockThingsIterator.Create(Source,ExplosionRadius);
int nhit = 0, nkill = 0;
bool haskilled = false;
Array<Actor> washit;
washit.Clear();
while ( bi.Next() )
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
Actor a = bi.Thing;
washit.Push(a);
// early checks for self and ignored actor (usually the instigator)
if ( !a || (a == ignoreme) || (a == Source) )
if ( (a == ignoreme) || (a == Source) )
continue;
// can't be affected
if ( !a.bSHOOTABLE && !a.bVULNERABLE )
@ -141,90 +136,6 @@ extend Class SWWMUtility
if ( (flags&DE_COUNTFHKILLS) && (oldhp < basehp) ) continue; // was not at full health
if ( (!a || (a.Health <= 0)) && (!(flags&DE_COUNTENEMIES) || hostile) && (!(flags&DE_COUNTSTEALTH) || inactive) ) nkill++;
}
// traverse portals (needed since BlockThingsIterator can't properly cross sector portals in both vertical directions)
int thisgroup = Source.CurSector.portalgroup;
for ( int i=0; i<level.GetPortalGroupCount(); i++ )
{
if ( i == thisgroup ) continue;
Vector2 relpos = Source.pos.xy+level.GetDisplacement(thisgroup,i);
bi = BlockThingsIterator.CreateFromPos(relpos.x,relpos.y,Source.pos.z,Source.pos.z+Source.height,ExplosionRadius,false);
while ( bi.Next() )
{
Actor a = bi.Thing;
// early exit for already processed actors
if ( washit.Find(a) < washit.Size() )
continue;
washit.Push(a);
// early checks for self and ignored actor (usually the instigator)
if ( !a || (a == ignoreme) || (a == Source) )
continue;
// can't be affected
if ( !a.bSHOOTABLE && !a.bVULNERABLE )
continue;
// no blasting if no radius dmg (unless forced)
if ( a.bNORADIUSDMG && !Source.bFORCERADIUSDMG )
continue;
// check the DONTHARMCLASS/DONTHARMSPECIES flags
if ( !a.player && ((Source.bDONTHARMCLASS && (a.GetClass() == Source.GetClass())) || (Source.bDONTHARMSPECIES && (a.GetSpecies() == Source.GetSpecies()))) )
continue;
// check friendliness
if ( (flags&DE_NOHURTFRIEND) && Instigator && Instigator.IsFriend(a) )
continue;
// can we see it
if ( !(flags&DE_THRUWALLS) && !Source.CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) )
continue;
// intersecting?
if ( !SphereIntersect(a,Source.pos,ExplosionRadius) )
continue;
// calculate factor
Vector3 dir;
if ( flags&DE_CENTERHEIGHT ) dir = level.Vec3Diff(Source.Vec3Offset(0,0,Source.Height/2),a.Vec3Offset(0,0,a.Height/2));
else dir = level.Vec3Diff(Source.pos,a.Vec3Offset(0,0,a.Height/2));
double dist = dir.length();
// intersecting, randomize direction
if ( dir.length() <= double.epsilon )
{
double ang = FRandom[DoBlast](0,360);
double pt = FRandom[DoBlast](-90,90);
dir = Vec3FromAngles(ang,pt);
}
else dir /= dist;
dist = clamp(dist-FullDamageRadius,0,min(dist,ExplosionRadius));
double damagescale;
if ( ExplosionRadius == FullDamageRadius ) damagescale = 1.;
else damagescale = 1.-clamp((dist-a.Radius)*brange,0.,1.);
double mm = MomentumTransfer*damagescale;
// no knockback if massive/unpushable
if ( (abs(mm) > 0.) && !a.bDORMANT && !a.bDONTTHRUST && (a.Mass < Actor.LARGE_MASS) )
{
Vector3 Momentum = dir*mm;
if ( (a.pos.z <= a.floorz) || !a.TestMobjZ() )
Momentum.z = max(Momentum.z,(flags&DE_EXTRAZTHRUST?.4:.1)*Momentum.length());
Momentum /= GameTicRate*max(50,a.Mass); // prevent tiny things from getting yeeted at warp speed
a.vel += Momentum;
if ( (flags&DE_BLAST) && a.bCANBLAST && !a.bDONTBLAST ) a.bBLASTED = true;
}
// hit it
bool inactive = (!a.player&&!a.target);
bool hostile = (Instigator&&a.IsHostile(Instigator)&&(a.bISMONSTER||a.player));
if ( (!(flags&DE_COUNTENEMIES) || hostile) && (!(flags&DE_COUNTSTEALTH) || inactive) ) nhit++;
int dmg = int(Damage*damagescale);
if ( flags&DE_QUADRAVOL )
{
OnFire.Apply(a,Instigator,dmg); // ignite
continue;
}
if ( dmg <= 0 ) continue; // no harm
int oldhp = a.Health;
int basehp = a.GetSpawnHealth();
int ndmg = a.DamageMobj(realinflictor?realinflictor:Source,Instigator,dmg,(DamageType=='')?Source.DamageType:DamageType,dflg,atan2(-dir.y,-dir.x));
if ( (ndmg > 0) && a && !(flags&DE_NOBLEED) ) a.TraceBleed(ndmg,Source);
if ( (flags&DE_HOWL) && a && (a.Health > 0) && a.bISMONSTER && !Random[DoBlast](0,3) ) a.Howl();
if ( hostile && (!a || (a.Health <= 0)) ) haskilled = true;
if ( (flags&DE_COUNTFHKILLS) && (oldhp < basehp) ) continue; // was not at full health
if ( (!a || (a.Health <= 0)) && (!(flags&DE_COUNTENEMIES) || hostile) && (!(flags&DE_COUNTSTEALTH) || inactive) ) nkill++;
}
}
if ( (Instigator is 'Demolitionist') && haskilled && !(flags&DE_NONEXPLOSIVE) )
{
let hnd = SWWMHandler(EventHandler.Find("SWWMHandler"));

View file

@ -111,11 +111,9 @@ Class ParryField : SWWMNonInteractiveActor
}
SetOrigin(SWWMUtility.GetFireOffset(master,20,0,0),false);
let raging = RagekitPower(master.FindInventory("RagekitPower"));
let s = Demolitionist(master).mystats;
let st = Demolitionist(master).mystats;
// check for projectiles to deflect
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a = Actor(ti.Next()) )
foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext )
{
if ( (justparried.Find(a) < justparried.Size()) || !(SWWMUtility.ValidProjectile(a) || a.bSKULLFLY) || a.bTHRUACTORS || (level.Vec3Diff(a.pos,pos).length() > 80) ) continue;
if ( a is 'Whirlwind' ) SWWMUtility.MarkAchievement("tornado",master.player);
@ -179,13 +177,13 @@ Class ParryField : SWWMNonInteractiveActor
if ( !critsnd )
{
A_StartSound("misc/soulsparry",CHAN_ITEM,CHANF_OVERLAP,1.,.5);
if ( s ) s.pparries++;
if ( st ) st.pparries++;
}
critsnd = true;
if ( (a is 'LostSoul') && (master.player.ReadyWeapon is 'SilverBullet') )
SWWMUtility.MarkAchievement("baseball",master.player);
}
if ( s ) s.parries++;
if ( st ) st.parries++;
SWWMUtility.AchievementProgressInc("parry",1,master.player);
}
if ( --special1 <= 0 ) Destroy();

View file

@ -266,14 +266,18 @@ Class HellblazerMissile : Actor
return;
}
// proximity check
let bt = BlockThingsIterator.Create(self,200);
while ( bt.Next() )
bool checked = false;
foreach ( s:level.Sectors )
{
let t = bt.Thing;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || !SWWMUtility.SphereIntersect(t,level.Vec3Offset(pos,vel),bNOGRAVITY?50:90) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
deto++;
tracer = t;
break;
for ( Actor t=s.thinglist; t; t=t.snext )
{
if ( !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || !SWWMUtility.SphereIntersect(t,level.Vec3Offset(pos,vel),bNOGRAVITY?50:90) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
deto++;
tracer = t;
checked = true;
break;
}
if ( checked ) break;
}
}

View file

@ -465,13 +465,11 @@ Class YnykronImpact : SWWMNonInteractiveActor
let s = Spawn("YnykronImpactArm",pos);
s.target = target;
}
let bt = BlockThingsIterator.Create(self,rad+200);
Array<Actor> candidates;
candidates.Clear();
while ( bt.Next() )
foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext )
{
let t = bt.Thing;
if ( !t || !t.bSHOOTABLE || !SWWMUtility.SphereIntersect(t,pos,rad) || (!SWWMUtility.SphereIntersect(t,pos,100) && !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) ) continue;
if ( !t.bSHOOTABLE || !SWWMUtility.SphereIntersect(t,pos,rad) || (!SWWMUtility.SphereIntersect(t,pos,100) && !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) ) continue;
if ( YnykronShot(master) && (YnykronShot(master).hitlist.Find(t) < YnykronShot(master).hitlist.Size()) )
continue;
Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.Height/2));

View file

@ -211,10 +211,8 @@ Class DeepImpact : SWWMWeapon
let p = SWWMPuff.Setup(avgpos,avgdir,invoker,self,l.a);
l.a.DamageMobj(p,self,int(dmg/250.),'Push',DMG_THRUSTLESS|DMG_INFLICTOR_IS_PUFF);
}
let ti = ThinkerIterator.Create("Actor");
Actor m;
let s = Demolitionist(self).mystats;
while ( m = Actor(ti.Next()) )
let st = Demolitionist(self).mystats;
foreach ( s:level.Sectors ) for ( Actor m=s.thinglist; m; m=m.snext )
{
if ( !SWWMUtility.ValidProjectile(m) ) continue;
Vector3 rdir = level.Vec3Diff(origin,m.pos);
@ -235,7 +233,7 @@ Class DeepImpact : SWWMWeapon
pb.AttachToOwner(m);
pb.bAMBUSH = true;
}
if ( s ) s.parries++;
if ( st ) st.parries++;
SWWMUtility.AchievementProgressInc("parry",1,player);
}
int numpt = Random[Impact](7,12);

View file

@ -192,12 +192,10 @@ Class BigBiospark : Actor
if ( !(special2%5) )
{
double closest = double.infinity;
let bt = BlockThingsIterator.Create(self,8000);
while ( bt.Next() )
foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext )
{
let t = bt.Thing;
double dist;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || ((dist=Distance3DSquared(t)) > 64000000) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
if ( !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || ((dist=Distance3DSquared(t)) > 64000000) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.height/2)).unit();
if ( dist > closest ) continue;
closest = dist;
@ -222,11 +220,9 @@ Class BigBiospark : Actor
angle = atan2(dir.y,dir.x);
pitch = asin(-dir.z);
// deal (proper) radius damage
let bt2 = BlockThingsIterator.Create(self,500);
while ( bt2.Next() )
foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext )
{
let t = bt2.Thing;
if ( !t || !t.bSHOOTABLE ) continue;
if ( !t.bSHOOTABLE ) continue;
if ( SWWMUtility.SphereIntersect(t,pos,40) )
{
t.DamageMobj(self,target,4+special1,'Biospark');
@ -465,12 +461,10 @@ Class BiosparkBall : Actor
if ( !(special2%5) )
{
double closest = double.infinity;
let bt = BlockThingsIterator.Create(self,500);
while ( bt.Next() )
foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext )
{
let t = bt.Thing;
double dist;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || ((dist=Distance3DSquared(t)) > 250000) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
if ( !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || ((dist=Distance3DSquared(t)) > 250000) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.height/2)).unit();
if ( dir dot dirto < .5 ) continue; // don't seek stuff that's behind us
if ( dist > closest ) continue;
@ -497,13 +491,17 @@ Class BiosparkBall : Actor
return;
}
// proximity check
let bt = BlockThingsIterator.Create(self,100);
while ( bt.Next() )
bool checked = false;
foreach ( s:level.Sectors )
{
let t = bt.Thing;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain')) || (t.Health <= 0) || (target && t.IsFriend(target)) || !SWWMUtility.SphereIntersect(t,level.Vec3Offset(pos,vel),16) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
deto++;
break;
for ( Actor t=s.thinglist; t; t=t.snext )
{
if ( !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain')) || (t.Health <= 0) || (target && t.IsFriend(target)) || !SWWMUtility.SphereIntersect(t,level.Vec3Offset(pos,vel),16) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
deto++;
checked = true;
break;
}
if ( checked ) break;
}
}
override void OnDestroy()
@ -1055,12 +1053,10 @@ Class BiosparkBeam : SWWMNonInteractiveActor
}
nextpos = t.Results.HitPos;
double closest = double.infinity;
let bt = BlockThingsIterator.Create(self,500);
while ( bt.Next() )
foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext )
{
let t = bt.Thing;
double dist;
if ( !t || (!(t is 'BiosparkHitbox') && (!t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)))) || ((dist=Distance3DSquared(t)) > 250000) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
if ( (!(t is 'BiosparkHitbox') && (!t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)))) || ((dist=Distance3DSquared(t)) > 250000) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
Vector3 dirto = level.Vec3Diff(nextpos,t.Vec3Offset(0,0,t.height/2));
if ( dir dot dirto < .2 ) continue;
if ( dist > closest ) continue;
@ -1362,12 +1358,10 @@ Class BiosparkArc : SWWMNonInteractiveActor
else
{
double closest = double.infinity;
let bt = BlockThingsIterator.Create(self,1500);
while ( bt.Next() )
foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext )
{
let t = bt.Thing;
double dist;
if ( !t || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || ((dist=Distance3DSquared(t)) > 2250000) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
if ( !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain') && !t.player) || (t.Health <= 0) || (target && t.IsFriend(target)) || ((dist=Distance3DSquared(t)) > 2250000) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue;
Vector3 dirto = level.Vec3Diff(nextpos,t.Vec3Offset(0,0,t.height/2));
if ( dir dot dirto < .2 ) continue;
if ( dist > closest ) continue;

View file

@ -245,15 +245,20 @@ Class ExplodiumMagHitbox : Actor
return;
}
SetOrigin(target.Vec3Offset(0,0,-height*.5),false);
let bt = BlockThingsIterator.Create(self,128);
while ( bt.Next() )
bool breakout = false;
foreach ( s:level.Sectors )
{
if ( !bt.Thing || (bt.Thing == self) || !bt.Thing.bSHOOTABLE || (bt.Thing == target.target) || bt.Thing.IsFriend(target.target) || !SWWMUtility.BoxIntersect(self,bt.Thing) )
continue;
target.bKILLED = true;
target.SetStateLabel("Detonate");
Destroy();
break;
for ( Actor t=s.thinglist; t; t=t.snext )
{
if ( (t == self) || !t.bSHOOTABLE || (t == target.target) || t.IsFriend(target.target) || !SWWMUtility.BoxIntersect(self,t) )
continue;
target.bKILLED = true;
target.SetStateLabel("Detonate");
Destroy();
breakout = true;
break;
}
if ( breakout ) break;
}
}
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )