swwmgz_m/zscript/swwm_common_fx.zsc
Marisa Kirisame 3500f72db0 Add "exclusive use to pickup" option.
Make "pickup through melee" optional (disabled by default).
Increased leniency of use to pickup (nice for items with small hitboxes).
2021-05-29 23:26:12 +02:00

1301 lines
28 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)/abs(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();
InitialReactionTime = ReactionTime;
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 = -ReactionTime;
IsLooping = true;
}
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
{
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];
}
States
{
Spawn:
BLPF # 1 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,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;
}
}
BLPF # 1 Bright A_FadeOut(.2);
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)
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;
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;
}
}