diff --git a/language.version b/language.version index caf47edea..97a60dcc9 100644 --- a/language.version +++ b/language.version @@ -1,3 +1,3 @@ [default] -SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r835 \cu(Sat 29 Jul 13:15:34 CEST 2023)\c-"; -SWWM_SHORTVER="\cw1.3pre r835 \cu(2023-07-29 13:15:34)\c-"; +SWWM_MODVER="\cyDEMOLITIONIST \cw1.3pre r834 \cu(Sat 29 Jul 12:25:53 CEST 2023)\c-"; +SWWM_SHORTVER="\cw1.3pre r834 \cu(2023-07-29 12:25:53)\c-"; diff --git a/zscript/dlc1/swwm_mister_fx.zsc b/zscript/dlc1/swwm_mister_fx.zsc index aaa61cd81..2ede28567 100644 --- a/zscript/dlc1/swwm_mister_fx.zsc +++ b/zscript/dlc1/swwm_mister_fx.zsc @@ -1036,18 +1036,14 @@ Class MisterGrenade : Actor if ( bNoProx ) return; // "safe delay" for main grenade if ( !bAMBUSH && (ReactionTime > default.ReactionTime-20) ) return; - bool breakout = false; - foreach ( s:level.Sectors ) + let bt = BlockThingsIterator.Create(self,120); + while ( bt.Next() ) { - 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; + 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; } } @@ -1133,11 +1129,13 @@ Class MisterGrenade : Actor } // gather seekable targets Array candidates; + let bt = BlockThingsIterator.Create(self,2000); let tt = new("TargetTracer"); tt.target = target; - foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext ) + while ( bt.Next() ) { - if ( (t == tracer) || !t.bSHOOTABLE || (!t.bISMONSTER && !(t is 'BossBrain')) || (t.Health <= 0) || (target && t.IsFriend(target)) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue; + 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; // 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 diff --git a/zscript/handler/swwm_handler_debugrender.zsc b/zscript/handler/swwm_handler_debugrender.zsc index e6f9de77b..372bd47b3 100644 --- a/zscript/handler/swwm_handler_debugrender.zsc +++ b/zscript/handler/swwm_handler_debugrender.zsc @@ -3,6 +3,7 @@ extend Class SWWMHandler { ui SWWMProjectionData projdata; + transient ui ThinkerIterator dbgti; private ui void DrawWorldLine( RenderEvent e, Vector3 apos, Vector3 bpos, Color col ) { @@ -102,7 +103,10 @@ 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); - foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext ) + if ( !dbgti ) dbgti = ThinkerIterator.Create("Actor"); + else dbgti.Reinit(); + Actor a; + while ( a = Actor(dbgti.Next()) ) { if ( (a == players[consoleplayer].Camera) && !(players[consoleplayer].cheats&CF_CHASECAM) ) continue; if ( a.bINVISIBLE && !(a is 'DynamicLight') ) continue; diff --git a/zscript/handler/swwm_handler_worldthings.zsc b/zscript/handler/swwm_handler_worldthings.zsc index 036679120..ef7ef9189 100644 --- a/zscript/handler/swwm_handler_worldthings.zsc +++ b/zscript/handler/swwm_handler_worldthings.zsc @@ -178,18 +178,14 @@ extend Class SWWMHandler // copies the floatbob of overlapping identical items, so it doesn't look weird private void CopyFloatBob( Actor a ) { - bool breakout = false; - foreach ( s:level.Sectors ) + let bt = BlockThingsIterator.Create(a,16); + while ( bt.Next() ) { - 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; + 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; } } diff --git a/zscript/handler/swwm_handler_worldtick.zsc b/zscript/handler/swwm_handler_worldtick.zsc index a1b2f3a70..75519cb01 100644 --- a/zscript/handler/swwm_handler_worldtick.zsc +++ b/zscript/handler/swwm_handler_worldtick.zsc @@ -98,9 +98,11 @@ 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; - // 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 ) + while ( a = Actor(cti.Next()) ) { if ( !a.player && !a.bISMONSTER ) continue; // ignore the dead @@ -204,12 +206,12 @@ extend Class SWWMHandler } mapclearagain++; if ( !iwantdie ) return; - for ( int i=0; i 0 ) return; + if ( s.deaths > 0 ) + return; } SWWMUtility.MarkAchievement("wantdie",players[consoleplayer]); } @@ -277,9 +279,11 @@ 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 - foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext ) + BlockThingsIterator bt = BlockThingsIterator.Create(players[consoleplayer].Camera,viewdist); + while ( bt.Next() ) { + 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; @@ -321,15 +325,19 @@ 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 viewdist ) continue; diff --git a/zscript/items/swwm_ammoextra.zsc b/zscript/items/swwm_ammoextra.zsc index f45f5e642..a4130b16c 100644 --- a/zscript/items/swwm_ammoextra.zsc +++ b/zscript/items/swwm_ammoextra.zsc @@ -523,19 +523,15 @@ 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; - foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; ) + while ( bt.Next() ) { - let next = t.snext; - if ( (t == self) || !(t is 'HammerspaceEmbiggener') || !(t.spawnpoint ~== spawnpoint) ) - { - t = next; - continue; - } + let t = bt.Thing; + if ( !t || (t == self) || !(t is 'HammerspaceEmbiggener') || !(t.spawnpoint ~== spawnpoint) ) continue; tamount += HammerspaceEmbiggener(t).Amount; t.ClearCounters(); t.Destroy(); - t = next; } if ( tamount <= 1 ) return; tamount -= tamount%2; // always even numbered diff --git a/zscript/items/swwm_lamp.zsc b/zscript/items/swwm_lamp.zsc index 16e2c66b3..3b3c892bf 100644 --- a/zscript/items/swwm_lamp.zsc +++ b/zscript/items/swwm_lamp.zsc @@ -58,10 +58,12 @@ Class LampMoth : Actor if ( !lamp ) { // look for nearby lamps + let bi = BlockThingsIterator.Create(self,250); double mindist = 62500.; - foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext ) + while ( bi.Next() ) { - if ( !(a is 'CompanionLamp') ) continue; + if ( !bi.Thing || !(bi.Thing is 'CompanionLamp') ) continue; + Actor a = bi.Thing; double dist = Distance3DSquared(a); if ( (a.frame == 0) || (dist > mindist) && !CheckSight(a,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) continue; mindist = dist; diff --git a/zscript/items/swwm_powerups.zsc b/zscript/items/swwm_powerups.zsc index c8cf7eb11..2fbe1f8db 100644 --- a/zscript/items/swwm_powerups.zsc +++ b/zscript/items/swwm_powerups.zsc @@ -310,9 +310,11 @@ Class GhostTarget : Actor } if ( isFrozen() ) return; if ( diedie ) A_FadeOut(.05); - foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext ) + let bt = BlockThingsIterator.Create(self,300); + while ( bt.Next() ) { - if ( !t.bIsMonster || t.player || !t.IsHostile(master) || (t.target != self) ) continue; + let t = bt.Thing; + if ( !t || !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 diff --git a/zscript/items/swwm_powerups_vip.zsc b/zscript/items/swwm_powerups_vip.zsc index 5f6aa0799..082a8930a 100644 --- a/zscript/items/swwm_powerups_vip.zsc +++ b/zscript/items/swwm_powerups_vip.zsc @@ -510,7 +510,9 @@ Class Mykradvo : Inventory { targets.Clear(); // search all actively hostile enemies within 50m - foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext ) + let ti = ThinkerIterator.Create("Actor"); + Actor a; + while ( a=Actor(ti.Next()) ) { // must be an active, shootable live monster if ( !a.bISMONSTER || !a.bSHOOTABLE || a.bDORMANT || (a.Health <= 0) ) continue; diff --git a/zscript/player/swwm_player_tick.zsc b/zscript/player/swwm_player_tick.zsc index bfd978895..2bf04bc32 100644 --- a/zscript/player/swwm_player_tick.zsc +++ b/zscript/player/swwm_player_tick.zsc @@ -5,15 +5,18 @@ extend Class Demolitionist { if ( player.cmd.buttons&BT_USER3 ) { - foreach ( s:level.Sectors ) for ( Actor i=s.thinglist; i; i=i.snext ) + let bt = BlockThingsIterator.Create(self,800); + while ( bt.Next() ) { - if ( !(i is 'Inventory') && !(i is 'Chancebox') && !(i is 'SWWMRespawnTimer') ) continue; + let i = bt.Thing; + if ( !i || (!(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; @@ -50,9 +53,11 @@ extend Class Demolitionist } if ( (magitem_cnt < 8) && !swwm_usetopickup ) { - foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext ) + let bt = BlockThingsIterator.Create(self,500); + while ( bt.Next() ) { - if ( !(t is 'Inventory') || !t.bSPECIAL || !t.bDROPPED || t.bINVISIBLE || Inventory(t).Owner || !SWWMUtility.SphereIntersect(t,pos,500) || !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) + 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) ) continue; let i = Inventory(t); Class cls = i.GetClass(); @@ -82,6 +87,7 @@ extend Class Demolitionist magitem = nmi; magitem_cnt++; } + bt.Destroy(); } SWWMMagItem itm = magitem; SWWMMagItem prev = null, next; @@ -523,13 +529,14 @@ 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; - foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext ) + while ( (spd > 0) && bi.Next() ) { - if ( spd <= 0 ) break; - if ( (a == self) || (!a.bSOLID && !a.bSHOOTABLE) || a.bTHRUACTORS || a.bCORPSE || !CanCollideWith(a,false) || !a.CanCollideWith(self,true) ) continue; + a = bi.Thing; + if ( !a || (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); diff --git a/zscript/utility/swwm_utility_blast.zsc b/zscript/utility/swwm_utility_blast.zsc index 1eed42a3b..494bc163e 100644 --- a/zscript/utility/swwm_utility_blast.zsc +++ b/zscript/utility/swwm_utility_blast.zsc @@ -63,12 +63,17 @@ 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; - foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext ) + Array washit; + washit.Clear(); + while ( bi.Next() ) { + Actor a = bi.Thing; + washit.Push(a); // early checks for self and ignored actor (usually the instigator) - if ( (a == ignoreme) || (a == Source) ) + if ( !a || (a == ignoreme) || (a == Source) ) continue; // can't be affected if ( !a.bSHOOTABLE && !a.bVULNERABLE ) @@ -136,6 +141,90 @@ 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 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")); diff --git a/zscript/weapons/swwm_baseweapon_melee.zsc b/zscript/weapons/swwm_baseweapon_melee.zsc index 6ef9ddcf1..5a388405c 100644 --- a/zscript/weapons/swwm_baseweapon_melee.zsc +++ b/zscript/weapons/swwm_baseweapon_melee.zsc @@ -111,9 +111,11 @@ Class ParryField : SWWMNonInteractiveActor } SetOrigin(SWWMUtility.GetFireOffset(master,20,0,0),false); let raging = RagekitPower(master.FindInventory("RagekitPower")); - let st = Demolitionist(master).mystats; + let s = Demolitionist(master).mystats; // check for projectiles to deflect - foreach ( s:level.Sectors ) for ( Actor a=s.thinglist; a; a=a.snext ) + let ti = ThinkerIterator.Create("Actor"); + Actor a; + while ( a = Actor(ti.Next()) ) { 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); @@ -177,13 +179,13 @@ Class ParryField : SWWMNonInteractiveActor if ( !critsnd ) { A_StartSound("misc/soulsparry",CHAN_ITEM,CHANF_OVERLAP,1.,.5); - if ( st ) st.pparries++; + if ( s ) s.pparries++; } critsnd = true; if ( (a is 'LostSoul') && (master.player.ReadyWeapon is 'SilverBullet') ) SWWMUtility.MarkAchievement("baseball",master.player); } - if ( st ) st.parries++; + if ( s ) s.parries++; SWWMUtility.AchievementProgressInc("parry",1,master.player); } if ( --special1 <= 0 ) Destroy(); diff --git a/zscript/weapons/swwm_blazeit_fx.zsc b/zscript/weapons/swwm_blazeit_fx.zsc index cf47b3ad2..ab62e2b7a 100644 --- a/zscript/weapons/swwm_blazeit_fx.zsc +++ b/zscript/weapons/swwm_blazeit_fx.zsc @@ -266,18 +266,14 @@ Class HellblazerMissile : Actor return; } // proximity check - bool checked = false; - foreach ( s:level.Sectors ) + let bt = BlockThingsIterator.Create(self,200); + while ( bt.Next() ) { - 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; + 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; } } diff --git a/zscript/weapons/swwm_deathlydeathcannon_fx.zsc b/zscript/weapons/swwm_deathlydeathcannon_fx.zsc index 54dedc63c..45accfc6a 100644 --- a/zscript/weapons/swwm_deathlydeathcannon_fx.zsc +++ b/zscript/weapons/swwm_deathlydeathcannon_fx.zsc @@ -465,11 +465,13 @@ Class YnykronImpact : SWWMNonInteractiveActor let s = Spawn("YnykronImpactArm",pos); s.target = target; } + let bt = BlockThingsIterator.Create(self,rad+200); Array candidates; candidates.Clear(); - foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext ) + while ( bt.Next() ) { - if ( !t.bSHOOTABLE || !SWWMUtility.SphereIntersect(t,pos,rad) || (!SWWMUtility.SphereIntersect(t,pos,100) && !CheckSight(t,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY)) ) continue; + 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 ( YnykronShot(master) && (YnykronShot(master).hitlist.Find(t) < YnykronShot(master).hitlist.Size()) ) continue; Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.Height/2)); diff --git a/zscript/weapons/swwm_deepdarkimpact.zsc b/zscript/weapons/swwm_deepdarkimpact.zsc index fdf2f529b..fadd3737c 100644 --- a/zscript/weapons/swwm_deepdarkimpact.zsc +++ b/zscript/weapons/swwm_deepdarkimpact.zsc @@ -211,8 +211,10 @@ 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 st = Demolitionist(self).mystats; - foreach ( s:level.Sectors ) for ( Actor m=s.thinglist; m; m=m.snext ) + let ti = ThinkerIterator.Create("Actor"); + Actor m; + let s = Demolitionist(self).mystats; + while ( m = Actor(ti.Next()) ) { if ( !SWWMUtility.ValidProjectile(m) ) continue; Vector3 rdir = level.Vec3Diff(origin,m.pos); @@ -233,7 +235,7 @@ Class DeepImpact : SWWMWeapon pb.AttachToOwner(m); pb.bAMBUSH = true; } - if ( st ) st.parries++; + if ( s ) s.parries++; SWWMUtility.AchievementProgressInc("parry",1,player); } int numpt = Random[Impact](7,12); diff --git a/zscript/weapons/swwm_sparkyboi_fx.zsc b/zscript/weapons/swwm_sparkyboi_fx.zsc index 637fe9e30..f033fbe1c 100644 --- a/zscript/weapons/swwm_sparkyboi_fx.zsc +++ b/zscript/weapons/swwm_sparkyboi_fx.zsc @@ -192,10 +192,12 @@ Class BigBiospark : Actor if ( !(special2%5) ) { double closest = double.infinity; - foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext ) + let bt = BlockThingsIterator.Create(self,8000); + while ( bt.Next() ) { + let t = bt.Thing; double dist; - 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; + 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; Vector3 dirto = level.Vec3Diff(pos,t.Vec3Offset(0,0,t.height/2)).unit(); if ( dist > closest ) continue; closest = dist; @@ -220,9 +222,11 @@ Class BigBiospark : Actor angle = atan2(dir.y,dir.x); pitch = asin(-dir.z); // deal (proper) radius damage - foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext ) + let bt2 = BlockThingsIterator.Create(self,500); + while ( bt2.Next() ) { - if ( !t.bSHOOTABLE ) continue; + let t = bt2.Thing; + if ( !t || !t.bSHOOTABLE ) continue; if ( SWWMUtility.SphereIntersect(t,pos,40) ) { t.DamageMobj(self,target,4+special1,'Biospark'); @@ -461,10 +465,12 @@ Class BiosparkBall : Actor if ( !(special2%5) ) { double closest = double.infinity; - foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext ) + let bt = BlockThingsIterator.Create(self,500); + while ( bt.Next() ) { + let t = bt.Thing; double dist; - 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; + 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; 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; @@ -491,17 +497,13 @@ Class BiosparkBall : Actor return; } // proximity check - bool checked = false; - foreach ( s:level.Sectors ) + let bt = BlockThingsIterator.Create(self,100); + while ( bt.Next() ) { - 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; + 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; } } override void OnDestroy() @@ -1053,10 +1055,12 @@ Class BiosparkBeam : SWWMNonInteractiveActor } nextpos = t.Results.HitPos; double closest = double.infinity; - foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext ) + let bt = BlockThingsIterator.Create(self,500); + while ( bt.Next() ) { + let t = bt.Thing; double dist; - 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; + 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; Vector3 dirto = level.Vec3Diff(nextpos,t.Vec3Offset(0,0,t.height/2)); if ( dir dot dirto < .2 ) continue; if ( dist > closest ) continue; @@ -1358,10 +1362,12 @@ Class BiosparkArc : SWWMNonInteractiveActor else { double closest = double.infinity; - foreach ( s:level.Sectors ) for ( Actor t=s.thinglist; t; t=t.snext ) + let bt = BlockThingsIterator.Create(self,1500); + while ( bt.Next() ) { + let t = bt.Thing; double dist; - 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; + 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; Vector3 dirto = level.Vec3Diff(nextpos,t.Vec3Offset(0,0,t.height/2)); if ( dir dot dirto < .2 ) continue; if ( dist > closest ) continue; diff --git a/zscript/weapons/swwm_splode_fx.zsc b/zscript/weapons/swwm_splode_fx.zsc index 57f0e3ca9..e9217a24c 100644 --- a/zscript/weapons/swwm_splode_fx.zsc +++ b/zscript/weapons/swwm_splode_fx.zsc @@ -245,20 +245,15 @@ Class ExplodiumMagHitbox : Actor return; } SetOrigin(target.Vec3Offset(0,0,-height*.5),false); - bool breakout = false; - foreach ( s:level.Sectors ) + let bt = BlockThingsIterator.Create(self,128); + while ( bt.Next() ) { - 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; + 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; } } override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )