swwmgz_m/zscript/swwm_common_fx.zsc

1806 lines
41 KiB
Text

// basic effects
// imitates UE1 light type LT_TexturePaletteOnce/LT_TexturePaletteLoop
Class PaletteLight : PointLight
{
Color pal[256];
bool IsLooping;
int InitialReactionTime;
Default
{
Tag "Explosion";
Args 0,0,0,80;
ReactionTime 15;
}
private void UpdateLight()
{
int index = clamp(255-((255*ReactionTime)/InitialReactionTime),0,255);
args[LIGHT_RED] = pal[index].r;
args[LIGHT_GREEN] = pal[index].g;
args[LIGHT_BLUE] = pal[index].b;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
String palname = GetTag();
int sep = palname.IndexOf(",");
int palnum = 0;
if ( sep != -1 )
{
String palnumstr = palname.Mid(sep+1);
palnum = palnumstr.ToInt()*768;
palname.Truncate(sep);
}
int lump = Wads.CheckNumForFullname(String.Format("palettes/%s.pal",palname));
String paldat = Wads.ReadLump(lump);
for ( int i=0; i<256; i++ )
{
pal[i].r = paldat.ByteAt(palnum++);
pal[i].g = paldat.ByteAt(palnum++);
pal[i].b = paldat.ByteAt(palnum++);
}
if ( ReactionTime < 0 )
{
ReactionTime = abs(ReactionTime)-1;
IsLooping = true;
}
InitialReactionTime = ReactionTime;
UpdateLight();
}
override void Tick()
{
Super.Tick();
if ( isFrozen() || (freezetics > 0) ) return;
ReactionTime--;
if ( ReactionTime < 0 )
{
if ( !IsLooping )
{
Destroy();
return;
}
else ReactionTime = abs(InitialReactionTime);
}
if ( target ) SetOrigin(target.pos,true);
UpdateLight();
}
}
// Generic smoke, lightweight tick
Class SWWMSmoke : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Shaded";
StencilColor "FFFFFF";
Speed 1;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
+ROLLCENTER;
Scale .3;
}
override void PostBeginPlay()
{
double ang, pt;
scale *= FRandom[Puff](.5,1.5);
alpha = min(1.,alpha*FRandom[Puff](.5,1.5));
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[Puff](.2,.8)*speed;
roll = Frandom[Puff](0,360);
scale.x *= RandomPick[Puff](-1,1);
scale.y *= RandomPick[Puff](-1,1);
}
override void Tick()
{
prev = pos; // for interpolation
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
vel *= .96;
vel.z += .01;
// linetrace-based movement (hopefully more reliable than traditional methods)
Vector3 dir = vel;
double spd = vel.length();
dir /= spd;
double dist = spd;
FLineTraceData d;
Vector3 newpos = pos;
newpos.z = clamp(newpos.z,floorz,ceilingz);
int nstep = 0;
while ( dist > 0 )
{
// safeguard, too many bounces
if ( nstep > MAXBOUNCEPERTIC )
{
Destroy();
return;
}
double ang = atan2(dir.y,dir.x);
double pt = asin(-dir.z);
LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d);
Vector3 hitnormal = -d.HitDir;
if ( d.HitType == TRACE_HitFloor )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
else hitnormal = d.HitSector.floorplane.Normal;
}
else if ( d.HitType == TRACE_HitCeiling )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
else hitnormal = d.HitSector.ceilingplane.Normal;
}
else if ( d.HitType == TRACE_HitWall )
{
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
if ( !d.LineSide ) hitnormal *= -1;
}
if ( d.HitType != TRACE_HitNone )
{
dist -= d.Distance;
// should only happen if we bounced
dir = d.HitDir-(FRandom[Puff](1.,1.2)*hitnormal*(d.HitDir dot hitnormal));
vel = dir*spd;
newpos = d.HitLocation+dir;
}
else
{
dist = 0.;
newpos = level.Vec3Offset(newpos,dir*spd);
}
nstep++;
}
newpos.z = clamp(newpos.z,floorz,ceilingz);
SetOrigin(newpos,true);
UpdateWaterLevel();
if ( (waterlevel > 0) && !bAMBUSH )
{
let b = Spawn("SWWMBubble",pos);
b.scale *= abs(scale.x);
b.vel = vel;
Destroy();
return;
}
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1);
Stop;
}
}
// strictly non-colliding smoke, much lighter tick, used for heavier effects
Class SWWMHalfSmoke : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Shaded";
StencilColor "FFFFFF";
Speed 1;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
+ROLLCENTER;
Scale 0.3;
}
override void PostBeginPlay()
{
double ang, pt;
scale *= FRandom[Puff](0.5,1.5);
alpha *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[Puff](0.2,0.8)*speed;
roll = Frandom[Puff](0,360);
scale.x *= RandomPick[Puff](-1,1);
scale.y *= RandomPick[Puff](-1,1);
}
override void Tick()
{
prev = pos; // for interpolation
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
vel *= 0.96;
vel.z += 0.01;
SetOrigin(level.Vec3Offset(pos,vel),true);
UpdateWaterLevel();
if ( (waterlevel > 0) && !bAMBUSH )
{
let b = Spawn("SWWMBubble",pos);
b.scale *= abs(scale.x);
b.vel = vel;
Destroy();
return;
}
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1);
Stop;
}
}
Class SWWMSmallSmoke : SWWMHalfSmoke
{
override void PostBeginPlay()
{
double ang, pt;
scale *= FRandom[Puff](0.1,0.3);
alpha *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[Puff](0.04,0.16);
roll = Frandom[Puff](0,360);
scale.x *= RandomPick[Puff](-1,1);
scale.y *= RandomPick[Puff](-1,1);
}
States
{
Spawn:
QSM6 ABCDEFGHIJKLMNOPQR 1 A_SetTics(1+special1);
Stop;
}
}
Class SWWMBubble : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
+FORCEXYBILLBOARD;
Scale 0.5;
}
override void PostBeginPlay()
{
double ang, pt;
scale *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[Puff](0.2,0.8);
if ( waterlevel <= 0 ) Destroy();
SetState(ResolveState("Spawn")+Random[Puff](0,19));
}
override void Tick()
{
prev = pos;
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
vel *= 0.96;
vel.z += 0.05;
// linetrace-based movement (hopefully more reliable than traditional methods)
Vector3 dir = vel;
double spd = vel.length();
dir /= spd;
double dist = spd;
FLineTraceData d;
Vector3 newpos = pos;
newpos.z = clamp(newpos.z,floorz,ceilingz);
int nstep = 0;
while ( dist > 0 )
{
// safeguard, too many bounces
if ( nstep > MAXBOUNCEPERTIC )
{
Destroy();
return;
}
double ang = atan2(dir.y,dir.x);
double pt = asin(-dir.z);
LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d);
Vector3 hitnormal = -d.HitDir;
if ( d.HitType == TRACE_HitFloor )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
else hitnormal = d.HitSector.floorplane.Normal;
}
else if ( d.HitType == TRACE_HitCeiling )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
else hitnormal = d.HitSector.ceilingplane.Normal;
}
else if ( d.HitType == TRACE_HitWall )
{
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
if ( !d.LineSide ) hitnormal *= -1;
}
if ( d.HitType != TRACE_HitNone )
{
dist -= d.Distance;
// should only happen if we bounced
dir = d.HitDir-(FRandom[Puff](1.,1.2)*hitnormal*(d.HitDir dot hitnormal));
vel = dir*spd;
newpos = d.HitLocation+dir;
}
else
{
dist = 0.;
newpos = level.Vec3Offset(newpos,dir*spd);
}
nstep++;
}
newpos.z = clamp(newpos.z,floorz,ceilingz);
SetOrigin(newpos,true);
UpdateWaterLevel();
if ( (waterlevel <= 0) || !Random[Puff](0,100) ) Destroy();
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XBUB ABCDEFGHIJKLMNOPQRST 1;
Loop;
}
}
Class SWWMSparkTrail : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
+FORCEXYBILLBOARD;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_SetScale(scale.x*.9,scale.y);
A_FadeOut(.06);
}
States
{
Spawn:
XZW1 A -1 Bright;
Stop;
}
}
Class SWWMSpark : SWWMNonInteractiveActor
{
bool dead;
Sector tracksector;
int trackplane;
Default
{
RenderStyle "Add";
+FORCEXYBILLBOARD;
Gravity .2;
Scale .05;
}
override void Tick()
{
prev = pos;
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
if ( dead )
{
// do nothing but follow floor movement
if ( tracksector )
{
double trackz;
if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy);
else trackz = tracksector.floorplane.ZAtPoint(pos.xy);
if ( trackz != pos.z ) SetZ(trackz);
}
}
else
{
vel.z -= GetGravity();
// linetrace-based movement (hopefully more reliable than traditional methods)
Vector3 dir = vel;
double spd = vel.length();
dir /= spd;
double dist = spd;
FLineTraceData d;
Vector3 newpos = pos;
newpos.z = clamp(newpos.z,floorz,ceilingz);
int nstep = 0;
while ( dist > 0 )
{
Vector3 oldpos = newpos;
// safeguard, too many bounces
if ( nstep > MAXBOUNCEPERTIC )
{
Destroy();
return;
}
double ang = atan2(dir.y,dir.x);
double pt = asin(-dir.z);
LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d);
Vector3 hitnormal = -d.HitDir;
if ( d.HitType == TRACE_HitFloor )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
else hitnormal = d.HitSector.floorplane.Normal;
}
else if ( d.HitType == TRACE_HitCeiling )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
else hitnormal = d.HitSector.ceilingplane.Normal;
}
else if ( d.HitType == TRACE_HitWall )
{
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
if ( !d.LineSide ) hitnormal *= -1;
}
if ( d.HitType != TRACE_HitNone )
{
dist -= d.Distance;
// should only happen if we bounced
dir = d.HitDir-(2*hitnormal*(d.HitDir dot hitnormal));
spd *= .4;
dist *= .4;
vel = dir*spd;
newpos = d.HitLocation+dir;
}
else
{
dist = 0.;
newpos = level.Vec3Offset(newpos,dir*spd);
}
if ( ((spd < 1.) || (dir.z <= 0.)) && ((d.HitType == TRACE_HitFloor) || (newpos.z <= floorz)) )
{
// lose speed and die
if ( d.Hit3DFloor )
{
newpos.z = d.Hit3DFloor.top.ZAtPoint(newpos.xy);
tracksector = d.Hit3DFloor.model;
trackplane = 1;
}
else
{
// hacky workaround
if ( !d.HitSector ) d.HitSector = floorsector;
newpos.z = d.HitSector.floorplane.ZAtPoint(newpos.xy);
tracksector = d.HitSector;
trackplane = 0;
}
vel = (0,0,0);
pitch = 0;
roll = 0;
dead = true;
SetStateLabel("Death");
break;
}
nstep++;
}
newpos.z = clamp(newpos.z,floorz,ceilingz);
Vector3 taildir = level.Vec3Diff(newpos,pos);
double taillen = taildir.length();
if ( (taillen > 0.) && (alpha > .3) && (waterlevel <= 0) )
{
taildir /= taillen;
let t = Spawn("SWWMSparkTrail",newpos);
t.alpha = alpha*.3;
t.speed = taillen;
[t.angle, t.pitch, t.scale.y] = SWWMUtility.CalcYBeam(taildir,taillen);
}
SetOrigin(newpos,true);
if ( (pos.z <= floorz) && GetFloorTerrain().IsLiquid )
{
Destroy();
return;
}
}
UpdateWaterLevel();
if ( waterlevel > 0 )
{
let b = Spawn("SWWMBubble",pos);
b.vel = vel;
b.scale *= 0.3;
Destroy();
return;
}
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
BLPF A 1 Bright A_FadeOut(0.01);
Wait;
Death:
BLPF A 1 Bright A_FadeOut(0.05);
Wait;
}
}
Class SWWMChip : SWWMNonInteractiveActor
{
SWWMChip prevchip, nextchip;
bool killme;
double anglevel, pitchvel, rollvel;
bool dead;
Sector tracksector;
int trackplane;
Default
{
+INTERPOLATEANGLES;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
Gravity .35;
Scale .2;
}
override void PostBeginPlay()
{
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
frame = Random[Junk](0,7);
scale *= Frandom[Junk](0.8,1.2);
SWWMHandler.QueueChip(self);
}
override void OnDestroy()
{
SWWMHandler.DeQueueChip(self);
Super.OnDestroy();
}
override void Tick()
{
prev = pos; // for interpolation
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
if ( dead )
{
// do nothing but follow floor movement
if ( tracksector )
{
double trackz;
if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy);
else trackz = tracksector.floorplane.ZAtPoint(pos.xy);
if ( trackz != pos.z )
{
SetZ(trackz);
UpdateWaterLevel(false);
}
}
}
else
{
if ( waterlevel <= 0 ) vel.z -= GetGravity();
// linetrace-based movement (hopefully more reliable than traditional methods)
Vector3 dir = vel;
double spd = vel.length();
dir /= spd;
double dist = spd;
FLineTraceData d;
Vector3 newpos = pos;
newpos.z = clamp(newpos.z,floorz,ceilingz);
int nstep = 0;
while ( dist > 0 )
{
// safeguard, too many bounces
if ( nstep > MAXBOUNCEPERTIC )
{
Destroy();
return;
}
double ang = atan2(dir.y,dir.x);
double pt = asin(-dir.z);
LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d);
Vector3 hitnormal = -d.HitDir;
if ( d.HitType == TRACE_HitFloor )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
else hitnormal = d.HitSector.floorplane.Normal;
}
else if ( d.HitType == TRACE_HitCeiling )
{
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
else hitnormal = d.HitSector.ceilingplane.Normal;
}
else if ( d.HitType == TRACE_HitWall )
{
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
if ( !d.LineSide ) hitnormal *= -1;
}
if ( d.HitType != TRACE_HitNone )
{
dist -= d.Distance;
// should only happen if we bounced
dir = d.HitDir-(2*hitnormal*(d.HitDir dot hitnormal));
spd *= .3;
dist *= .3;
vel = dir*spd;
newpos = d.HitLocation+dir;
SetStateLabel("Bounce");
}
else
{
dist = 0.;
newpos = level.Vec3Offset(newpos,dir*spd);
}
newpos.z = clamp(newpos.z,floorz,ceilingz);
if ( ((spd < 1.) || (dir.z <= 0.)) && ((d.HitType == TRACE_HitFloor) || (newpos.z <= floorz)) )
{
// lose speed and die
if ( d.Hit3DFloor )
{
newpos.z = d.Hit3DFloor.top.ZAtPoint(newpos.xy);
tracksector = d.Hit3DFloor.model;
trackplane = 1;
}
else
{
// hacky workaround
if ( !d.HitSector ) d.HitSector = floorsector;
newpos.z = d.HitSector.floorplane.ZAtPoint(newpos.xy);
tracksector = d.HitSector;
trackplane = 0;
}
vel = (0,0,0);
dead = true;
SetStateLabel("Death");
break;
}
nstep++;
}
SetOrigin(newpos,true);
UpdateWaterLevel();
if ( (pos.z <= floorz) && GetFloorTerrain().IsLiquid )
{
Destroy();
return;
}
}
if ( killme ) A_FadeOut(.01);
if ( waterlevel > 0 )
{
vel *= .99;
if ( pos.z > floorz ) vel.z -= gravity*.01;
anglevel *= .98;
pitchvel *= .98;
rollvel *= .98;
}
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XZW1 # 1
{
angle += anglevel;
pitch += pitchvel;
roll += rollvel;
}
Loop;
Bounce:
XZW1 # 0
{
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
}
Goto Spawn;
Death:
XZW1 # -1;
Stop;
}
}
Class PoofLight : PaletteLight
{
Default
{
Tag "Yellow";
ReactionTime 5;
Args 0,0,0,60;
}
}
Class PoofLight2 : PaletteLight
{
Default
{
Tag "Yellow";
ReactionTime 20;
Args 0,0,0,90;
}
}
Class SWWMItemFog : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
}
States
{
Spawn:
BLPF A 2 Bright NoDelay
{
// offset up
SetOrigin(Vec3Offset(0,0,16),false);
roll = FRandom[ExploS](0,360);
scale *= FRandom[ExploS](0.9,1.1);
scale.x *= RandomPick[ExploS](-1,1);
scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](8,12);
if ( bAMBUSH ) numpt *= 2;
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](.3,8);
let s = Spawn(bAMBUSH?"SWWMSmoke":"SWWMSmallSmoke",pos);
s.vel = pvel;
s.SetShade(Color(3,2,1)*Random[ExploS](64,85));
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
s.scale *= 3.;
s.alpha *= bAMBUSH?.4:.2;
}
Spawn(bAMBUSH?"PoofLight2":"PoofLight",pos);
}
BLPF A 1 Bright A_FadeOut(.3);
Wait;
}
}
Class TeleLight : PaletteLight
{
Default
{
Tag "ImpactWav";
ReactionTime 10;
Args 0,0,0,150;
}
}
Class SWWMTeleportSparkle : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
Scale .3;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
A_SetScale(scale.x*specialf1);
A_FadeOut(specialf2);
if ( vel != (0,0,0) )
{
prev = pos;
SetOrigin(level.Vec3Offset(pos,vel),true);
vel *= .98;
}
}
States
{
Spawn:
BLPF C -1 Bright;
Stop;
}
}
Class SWWMTeleportDest : SWWMNonInteractiveActor
{
FSpawnParticleParams flare;
override void PostBeginPlay()
{
special1 = Random[ExploS](0,10);
flare.color1 = "88 AA FF";
flare.texture = TexMan.CheckForTexture("graphics/Particles/xflare.png");
flare.style = STYLE_AddShaded;
flare.flags = SPF_FULLBRIGHT;
flare.fadestep = -1;
}
override void Tick()
{
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
if ( (level.maptime+special1)%10 ) return;
flare.pos = pos+(0,0,28);
int numpt = Random[ExploS](0,2);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
Vector3 pvel = SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[ExploS](.2,.8);
flare.lifetime = Random[ExploS](120,240);
flare.size = FRandom[ExploS](2.,4.);
flare.sizestep = FRandom[ExploS](-.02,-.01);
flare.vel = pvel;
flare.startalpha = FRandom[ExploS](.15,.3);
level.SpawnParticle(flare);
}
}
}
Class SWWMTeleportLine : SWWMNonInteractiveActor
{
Line tline;
FSpawnParticleParams flare;
override void PostBeginPlay()
{
special1 = Random[ExploS](0,5);
flare.color1 = "88 AA FF";
flare.texture = TexMan.CheckForTexture("graphics/Particles/xflare.png");
flare.style = STYLE_AddShaded;
flare.flags = SPF_FULLBRIGHT;
flare.fadestep = -1;
}
override void Tick()
{
if ( !tline || !tline.special )
{
Destroy();
return;
}
if ( freezetics > 0 )
{
freezetics--;
return;
}
if ( isFrozen() ) return;
if ( (level.maptime+special1)%5 ) return;
Vector3 apos, bpos;
apos.xy = tline.v1.p;
bpos.xy = tline.v2.p;
apos.z = max(tline.frontsector.floorplane.ZAtPoint(apos.xy),tline.backsector.floorplane.ZAtPoint(apos.xy));
bpos.z = max(tline.frontsector.floorplane.ZAtPoint(bpos.xy),tline.backsector.floorplane.ZAtPoint(bpos.xy));
int numpt = Random[ExploS](0,2);
numpt *= int(clamp((apos-bpos).length()/32,1,8));
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
Vector3 pvel = SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[ExploS](.0,.3);
double d = FRandom[ExploS](0.,1.);
flare.lifetime = Random[ExploS](120,240);
flare.size = FRandom[ExploS](2.,4.);
flare.sizestep = FRandom[ExploS](-.02,-.01);
flare.pos = bpos*d+apos*(1.-d)+(0,0,FRandom[ExploS](1,4));
flare.vel = pvel;
flare.accel = (0,0,FRandom[ExploS](.05,.1));
flare.startalpha = FRandom[ExploS](.15,.3);
level.SpawnParticle(flare);
}
}
}
Class SWWMTeleportFog : SWWMNonInteractiveActor
{
Default
{
RenderStyle "Add";
+FORCEXYBILLBOARD;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("misc/teleport",CHAN_VOICE);
Spawn("TeleLight",pos);
}
States
{
Spawn:
TNT1 A 1
{
int numpt = int(Random[ExploS](6,12)*alpha);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](.3,8)*alpha;
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = pvel;
s.SetShade(Color(1,2,3)*int(Random[ExploS](64,85)*alpha));
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
s.scale *= 3.*alpha;
s.alpha *= alpha;
}
numpt = int(Random[ExploS](4,8));
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
double dist = (FRandom[ExploS](5,10)+60*(1.-alpha));
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
Vector3 ofs = SWWMUtility.Vec3FromAngles(ang,pt)*dist;
Vector3 spos = level.Vec3Offset(pos,ofs);
let s = Spawn("SWWMTeleportSparkle",spos);
s.scale *= FRandom[ExploS](.8,1.2);
s.specialf1 = FRandom[ExploS](.93,.97);
s.specialf2 = FRandom[ExploS](.02,.04);
s.roll = FRandom[ExploS](0,360);
}
A_FadeOut(.07);
}
Wait;
}
}
Class SWWMPickupFlash : SWWMNonInteractiveActor
{
Vector3 lastitempos;
FSpawnParticleParams flare;
Default
{
RenderStyle "Add";
Args 0,3,2,1;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
frame = Args[0];
if ( CurState != ResolveState("Pickup") ) return;
WorldOffset = (0,0,16);
flare.color1 = Color(Args[1]*85,Args[2]*85,Args[3]*85);
flare.texture = TexMan.CheckForTexture("graphics/Particles/xflare.png");
flare.style = STYLE_AddShaded;
flare.flags = SPF_FULLBRIGHT;
flare.lifetime = 30;
flare.accel = (0,0,.05);
flare.fadestep = -1;
}
void A_Sparkle()
{
// offset up
SetOrigin(Vec3Offset(0,0,16),false);
roll = FRandom[ExploS](0,360);
scale *= FRandom[ExploS](.9,1.1);
scale.x *= RandomPick[ExploS](-1,1);
scale.y *= RandomPick[ExploS](-1,1);
int numpt = Random[ExploS](8,10);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = SWWMUtility.Vec3FromAngles(FRandom[ExploS](0,360),FRandom[ExploS](-90,90))*FRandom[ExploS](.3,8);
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = pvel;
s.SetShade(Color(Args[1],Args[2],Args[3])*Random[ExploS](64,85));
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
s.scale *= 3.;
s.alpha *= .5;
}
}
void A_Shimmer()
{
if ( !target || Inventory(target).Owner )
{
Destroy();
return;
}
if ( target.bINVISIBLE )
{
bINVISIBLE = true;
return;
}
else if ( bINVISIBLE ) bINVISIBLE = false;
// try to reduce calls to SetOrigin as much as possible, for performance
if ( target.pos != lastitempos )
{
prev = pos;
SetOrigin(target.pos,true);
}
lastitempos = target.pos;
if ( target.bFLOATBOB && !bFLOATBOB )
{
bFLOATBOB = true;
FloatBobStrength = target.FloatBobStrength;
FloatBobPhase = target.FloatBobPhase;
}
else if ( !target.bFLOATBOB && bFLOATBOB ) bFLOATBOB = false;
A_SetScale(FRandom[ClientSparkles](1.1,1.3));
alpha = FRandom[ClientSparkles](.9,1.)*clamp((max(0,Distance3DSquared(players[consoleplayer].Camera)-40000.)/160000000.)**.25,0.,1.);
// nearby sparkles
if ( !SWWMUtility.SphereIntersect(self,players[consoleplayer].Camera.pos,500) )
return;
double alph = clamp((250000.-Distance3DSquared(players[consoleplayer].Camera))/250000.,0.,1.);
flare.startalpha = alph;
int numpt = Random[ClientSparkles](1,3);
for ( int i=0; i<numpt; i++ )
{
double ang = FRandom[ClientSparkles](0,360);
double pt = FRandom[ClientSparkles](0,360);
double dst = FRandom[ClientSparkles](4,16);
Vector3 dir = SWWMUtility.Vec3FromAngles(ang,pt);
flare.size = FRandom[ExploS](2.,3.);
flare.pos = pos+(0,0,16+GetBobOffset())+dir*dst;
flare.vel = dir*.2;
flare.sizestep = FRandom[ExploS](-.04,-.02);
level.SpawnParticle(flare);
}
}
States
{
Spawn:
BLPF # 0 Bright;
BLPF # 1 Bright A_Sparkle();
BLPF # 1 Bright A_FadeOut(.2);
Wait;
Pickup:
BLPS # 1 Bright A_Shimmer();
Wait;
}
}
Class SWWMPinkPickupFlash : SWWMPickupFlash
{
Default
{
Args 1,3,1,2;
}
}
Class SWWMCyanPickupFlash : SWWMPickupFlash
{
Default
{
Args 2,1,2,3;
}
}
Class SWWMGreenPickupFlash : SWWMPickupFlash
{
Default
{
Args 3,1,3,1;
}
}
Class SWWMBluePickupFlash : SWWMPickupFlash
{
Default
{
Args 4,1,1,3;
}
}
Class SWWMPurplePickupFlash : SWWMPickupFlash
{
Default
{
Args 5,2,1,3;
}
}
Class SWWMRedPickupFlash : SWWMPickupFlash
{
Default
{
Args 6,3,1,1;
}
}
Class SWWMWhitePickupFlash : SWWMPickupFlash
{
Default
{
Args 7,3,3,3;
}
}
// Bullet trails from DT
Class WaterHit
{
Sector sect;
Vector3 hitpos;
}
Class InvisibleSplasher : Actor
{
Default
{
Mass 100;
VSpeed -2;
Radius 2;
Height 4;
+NOBLOCKMAP; // needed to prevent infinite loops with some 3D floor water (yes, you read that right)
}
States
{
Spawn:
TNT1 A 2;
Stop;
}
}
Class SmolInvisibleSplasher : InvisibleSplasher
{
Default
{
Mass 5;
}
}
Class SWWMBulletTrail : LineTracer
{
Array<WaterHit> WaterHitList;
Array<Line> ShootThroughList;
Actor ignoreme;
static play void DoTrail( Actor target, Vector3 pos, Vector3 dir, double dist, int bubblechance, bool smoky = false )
{
let t = new("SWWMBulletTrail");
t.ignoreme = target;
t.WaterHitList.Clear();
t.ShootThroughList.Clear();
t.Trace(pos,level.PointInSector(pos.xy),dir,dist,0);
foreach ( l:t.ShootThroughList )
{
// have to do both because WOW, HOW THE FUCK IS THIS INTENTIONAL???
l.Activate(target,0,SPAC_PCross);
l.Activate(target,0,SPAC_Impact);
}
foreach ( w:t.WaterHitList )
{
let b = Actor.Spawn("InvisibleSplasher",w.hitpos);
b.target = target;
b.A_CheckTerrain();
}
for ( int i=5; i<t.Results.Distance; i+=10 )
{
if ( !Random[Boolet](0,bubblechance) ) continue;
let b = Actor.Spawn(smoky?"SWWMSmallSmoke":"SWWMBubble",level.Vec3Offset(pos,dir*i));
b.Scale *= FRandom[Boolet](.4,.6);
}
t.Destroy();
}
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop;
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
return TRACE_Stop;
}
}
// Dummy "puff" actor used for hitscan compatibility
// (don't forget to pass DMG_INFLICTOR_IS_PUFF to DamageMobj calls for this to work)
Class SWWMPuff : SWWMNonInteractiveActor
{
static Actor Setup( Vector3 pos, Vector3 dir, Actor inflictor = null, Actor source = null, Actor victim = null )
{
let p = Spawn("SWWMPuff",pos);
p.angle = atan2(dir.y,dir.x);
p.pitch = asin(-dir.z);
p.master = inflictor; // so certain scripts can fetch the weapon that spawned this puff
p.target = source; // as expected with PUFFGETSOWNER
p.tracer = victim; // as expected with HITTRACER
return p;
}
// make sure obituaries work as intended
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( master ) return master.GetObituary(victim,master,mod,playerattack);
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
default
{
+ALWAYSPUFF;
+PUFFONACTORS;
+PUFFGETSOWNER;
+HITTRACER;
}
States
{
Spawn:
TNT1 A 5; // should be enough for damage accumulators, just to be safe
Stop;
}
}
// finds the first pickup-able item
Class SWWMItemTracer : LineTracer
{
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor )
{
if ( (Results.HitActor is 'Inventory') && Results.HitActor.bSPECIAL ) return TRACE_Stop;
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockUse|Line.ML_BlockEverything)) )
return TRACE_Stop;
return TRACE_Skip;
}
return TRACE_Stop;
}
}
// Blob shadows for player and (eventually) monsters
Class SWWMShadow : SWWMNonInteractiveActor
{
Sector oldfloor;
static SWWMShadow Track( Actor other )
{
// this is necessary due to multiplayer respawn nonsense
let ti = ThinkerIterator.Create("SWWMShadow");
SWWMShadow s;
while ( s = SWWMShadow(ti.Next()) )
{
if ( s.target != other ) continue;
s.Update(true);
return s;
}
s = SWWMShadow(Spawn("SWWMShadow",other.pos));
s.target = other;
s.Update(true);
return s;
}
private void UpdateVisual()
{
// update scale / alpha
if ( target.bINVISIBLE || (target.sprite == 0) || (target.CurSector.GetTexture(0) == skyflatnum) )
alpha = 0.;
else
{
double relz = target.pos.z-pos.z;
if ( target.bFLOATBOB ) relz += target.GetBobOffset();
double bscale = (target.radius/16.)*(1.-min(1.,.003*relz));
alpha = 1.-min(1.,.006*abs(target.pos.z-pos.z));
alpha *= target.alpha;
A_SetScale(bscale);
}
}
private void Update( bool nointerpolate = false )
{
// update position
double curz = target.CurSector.NextLowestFloorAt(target.pos.x,target.pos.y,target.pos.z);
if ( (target.pos.xy == pos.xy) && (pos.z == curz) )
{
UpdateVisual();
return;
}
prev = pos;
SetOrigin((target.pos.x,target.pos.y,curz),true);
if ( nointerpolate )
prev = pos;
else if ( oldfloor != target.CurSector )
prev.z = pos.z; // prevent interpolation of steep height changes
// update slope alignment
if ( !oldfloor || (oldfloor != target.CurSector) )
SWWMUtility.SetToSlope(self,0);
oldfloor = target.CurSector;
UpdateVisual();
}
override void Tick()
{
if ( !target )
{
Destroy();
return;
}
if ( freezetics > 0 )
{
freezetics--;
return;
}
Update();
}
default
{
RenderStyle "Shaded";
StencilColor "000000";
}
States
{
Spawn:
XZW1 A -1;
Stop;
}
}
// Terrain FX (cheap)
Class SWWMBaseSplash : SWWMNonInteractiveActor abstract
{
TextureID pufftex[8];
FSpawnParticleParams puff;
abstract void DoSplash();
void Splash()
{
puff.style = STYLE_Shaded;
puff.fadestep = -1;
puff.pos = pos;
for ( int i=0; i<8; i++ )
pufftex[i] = TexMan.CheckForTexture("graphics/Particles/xpuff"..i..".png");
DoSplash();
}
States
{
Spawn:
TNT1 A 1 NoDelay Splash();
Stop;
}
}
Class SWWMWaterSplash : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 60;
puff.startalpha = .5;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](2.,12.);
dir *= str*.25;
dir.z += 1.;
puff.color1 = SWWMUtility.LerpColor("40 60 FF","A0 C0 FF",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
A_AlertMonsters(swwm_uncapalert?0:1200);
}
}
Class SWWMWaterSplash2 : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 50;
puff.startalpha = .5;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](1.,6.);
dir *= str*.25;
dir.z += .35;
puff.color1 = SWWMUtility.LerpColor("40 60 FF","A0 C0 FF",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
A_AlertMonsters(swwm_uncapalert?0:300);
}
}
Class SWWMBloodSplash : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 60;
puff.startalpha = .5;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](2.,12.);
dir *= str*.25;
dir.z += 1.;
puff.color1 = SWWMUtility.LerpColor("80 00 00","60 00 00",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str+.5;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
A_AlertMonsters(swwm_uncapalert?0:1200);
}
}
Class SWWMBloodSplash2 : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 50;
puff.startalpha = .5;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](1.,6.);
dir *= str*.25;
dir.z += .35;
puff.color1 = SWWMUtility.LerpColor("80 00 00","60 00 00",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str+.5;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
A_AlertMonsters(swwm_uncapalert?0:300);
}
}
Class SWWMSludgeSplash : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 40;
puff.startalpha = .8;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](2.,8.);
dir *= str*.25;
dir.z += .4;
puff.color1 = SWWMUtility.LerpColor("40 50 40","30 30 30",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str+2.;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
A_AlertMonsters(swwm_uncapalert?0:1200);
}
}
Class SWWMSludgeSplash2 : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 30;
puff.startalpha = .8;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](1.,4.);
dir *= str*.25;
dir.z += .15;
puff.color1 = SWWMUtility.LerpColor("40 50 40","30 30 30",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str+2.;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
A_AlertMonsters(swwm_uncapalert?0:300);
}
}
Class SWWMMudSplash : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 40;
puff.startalpha = .8;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](2.,8.);
dir *= str*.25;
dir.z += .4;
puff.color1 = SWWMUtility.LerpColor("50 40 20","30 20 10",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str+2.;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
A_AlertMonsters(swwm_uncapalert?0:1200);
}
}
Class SWWMMudSplash2 : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 30;
puff.startalpha = .8;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](1.,4.);
dir *= str*.25;
dir.z += .15;
puff.color1 = SWWMUtility.LerpColor("50 40 20","30 20 10",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str+2.;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
A_AlertMonsters(swwm_uncapalert?0:300);
}
}
Class SWWMSlimeSplash : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 40;
puff.startalpha = .8;
puff.style = STYLE_AddShaded;
puff.flags = SPF_FULLBRIGHT;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](2.,8.);
dir *= str*.25;
dir.z += .4;
puff.color1 = SWWMUtility.LerpColor("00 FF 00","00 80 00",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str+2.;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
A_AlertMonsters(swwm_uncapalert?0:1200);
}
}
Class SWWMSlimeSplash2 : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 30;
puff.startalpha = .8;
puff.style = STYLE_AddShaded;
puff.flags = SPF_FULLBRIGHT;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](1.,4.);
dir *= str*.25;
dir.z += .15;
puff.color1 = SWWMUtility.LerpColor("00 FF 00","00 80 00",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str+2.;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
A_AlertMonsters(swwm_uncapalert?0:300);
}
}
Class SWWMLavaSplash : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 40;
puff.startalpha = .8;
puff.style = STYLE_AddShaded;
puff.flags = SPF_FULLBRIGHT;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](2.,12.);
dir *= str*.35;
dir.z += .6;
puff.color1 = SWWMUtility.LerpColor("FF C0 40","FF 40 20",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str+1.;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
let s = Spawn("SWWMSizzleSmoke",pos);
s.target = target;
}
}
Class SWWMLavaSplash2 : SWWMBaseSplash
{
override void DoSplash()
{
puff.lifetime = 30;
puff.startalpha = .8;
puff.style = STYLE_AddShaded;
puff.flags = SPF_FULLBRIGHT;
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](1.,6.);
dir *= str*.35;
dir.z += .2;
puff.color1 = SWWMUtility.LerpColor("FF C0 40","FF 40 20",FRandom[ExploS](0.,1.));
puff.texture = pufftex[Random[ExploS](0,7)];
puff.size = str+1.;
puff.sizestep = -.02*str;
puff.vel = dir;
puff.accel = (0,0,-.03*str);
level.SpawnParticle(puff);
}
let s = Spawn("SWWMSizzleSmoke2",pos);
s.target = target;
}
}
Class SWWMSizzleSmoke : SWWMBaseSplash
{
override void DoSplash()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<2; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](.5,2.);
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = dir*str+(0,0,.4);
s.SetShade(Color(1,1,1)*Random[ExploS](192,224));
s.scale *= 40.;
s.A_SetRenderStyle(s.alpha*.4,STYLE_AddShaded);
s.special1 = Random[ExploS](0,2);
}
A_AlertMonsters(swwm_uncapalert?0:1200);
}
}
Class SWWMSizzleSmoke2 : SWWMBaseSplash
{
override void DoSplash()
{
double ang, pt, str;
Vector3 dir;
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = SWWMUtility.Vec3FromAngles(ang,pt);
str = FRandom[ExploS](.25,1.);
let s = Spawn("SWWMSmallSmoke",pos);
s.vel = dir*str+(0,0,.15);
s.SetShade(Color(1,1,1)*Random[ExploS](192,224));
s.scale *= 20.;
s.A_SetRenderStyle(s.alpha*.3,STYLE_AddShaded);
s.special1 = Random[ExploS](0,1);
A_AlertMonsters(swwm_uncapalert?0:300);
}
}
// Hexen thing
Class SWWMCrushedSpike : Actor
{
Default
{
Radius 20;
Height 16;
+SOLID;
+FLOORCLIP;
+NOTELEPORT;
}
States
{
Spawn:
TSPK X -1;
Stop;
}
}