So... Remember that one decision I made about avoiding BlockThingsIterator as much as possible? Turns out that was a stupid idea. There ARE situations where it's better to iterate sector thinglists, yes, especially for things that are NOT part of the blockmap, but in other cases, the excess allocations of new iterators are a reasonable price to pay for the lower perf impact in extreme cases, such as maps that have a gazillion sectors with gazillions of things in them (I'm looking at you, UDMF mappers). As a compromise, at least, in situations where the thinglists are needed, I did add a sort of micro-optimization by implementing code to check if a bounding box is inside a sector (would be nice if this was part of GZDoom itself, tho).
464 lines
11 KiB
Text
464 lines
11 KiB
Text
// Hellblazer projectiles and effects
|
|
|
|
Class HellblazerExplLight : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "HellExpl";
|
|
Args 0,0,0,200;
|
|
ReactionTime 25;
|
|
}
|
|
}
|
|
Class HellblazerSubExpl : SWWMNonInteractiveActor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Scale 2.2;
|
|
Alpha .75;
|
|
+FORCEXYBILLBOARD;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
roll = FRandom[ExploS](0,360);
|
|
scale.x *= RandomPick[ExploS](-1,1);
|
|
scale.y *= RandomPick[ExploS](-1,1);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XEX0 ABCDEFGHIJKLMNOPQRS 1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class HellblazerRing : SWWMNonInteractiveActor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Scale 2.;
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XRG3 ACEGIKMOQSUW 1 Bright A_SetScale(scale.x*1.15);
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class HellblazerArmTrail : SWWMNonInteractiveActor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
+FORCEXYBILLBOARD;
|
|
Scale 2.5;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XEX0 ACEGIKMOQS 1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class HellblazerArm : Actor
|
|
{
|
|
Default
|
|
{
|
|
Obituary "$O_HELLBLAZER";
|
|
DamageType 'Fire';
|
|
PROJECTILE;
|
|
+THRUACTORS;
|
|
+BOUNCEONWALLS;
|
|
+BOUNCEONFLOORS;
|
|
+BOUNCEONCEILINGS;
|
|
+CANBOUNCEWATER;
|
|
+NODAMAGETHRUST;
|
|
+FORCERADIUSDMG;
|
|
-NOGRAVITY;
|
|
+NOFRICTION;
|
|
Gravity 0.35;
|
|
BounceFactor 1.0;
|
|
Radius 4;
|
|
Height 4;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
reactiontime = Random[ExploS](6,8);
|
|
double ang, pt;
|
|
ang = FRandom[ExploS](0,360);
|
|
pt = FRandom[ExploS](-90,90);
|
|
vel = SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[ExploS](30.,40.);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1
|
|
{
|
|
Spawn("HellblazerArmTrail",pos);
|
|
SWWMUtility.DoExplosion(self,6+reactiontime/2,3000+500*reactiontime,80+10*reactiontime);
|
|
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](1,5);
|
|
let s = Spawn("SWWMHalfSmoke",pos);
|
|
s.vel = pvel+vel*.2;
|
|
s.SetShade(Color(4,2,1)*Random[ExploS](48,63));
|
|
s.special1 = Random[ExploS](0,2);
|
|
s.scale *= 3.;
|
|
s.alpha *= 0.1+.4*(ReactionTime/8.);
|
|
A_CountDown();
|
|
}
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class HellblazerTrail : SWWMNonInteractiveActor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Scale .2;
|
|
Alpha .3;
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
SetState(FindState("Spawn")+Random[ExploS](0,7));
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
JFLR ABCDEFGH 1 Bright
|
|
{
|
|
A_FadeOut(.06);
|
|
A_SetScale(scale.x*.95);
|
|
if ( waterlevel > 0 )
|
|
{
|
|
let b = Spawn("SWWMBubble",pos);
|
|
b.vel = vel;
|
|
b.scale *= scale.x;
|
|
Destroy();
|
|
}
|
|
}
|
|
Loop;
|
|
}
|
|
}
|
|
|
|
Class HellblazerFlare : SWWMNonInteractiveActor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
if ( !master || !master.bMISSILE )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
Vector3 traildir = -SWWMUtility.Vec3FromAngles(master.angle,master.pitch);
|
|
SetOrigin(level.Vec3Offset(master.pos,traildir*3),true);
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
HFLR A -1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// rockets
|
|
Class HellblazerMissile : Actor
|
|
{
|
|
Mixin SWWMMissileFix;
|
|
|
|
int deto, detact;
|
|
Actor seektarget;
|
|
Vector3 InitialDir, Acceleration;
|
|
|
|
Default
|
|
{
|
|
Obituary "$O_HELLBLAZER";
|
|
DamageType 'Sydon';
|
|
Radius 2;
|
|
Height 4;
|
|
Speed 50;
|
|
PROJECTILE;
|
|
+EXPLODEONWATER;
|
|
+FORCERADIUSDMG;
|
|
+NODAMAGETHRUST;
|
|
+HITTRACER;
|
|
+SKYEXPLODE;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
if ( bNOGRAVITY )
|
|
{
|
|
A_StartSound("hellblazer/fly",CHAN_BODY,CHANF_LOOP,1.,2.);
|
|
let t = Spawn("HellblazerFlare",pos);
|
|
t.master = self;
|
|
}
|
|
}
|
|
|
|
void A_BlazerTick( Color smokecol )
|
|
{
|
|
Vector3 traildir = -SWWMUtility.Vec3FromAngles(angle,pitch);
|
|
for ( int i=0; i<3; i++ )
|
|
{
|
|
let s = Spawn("SWWMHalfSmoke",level.Vec3Offset(pos,traildir*3));
|
|
s.SetShade(smokecol*Random[Hellblazer](48,63));
|
|
s.scale *= FRandom[Hellblazer](.8,1.2);
|
|
s.special1 = Random[Hellblazer](0,2);
|
|
s.alpha *= .5;
|
|
s.vel = .3*vel + (traildir+SWWMUtility.Vec3FromAngles(FRandom[Hellblazer](0,360),FRandom[Hellblazer](-90,90))*.4).unit()*FRandom[Hellblazer](1.,2.);
|
|
}
|
|
if ( bNOGRAVITY )
|
|
{
|
|
for ( double i=0.; i<vel.length(); i+= 5. )
|
|
{
|
|
let t = Spawn("HellblazerTrail",level.Vec3Offset(pos,-vel.unit()*i+traildir*3));
|
|
t.vel = traildir*4.;
|
|
}
|
|
if ( InitialDir.length() < double.epsilon ) InitialDir = vel.unit();
|
|
vel += Acceleration/GameTicRate;
|
|
Vector3 dir = vel.unit();
|
|
if ( vel.length() > 50. ) vel = dir*50.;
|
|
}
|
|
// don't auto-detonate if we're too close to our shooter
|
|
// ensures player doesn't eat up the full brunt of the blast
|
|
if ( (deto > 1) && (!target || !SWWMUtility.SphereIntersect(target,pos,150)) )
|
|
{
|
|
ExplodeMissile();
|
|
return;
|
|
}
|
|
if ( seektarget )
|
|
{
|
|
// seek tracer
|
|
if ( SWWMUtility.SphereIntersect(seektarget,level.Vec3Offset(pos,vel),bNOGRAVITY?50:90) )
|
|
{
|
|
deto++;
|
|
tracer = seektarget;
|
|
return;
|
|
}
|
|
if ( bNOGRAVITY )
|
|
{
|
|
Vector3 SeekingDir = level.Vec3Diff(pos,seektarget.Vec3Offset(0,0,seektarget.height/2)).unit();
|
|
if ( SeekingDir dot InitialDir > -1 )
|
|
{
|
|
double MagnitudeVel = Vel.length();
|
|
SeekingDir = (SeekingDir*.25*MagnitudeVel+Vel).unit();
|
|
Vel = MagnitudeVel*SeekingDir;
|
|
Acceleration = 25*SeekingDir;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
// proximity check
|
|
let bt = BlockThingsIterator.Create(self,256);
|
|
while ( bt.Next() )
|
|
{
|
|
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;
|
|
}
|
|
bt.Destroy();
|
|
}
|
|
|
|
void A_BlazerMissileExplode()
|
|
{
|
|
bForceXYBillboard = true;
|
|
bRollSprite = false;
|
|
A_SetRenderStyle(1.0,STYLE_Add);
|
|
A_SprayDecal("BigRocketBlast",50);
|
|
A_SetScale(4.5);
|
|
SWWMUtility.DoExplosion(self,200,320000,150,100);
|
|
A_NoGravity();
|
|
A_QuakeEx(5.,5.,5.,15,0,1500,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,falloff:500,rollIntensity:.8);
|
|
A_StopSound(CHAN_BODY);
|
|
A_StartSound("hellblazer/hit",CHAN_WEAPON,attenuation:.8);
|
|
A_StartSound("hellblazer/hit",CHAN_VOICE,attenuation:.5);
|
|
A_AlertMonsters(swwm_uncapalert?0:2500,AMF_EMITFROMTARGET);
|
|
Spawn("HellblazerExplLight",pos);
|
|
int numpt = Random[Hellblazer](12,24);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[Hellblazer](0,360),FRandom[Hellblazer](-90,90))*FRandom[Hellblazer](2,8);
|
|
let s = Spawn("SWWMSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(4,2,1)*Random[Hellblazer](48,63));
|
|
s.special1 = Random[Hellblazer](1,4);
|
|
s.scale *= 2.8;
|
|
s.alpha *= .4;
|
|
}
|
|
numpt = Random[Hellblazer](8,12);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[Hellblazer](0,360),FRandom[Hellblazer](-90,90))*FRandom[Hellblazer](4,12);
|
|
let s = Spawn("SWWMSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Random[Hellblazer](12,16);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[Hellblazer](0,360),FRandom[Hellblazer](-90,90))*FRandom[Hellblazer](10,20);
|
|
let s = Spawn("SWWMChip",pos);
|
|
s.vel = pvel;
|
|
s.scale *= FRandom[Hellblazer](0.9,1.8);
|
|
}
|
|
Spawn("HellblazerRing",pos);
|
|
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,320,SWWMUtility.Vec3FromAngles(angle,pitch));
|
|
}
|
|
|
|
void A_SubExpl( double xscale = 1. )
|
|
{
|
|
if ( special1 < 15 ) SWWMUtility.DoExplosion(self,60-special1*4,3000-special1*200,100+special1*10,damagetype:'Fire');
|
|
special1++;
|
|
if ( (special1 <= 8) && !(special1%2) )
|
|
{
|
|
int numpt = Random[Hellblazer](0,8-special1);
|
|
double ang, pt;
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
ang = FRandom[Hellblazer](0,360);
|
|
pt = FRandom[Hellblazer](-90,90);
|
|
FLineTraceData d;
|
|
LineTrace(ang,FRandom[Hellblazer](10,20)+10*special1*xscale,pt,TRF_THRUACTORS,data:d);
|
|
Vector3 HitNormal = SWWMUtility.GetLineTraceHitNormal(d);
|
|
let p = Spawn("HellblazerSubExpl",d.HitLocation+hitnormal*4);
|
|
p.angle = atan2(hitnormal.y,hitnormal.x);
|
|
p.pitch = asin(-hitnormal.z);
|
|
p.target = target;
|
|
p.scale *= xscale*(2-special1*.1);
|
|
p.alpha *= 1-special1*.1;
|
|
}
|
|
}
|
|
special2++;
|
|
if ( (special2 <= 6) && !(special2%3) )
|
|
{
|
|
int numpt = 5-special2/2;
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
double ang = FRandom[Hellblazer](0,360);
|
|
double pt = FRandom[Hellblazer](-90,90);
|
|
let c = Spawn("HellblazerArm",pos);
|
|
c.angle = ang;
|
|
c.pitch = pt;
|
|
c.target = target;
|
|
}
|
|
}
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A 1 A_BlazerTick(Color(4,3,2));
|
|
Wait;
|
|
Death:
|
|
TNT1 A 0 A_BlazerMissileExplode();
|
|
HEXP ABCDEFGHIJKLMNOPQR 1 Bright A_SubExpl();
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// grenades
|
|
Class HellblazerMissile2 : HellblazerMissile
|
|
{
|
|
double rollvel, anglevel, pitchvel;
|
|
Vector3 oldvel;
|
|
|
|
Default
|
|
{
|
|
BounceFactor 1.;
|
|
WallBounceFactor 1.;
|
|
Gravity .35;
|
|
ReactionTime 85;
|
|
-NOGRAVITY;
|
|
-EXPLODEONWATER;
|
|
+USEBOUNCESTATE;
|
|
+BOUNCEONWALLS;
|
|
+BOUNCEONFLOORS;
|
|
+BOUNCEONCEILINGS;
|
|
+ALLOWBOUNCEONACTORS;
|
|
+DONTBOUNCEONSHOOTABLES;
|
|
+CANBOUNCEWATER;
|
|
+INTERPOLATEANGLES;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
rollvel = FRandom[Hellblazer](-4,4);
|
|
anglevel = FRandom[Hellblazer](-4,4);
|
|
pitchvel = FRandom[Hellblazer](-4,4);
|
|
oldvel = vel;
|
|
}
|
|
override void Tick()
|
|
{
|
|
oldvel = vel;
|
|
Super.Tick();
|
|
if ( isFrozen() || (freezetics > 0) || !InStateSequence(CurState,FindState("Spawn")) ) return;
|
|
roll += rollvel;
|
|
angle += anglevel;
|
|
pitch += pitchvel;
|
|
ReactionTime--;
|
|
if ( ReactionTime <= 0 ) ExplodeMissile();
|
|
}
|
|
virtual void A_HandleBounce()
|
|
{
|
|
Vector3 HitNormal = SWWMUtility.GetActorHitNormal(self);
|
|
// undo the bounce, we need to hook in our own
|
|
vel = oldvel;
|
|
// re-do the bounce with our formula
|
|
Vector3 RealHitNormal = HitNormal;
|
|
HitNormal = (HitNormal+SWWMUtility.Vec3FromAngles(FRandom[Hellblazer](0,360),FRandom[Hellblazer](-90,90))*.1).unit();
|
|
if ( (HitNormal dot RealHitNormal) < 0 ) HitNormal *= -.5;
|
|
vel = FRandom[Hellblazer](.8,.9)*((vel dot HitNormal)*HitNormal*(FRandom[Hellblazer](-2.,-1.6))+vel);
|
|
if ( vel.z > 10 ) vel.z = 0.5*(10+vel.z);
|
|
bHITOWNER = true;
|
|
if ( vel.length() < 4. )
|
|
{
|
|
ClearBounce();
|
|
ExplodeMissile();
|
|
return;
|
|
}
|
|
A_StartSound("hellblazer/bounce",CHAN_ITEM);
|
|
rollvel = FRandom[Hellblazer](-16,16);
|
|
anglevel = FRandom[Hellblazer](-16,16);
|
|
pitchvel = FRandom[Hellblazer](-16,16);
|
|
// steer towards seek target
|
|
if ( seektarget )
|
|
{
|
|
Vector3 dirto = level.Vec3Diff(pos,seektarget.Vec3Offset(0,0,seektarget.Height/2));
|
|
double distto = dirto.length();
|
|
if ( distto <= 0. ) return;
|
|
dirto /= distto;
|
|
dirto.z += .1;
|
|
double spd = vel.length();
|
|
if ( spd <= 0. ) return;
|
|
vel /= spd;
|
|
vel = (vel*.3+dirto*.7)*spd;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Bounce:
|
|
XZW1 A 0 A_HandleBounce();
|
|
Goto Spawn;
|
|
}
|
|
}
|