swwmgz_m/zscript/swwm_common_fx.zsc
Marisa Kirisame 4d7019ae86 Several changes from devel once more:
- Fuck it, Quadravol will be lever action.
 - Tiny cleanup.
 - Rewrite the weapon replacement system (less of a mess now maybe?).
 - Some menu fixes.
 - Minimap zoom increments like in the Common Library.
 - Add missing sound definition for Safety Tether.
   (This mostly went unnoticed because it's VERY rare to have it play)
 - Shift Sparkster x3 (DLC2) to slot 7.
   This way you can have both it and the Quadravol simultaneously.
   It would be unfair to not let you hold both "iconic" UnSX weapons at once.
 - Small lore tweak on Quadravol stance swap.
 - Fix off-by-one bug in looping palette lights.
 - Re-do logo shader. Use separate layer textures.
 - Fix Elemental Coating breaking "End Level" damage sectors.
(This will be the last batch of changes before I continue working on menus)
2021-12-08 18:17:41 +01:00

1636 lines
36 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() ) 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 : Actor
{
Default
{
RenderStyle "Shaded";
StencilColor "FFFFFF";
Radius .1;
Height 0;
Speed 1;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
+ROLLCENTER;
+THRUACTORS;
+NOTELEPORT;
+NOINTERACTION;
Scale .3;
FloatBobPhase 0;
}
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 += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(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 ( 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 ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1);
Stop;
}
}
// strictly non-interacting smoke, much lighter tick, used for heavier effects
Class SWWMHalfSmoke : Actor
{
Default
{
RenderStyle "Shaded";
StencilColor "FFFFFF";
Radius .1;
Height 0;
Speed 1;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
+ROLLCENTER;
+NOTELEPORT;
+NOINTERACTION;
Scale 0.3;
FloatBobPhase 0;
}
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 += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(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 ( 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 ( 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 += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(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 : Actor
{
Default
{
RenderStyle "Add";
Radius .1;
Height 0;
+NOBLOCKMAP;
+NOGRAVITY;
+DONTSPLASH;
+FORCEXYBILLBOARD;
+NOTELEPORT;
+THRUACTORS;
+NOINTERACTION;
Scale 0.5;
FloatBobPhase 0;
}
override void PostBeginPlay()
{
double ang, pt;
scale *= FRandom[Puff](0.5,1.5);
ang = FRandom[Puff](0,360);
pt = FRandom[Puff](-90,90);
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
if ( waterlevel <= 0 ) Destroy();
SetState(ResolveState("Spawn")+Random[Puff](0,19));
}
override void Tick()
{
prev = pos;
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 ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
XBUB ABCDEFGHIJKLMNOPQRST 1;
Loop;
}
}
Class SWWMSparkTrail : Actor
{
Default
{
RenderStyle "Add";
Radius .1;
Height 0.;
+FORCEXYBILLBOARD;
+NOGRAVITY;
+NOBLOCKMAP;
+NOINTERACTION;
+DONTSPLASH;
+NOTELEPORT;
}
override void Tick()
{
if ( isFrozen() ) return;
A_SetScale(scale.x*.9,scale.y);
A_FadeOut(.06);
}
States
{
Spawn:
XZW1 A -1 Bright;
Stop;
}
}
Class SWWMSpark : Actor
{
bool dead;
Sector tracksector;
int trackplane;
Default
{
RenderStyle "Add";
Radius .1;
Height 0;
+NOBLOCKMAP;
+FORCEXYBILLBOARD;
+THRUACTORS;
+NOTELEPORT;
+DONTSPLASH;
+NOINTERACTION;
Gravity 0.2;
Scale 0.05;
FloatBobPhase 0;
}
override void Tick()
{
prev = pos;
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.scale.y = taillen;
t.angle = atan2(taildir.y,taildir.x);
t.pitch = asin(-taildir.z)+90;
}
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 ( 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 : Actor
{
SWWMChip prevchip, nextchip;
bool killme;
double anglevel, pitchvel, rollvel;
bool dead;
Sector tracksector;
int trackplane;
Default
{
Radius .1;
Height 0;
+NOBLOCKMAP;
+THRUACTORS;
+NOTELEPORT;
+DONTSPLASH;
+INTERPOLATEANGLES;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
+NOINTERACTION;
Gravity 0.35;
Scale 0.2;
FloatBobPhase 0;
}
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 ( 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);
pitch = 0;
roll = 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 *= .98;
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:
XZW2 # -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 : Actor
{
Default
{
RenderStyle "Add";
Radius .1;
Height 0;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+ROLLSPRITE;
+ROLLCENTER;
+NOINTERACTION;
+FORCEXYBILLBOARD;
FloatBobPhase 0;
}
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 = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*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 : Actor
{
Default
{
RenderStyle "Add";
Scale 0.3;
Radius .1;
Height 0.;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+ROLLSPRITE;
+ROLLCENTER;
+FORCEXYBILLBOARD;
+NOINTERACTION;
FloatBobPhase 0;
}
override void Tick()
{
if ( isFrozen() ) return;
A_SetScale(scale.x*specialf1);
A_FadeOut(specialf2);
if ( vel != (0,0,0) )
{
SetOrigin(level.Vec3Offset(pos,vel),true);
vel *= .98;
}
}
States
{
Spawn:
BLPF C -1 Bright;
Stop;
}
}
Class SWWMTeleportDest : Actor
{
Default
{
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOINTERACTION;
Radius .1;
Height 0.;
}
override void PostBeginPlay()
{
special1 = Random[ExploS](0,10);
}
override void Tick()
{
if ( isFrozen() ) return;
if ( (level.maptime+special1)%10 ) return;
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);
A_SpawnParticle("88 AA FF",SPF_FULLBRIGHT,Random[ExploS](120,240),FRandom[ExploS](2.,4.),0,0,0,28,FRandom[ExploS](-.8,.8),FRandom[ExploS](-.8,.8),FRandom[ExploS](-.8,.8),0,0,0,FRandom[ExploS](.15,.3),-1,FRandom[ExploS](-.02,-.01));
}
}
}
Class SWWMTeleportLine : Actor
{
Line tline;
Default
{
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOINTERACTION;
Radius .1;
Height 0.;
}
override void PostBeginPlay()
{
special1 = Random[ExploS](0,5);
}
override void Tick()
{
if ( !tline )
{
Destroy();
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);
double d = FRandom[ExploS](0.,1.);
Vector3 ppos = bpos*d+apos*(1.-d)+(0,0,FRandom[ExploS](1,4));
Vector3 rpos = ppos-pos;
A_SpawnParticle("88 AA FF",SPF_FULLBRIGHT,Random[ExploS](120,240),FRandom[ExploS](2.,4.),0,rpos.x,rpos.y,rpos.z,FRandom[ExploS](-.3,.3),FRandom[ExploS](-.3,.3),FRandom[ExploS](-.3,.3),0,0,FRandom[ExploS](.05,.1),FRandom[ExploS](.15,.3),-1,FRandom[ExploS](-.02,-.01));
}
}
}
Class SWWMTeleportFog : Actor
{
Default
{
RenderStyle "Add";
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+NOINTERACTION;
+FORCEXYBILLBOARD;
Radius .1;
Height 0.;
FloatBobPhase 0;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
A_StartSound("misc/teleport",CHAN_VOICE);
Spawn("TeleLight",pos);
if ( swwm_simplefog ) SetStateLabel("Simple");
}
States
{
Spawn:
TNT1 A 1
{
int numpt = int(Random[ExploS](6,12)*alpha);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*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 = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(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;
Simple:
SPEX ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
Stop;
}
}
Class SWWMPickupFlash : Actor
{
Vector3 lastitempos;
Default
{
RenderStyle "Add";
Args 0,3,2,1;
Radius .1;
Height 0;
+NOGRAVITY;
+NOBLOCKMAP;
+DONTSPLASH;
+ROLLSPRITE;
+ROLLCENTER;
+NOINTERACTION;
+FORCEXYBILLBOARD;
FloatBobPhase 0;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
frame = Args[0];
}
action 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 = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*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;
}
}
action 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 != invoker.lastitempos ) SetOrigin(target.Vec3Offset(0,0,16),true);
invoker.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](.9,1.1)*(max(target.radius,target.height)/16.));
alpha = FRandom[ClientSparkles](.9,1.)*clamp((max(0,Distance3DSquared(players[consoleplayer].Camera)-40000.)/160000000.)**.25,0.,1.);
}
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;
}
}
// TODO very precise tiny fast projectiles using hitscan-based stepping (e.g.: bullets)
Class SWWMLightProjectile : Actor abstract {}
// 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)
FloatBobPhase 0;
}
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);
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
{
// have to do both because WOW, HOW THE FUCK IS THIS INTENTIONAL???
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
}
for ( int i=0; i<t.WaterHitList.Size(); i++ )
{
let b = Actor.Spawn("InvisibleSplasher",t.WaterHitList[i].hitpos);
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;
}
}
// 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
Class SWWMShadow : Actor
{
Sector oldfloor;
static void Track( Actor other )
{
// prevent infinite recursion
if ( other is 'SWWMShadow' ) return;
// no shadows for overlay actors
if ( other is 'GhostArtifactX' ) return;
// no shadows for things with zero radius
if ( other.radius <= 0. ) return;
let s = SWWMShadow(Spawn("SWWMShadow",other.pos));
s.target = other;
s.Update(true);
}
private void Update( bool nointerpolate = false )
{
// update scale / alpha
if ( ((target is 'Inventory') && Inventory(target).Owner) || target.bKILLED || target.bINVISIBLE || (target.sprite == target.GetSpriteIndex('TNT1')) || (target.sprite == target.GetSpriteIndex('ACLO')) || (target.CurSector.GetTexture(0) == skyflatnum) )
alpha = 0.;
else
{
alpha = 1.-min(1.,.006*abs(target.pos.z-pos.z));
alpha *= target.alpha;
double relz = target.pos.z-pos.z;
if ( target.bFLOATBOB ) relz += target.GetBobOffset();
double bscale = (target.radius/16.)*(1.-min(1.,.003*relz));
// hax
if ( target is 'CompanionLamp' ) bscale *= 2.;
A_SetScale(bscale);
}
// 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) ) return;
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;
}
override void Tick()
{
if ( !target )
{
Destroy();
return;
}
Update();
}
default
{
RenderStyle "Shaded";
StencilColor "000000";
DistanceCheck 'swwm_shadowdist';
Radius .1;
Height 0.;
+NOBLOCKMAP;
+NOINTERACTION;
+DONTSPLASH;
+NOTELEPORT;
FloatBobPhase 0;
}
States
{
Spawn:
XZW1 A -1;
Stop;
}
}
// Terrain FX (cheap)
Class SWWMBaseSplash : Actor
{
default
{
Radius .1;
Height 0.;
+NOBLOCKMAP;
+NOINTERACTION;
+DONTSPLASH;
+NOTELEPORT;
FloatBobPhase 0;
}
States
{
Spawn:
TNT1 A 1;
Stop;
}
}
Class SWWMWaterSplash : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](2.,12.);
dir *= str*.25;
dir.z += 1.;
A_SpawnParticle(SWWMUtility.LerpColor("40 60 FF","A0 C0 FF",FRandom[ExploS](0.,1.)),0,60,str,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.5,-1,-.02*str);
}
}
}
Class SWWMWaterSplash2 : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](1.,6.);
dir *= str*.25;
dir.z += .35;
A_SpawnParticle(SWWMUtility.LerpColor("40 60 FF","A0 C0 FF",FRandom[ExploS](0.,1.)),0,50,str,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.5,-1,-.02*str);
}
}
}
Class SWWMBloodSplash : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](2.,12.);
dir *= str*.25;
dir.z += 1.;
A_SpawnParticle(SWWMUtility.LerpColor("80 00 00","60 00 00",FRandom[ExploS](0.,1.)),0,60,str+.5,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.5,-1,-.02*str);
}
}
}
Class SWWMBloodSplash2 : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](1.,6.);
dir *= str*.25;
dir.z += .35;
A_SpawnParticle(SWWMUtility.LerpColor("80 00 00","60 00 00",FRandom[ExploS](0.,1.)),0,50,str+.5,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.5,-1,-.02*str);
}
}
}
Class SWWMSludgeSplash : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](2.,8.);
dir *= str*.25;
dir.z += .4;
A_SpawnParticle(SWWMUtility.LerpColor("40 50 40","30 30 30",FRandom[ExploS](0.,1.)),0,40,str+2.,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.8,-1,-.02*str);
}
}
}
Class SWWMSludgeSplash2 : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](1.,4.);
dir *= str*.25;
dir.z += .15;
A_SpawnParticle(SWWMUtility.LerpColor("40 50 40","30 30 30",FRandom[ExploS](0.,1.)),0,30,str+2.,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.8,-1,-.02*str);
}
}
}
Class SWWMMudSplash : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](2.,8.);
dir *= str*.25;
dir.z += .4;
A_SpawnParticle(SWWMUtility.LerpColor("50 40 20","30 20 10",FRandom[ExploS](0.,1.)),0,40,str+2.,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.8,-1,-.02*str);
}
}
}
Class SWWMMudSplash2 : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](1.,4.);
dir *= str*.25;
dir.z += .15;
A_SpawnParticle(SWWMUtility.LerpColor("50 40 20","30 20 10",FRandom[ExploS](0.,1.)),0,30,str+2.,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.8,-1,-.02*str);
}
}
}
Class SWWMSlimeSplash : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](2.,8.);
dir *= str*.25;
dir.z += .4;
A_SpawnParticle(SWWMUtility.LerpColor("00 FF 00","00 80 00",FRandom[ExploS](0.,1.)),SPF_FULLBRIGHT,40,str+2.,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.8,-1,-.02*str);
}
}
}
Class SWWMSlimeSplash2 : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](1.,4.);
dir *= str*.25;
dir.z += .15;
A_SpawnParticle(SWWMUtility.LerpColor("00 FF 00","00 80 00",FRandom[ExploS](0.,1.)),SPF_FULLBRIGHT,30,str+2.,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.8,-1,-.02*str);
}
}
}
Class SWWMLavaSplash : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<60; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-60);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](2.,12.);
dir *= str*.35;
dir.z += .6;
A_SpawnParticle(SWWMUtility.LerpColor("FF C0 40","FF 40 20",FRandom[ExploS](0.,1.)),SPF_FULLBRIGHT,40,str+1.,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.8,-1,-.02*str);
}
Spawn("SWWMSizzleSmoke",pos);
}
}
Class SWWMLavaSplash2 : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<15; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt));
str = FRandom[ExploS](1.,6.);
dir *= str*.35;
dir.z += .2;
A_SpawnParticle(SWWMUtility.LerpColor("FF C0 40","FF 40 20",FRandom[ExploS](0.,1.)),SPF_FULLBRIGHT,30,str+1.,0,0,0,0,dir.x,dir.y,dir.z,0,0,-.03*str,.8,-1,-.02*str);
}
Spawn("SWWMSizzleSmoke2",pos);
}
}
Class SWWMSizzleSmoke : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
for ( int i=0; i<2; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(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);
}
}
}
Class SWWMSizzleSmoke2 : SWWMBaseSplash
{
override void PostBeginPlay()
{
double ang, pt, str;
Vector3 dir;
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,-30);
dir = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(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);
}
}
// Hexen thing
Class SWWMCrushedSpike : Actor
{
Default
{
Radius 20;
Height 16;
+SOLID;
+FLOORCLIP;
+NOTELEPORT;
}
States
{
Spawn:
TSPK X -1;
Stop;
}
}