Further optimization could be possible if I found a way to divide the trace results into segments based on intersections with water volumes. This would save on countless calls to Vec3Offset and PointInSector. Something to keep in mind, I suppose. While I was at it, I did make the underwater check into a new utility function. It is more or less based on how updating water level is done internally, but saves time by just checking a single point rather than an actor's full height. Not sure if I'll need to use it elsewhere, but if that turns out to not be the case I'll change it to a private function of the SWWMBulletTrail class afterwards.
1995 lines
48 KiB
Text
1995 lines
48 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();
|
|
}
|
|
}
|
|
|
|
// base visual thinker subclasses
|
|
Class SWWMStaticSprite : VisualThinker abstract
|
|
{
|
|
bool bCheckWater; // checks water level, calls EnteredWater and LeftWater virtuals (perf intensive)
|
|
bool lastwater; // internal, water state from previous tic
|
|
bool firstwater; // internal, water state has been checked at least once
|
|
bool bWallStop; // stops moving if the next tic takes us out of bounds (perf intensive)
|
|
bool bWallKill; // destroy self if out of bounds (perf intensive)
|
|
double speed; // multiply any initial thrust by this (used by subclasses, mainly)
|
|
|
|
protected bool CheckWater()
|
|
{
|
|
if ( !cursector ) return false;
|
|
if ( cursector.moreflags&Sector.SECMF_UNDERWATER )
|
|
{
|
|
// directly underwater
|
|
return true;
|
|
}
|
|
let hsec = cursector.GetHeightSec();
|
|
if ( hsec )
|
|
{
|
|
// check for height transfer
|
|
double fh = hsec.floorplane.ZAtPoint(pos.xy);
|
|
if ( hsec.moreflags&Sector.SECMF_UNDERWATERMASK )
|
|
{
|
|
if ( fh-pos.z > 0 ) return true;
|
|
double ch = hsec.ceilingplane.ZAtPoint(pos.xy);
|
|
if ( !(hsec.moreflags&Sector.SECMF_FAKEFLOORONLY)
|
|
&& (pos.z > ch) )
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// check for swimmable 3d floors
|
|
for ( int i=0; i<cursector.Get3DFloorCount(); i++ )
|
|
{
|
|
let ff = cursector.Get3DFloor(i);
|
|
if ( !(ff.flags&F3DFloor.FF_EXISTS)
|
|
|| (ff.flags&F3DFloor.FF_SOLID)
|
|
|| !(ff.flags&F3DFloor.FF_SWIMMABLE) )
|
|
continue;
|
|
double fh = ff.bottom.ZAtPoint(pos.xy);
|
|
double ch = ff.top.ZAtPoint(pos.xy);
|
|
if ( (pos.z >= fh) && (pos.z <= ch) )
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual void EnteredWater() {}
|
|
virtual void LeftWater() {}
|
|
|
|
virtual void OnTick() {}
|
|
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( IsFrozen() )
|
|
return;
|
|
if ( bCheckWater )
|
|
{
|
|
bool curwater = CheckWater();
|
|
if ( curwater && (!lastwater || !firstwater) )
|
|
EnteredWater();
|
|
else if ( !curwater && (lastwater || !firstwater) )
|
|
LeftWater();
|
|
if ( bDestroyed ) // might destroy self in either virtual function
|
|
return;
|
|
lastwater = curwater;
|
|
firstwater = true;
|
|
}
|
|
if ( bWallStop )
|
|
{
|
|
let newpos = level.Vec3Offset(pos,vel);
|
|
if ( !level.IsPointInLevel(newpos) ) vel *= 0.;
|
|
}
|
|
if ( bWallKill && !level.IsPointInLevel(pos) )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
OnTick();
|
|
}
|
|
|
|
virtual void SetupSprite()
|
|
{
|
|
// safe defaults
|
|
texture = TexMan.CheckForTexture("BLPFA0");
|
|
bCheckWater = false;
|
|
bWallStop = false;
|
|
bWallKill = false;
|
|
}
|
|
|
|
static SWWMStaticSprite SpawnAt( Class<SWWMStaticSprite> type, Vector3 spawnpos )
|
|
{
|
|
let t = SWWMStaticSprite(level.SpawnVisualThinker(type));
|
|
t.pos = spawnpos;
|
|
t.cursector = level.PointInSector(t.pos.xy);
|
|
t.SetupSprite();
|
|
t.speed = 1.; // default
|
|
return t;
|
|
}
|
|
}
|
|
|
|
Class SWWMAnimSprite : SWWMStaticSprite
|
|
{
|
|
string sprname; // base name of sprite
|
|
uint8 sprframe; // current sprite frame (0-28)
|
|
uint8 numframes; // total frame count of animation (max 29)
|
|
uint8 frameskip; // how many frames to skip each step
|
|
uint8 framestep; // how many tics to wait for each frame step
|
|
uint8 framecnt; // internal, counter for frame stepping
|
|
bool bLooping; // animation loops, otherwise die upon reaching last frame
|
|
|
|
private void TickAnim( bool bFirstTick = false )
|
|
{
|
|
if ( !bFirstTick )
|
|
{
|
|
framecnt++;
|
|
if ( framecnt < framestep )
|
|
return;
|
|
framecnt = 0;
|
|
sprframe += 1+frameskip;
|
|
if ( sprframe >= numframes )
|
|
{
|
|
if ( !bLooping )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
else sprframe -= numframes;
|
|
}
|
|
}
|
|
string tname = String.Format("%.4s%c0",sprname,0x41+sprframe);
|
|
texture = TexMan.CheckForTexture(tname);
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !IsFrozen() && !bDestroyed )
|
|
TickAnim();
|
|
}
|
|
|
|
override void SetupSprite()
|
|
{
|
|
// safe defaults
|
|
sprname = "XEX1";
|
|
sprframe = 0;
|
|
numframes = 28;
|
|
frameskip = 0;
|
|
framestep = 1;
|
|
framecnt = 0;
|
|
bLooping = false;
|
|
bCheckWater = false;
|
|
bWallStop = false;
|
|
bWallKill = false;
|
|
}
|
|
|
|
static SWWMAnimSprite SpawnAt( Class<SWWMAnimSprite> type, Vector3 spawnpos )
|
|
{
|
|
let t = SWWMAnimSprite(level.SpawnVisualThinker(type));
|
|
t.pos = spawnpos;
|
|
t.cursector = level.PointInSector(t.pos.xy);
|
|
t.SetupSprite();
|
|
t.speed = 1.; // default
|
|
t.TickAnim(true);
|
|
return t;
|
|
}
|
|
}
|
|
|
|
// Generic smoke, lightweight tick
|
|
Class SWWMSmoke : SWWMNonInteractiveActor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle 'Shaded';
|
|
StencilColor "FF FF FF";
|
|
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);
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
Vector3 hitnormal = SWWMUtility.GetLineTraceHitNormal(d);
|
|
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;
|
|
}
|
|
}
|
|
|
|
// Visual thinker smoke, less overhead, used for heavier effects
|
|
Class SWWMHalfSmoke : SWWMAnimSprite
|
|
{
|
|
override void SetupSprite()
|
|
{
|
|
Super.SetupSprite();
|
|
sprname = "XSMK";
|
|
numframes = 20;
|
|
bCheckWater = true;
|
|
SetRenderStyle(STYLE_Shaded);
|
|
scolor = 0xFFFFFFFF;
|
|
Scale = (.3,.3);
|
|
Flags |= SPF_ROLL;
|
|
}
|
|
|
|
override void OnTick()
|
|
{
|
|
vel *= .96;
|
|
vel.z += .01;
|
|
}
|
|
|
|
override void EnteredWater()
|
|
{
|
|
let b = SWWMAnimSprite.SpawnAt('SWWMHalfBubble',pos);
|
|
b.scale *= abs(scale.x);
|
|
b.vel = vel;
|
|
Destroy();
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](.5,1.5);
|
|
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);
|
|
bXFlip = Random[Puff](0,1);
|
|
bYFlip = Random[Puff](0,1);
|
|
}
|
|
}
|
|
|
|
// lesser version
|
|
Class SWWMSmallSmoke : SWWMHalfSmoke
|
|
{
|
|
override void SetupSprite()
|
|
{
|
|
Super.SetupSprite();
|
|
sprname = "QSM6";
|
|
numframes = 18;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](.5,1.5);
|
|
alpha *= FRandom[Puff](.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[Puff](.04,.16)*speed;
|
|
roll = Frandom[Puff](0,360);
|
|
bXFlip = Random[Puff](0,1);
|
|
bYFlip = Random[Puff](0,1);
|
|
}
|
|
}
|
|
|
|
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) || (waterdepth < (scale.y*9.)) ) 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);
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
Vector3 hitnormal = SWWMUtility.GetLineTraceHitNormal(d);
|
|
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) || (waterdepth < (scale.y*9.)) || !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;
|
|
}
|
|
}
|
|
|
|
// Visual thinker version of the above, much simpler checks
|
|
Class SWWMHalfBubble : SWWMAnimSprite
|
|
{
|
|
override void SetupSprite()
|
|
{
|
|
Super.SetupSprite();
|
|
sprname = "XBUB";
|
|
sprframe = Random[Puff](0,19);
|
|
numframes = 20;
|
|
bCheckWater = true;
|
|
bLooping = true;
|
|
bWallKill = true;
|
|
SetRenderStyle(STYLE_Add);
|
|
Scale = (.5,.5);
|
|
}
|
|
|
|
override void OnTick()
|
|
{
|
|
vel *= .96;
|
|
vel.z += .05;
|
|
if ( !Random[Puff](0,100) )
|
|
Destroy();
|
|
}
|
|
|
|
override void LeftWater()
|
|
{
|
|
Destroy();
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= 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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
Vector3 hitnormal = SWWMUtility.GetLineTraceHitNormal(d);
|
|
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);
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
Vector3 hitnormal = SWWMUtility.GetLineTraceHitNormal(d);
|
|
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 = SWWMAnimSprite.SpawnAt('SWWMHalfSmoke',pos);
|
|
s.vel = pvel;
|
|
s.scolor = Color(3,2,1)*Random[ExploS](64,85);
|
|
s.SetRenderStyle(STYLE_AddShaded);
|
|
s.scale *= 3.;
|
|
s.alpha *= .4;
|
|
}
|
|
}
|
|
else 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 = SWWMAnimSprite.SpawnAt('SWWMSmallSmoke',pos);
|
|
s.vel = pvel;
|
|
s.scolor = Color(3,2,1)*Random[ExploS](64,85);
|
|
s.SetRenderStyle(STYLE_AddShaded);
|
|
s.scale *= 3.;
|
|
s.alpha *= .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 : SWWMStaticSprite
|
|
{
|
|
double scalestep, alphastep;
|
|
|
|
override void SetupSprite()
|
|
{
|
|
texture = TexMan.CheckForTexture("BLPFC0");
|
|
bCheckWater = false;
|
|
bWallStop = false;
|
|
bWallKill = false;
|
|
Scale = (.3,.3);
|
|
Flags |= SPF_FULLBRIGHT;
|
|
SetRenderStyle(STYLE_Add);
|
|
}
|
|
|
|
override void OnTick()
|
|
{
|
|
vel *= .98;
|
|
scale *= scalestep;
|
|
alpha = max(0.,alpha-alphastep);
|
|
if ( alpha <= 0. ) Destroy();
|
|
}
|
|
}
|
|
|
|
Class SWWMTeleportDest : SWWMNonInteractiveActor
|
|
{
|
|
FSpawnParticleParams flare;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
special1 = Random[ExploS](0,10);
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
if ( freezetics > 0 )
|
|
{
|
|
freezetics--;
|
|
return;
|
|
}
|
|
if ( isFrozen() ) return;
|
|
if ( (level.maptime+special1)%10 ) return;
|
|
if ( !flare.texture )
|
|
{
|
|
flare.color1 = 0xFF88AAFF;
|
|
flare.texture = TexMan.CheckForTexture("graphics/Particles/xflare.png");
|
|
flare.style = STYLE_AddShaded;
|
|
flare.flags = SPF_FULLBRIGHT;
|
|
flare.fadestep = -1;
|
|
}
|
|
double dist = Distance3DSquared(players[consoleplayer].Camera);
|
|
if ( dist >= 250000. ) return;
|
|
double alph = clamp((250000.-dist)/250000.,0.,1.);
|
|
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)*alph;
|
|
level.SpawnParticle(flare);
|
|
}
|
|
}
|
|
}
|
|
|
|
Class SWWMTeleportLine : SWWMNonInteractiveActor
|
|
{
|
|
Line tline;
|
|
FSpawnParticleParams flare;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
special1 = Random[ExploS](0,5);
|
|
}
|
|
|
|
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));
|
|
if ( !flare.texture )
|
|
{
|
|
flare.color1 = 0xFF88AAFF;
|
|
flare.texture = TexMan.CheckForTexture("graphics/Particles/xflare.png");
|
|
flare.style = STYLE_AddShaded;
|
|
flare.flags = SPF_FULLBRIGHT;
|
|
flare.fadestep = -1;
|
|
}
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
double d = FRandom[ExploS](0.,1.);
|
|
Vector3 ppos = bpos*d+apos*(1.-d)+(0,0,FRandom[ExploS](1,4));
|
|
Vector3 rpos = level.Vec3Diff(ppos,players[consoleplayer].Camera.pos);
|
|
double dist = rpos dot rpos;
|
|
if ( dist >= 250000. ) continue;
|
|
double alph = clamp((250000.-dist)/250000.,0.,1.);
|
|
double ang = FRandom[ExploS](0,360);
|
|
double pt = FRandom[ExploS](-90,90);
|
|
Vector3 pvel = SWWMUtility.Vec3FromAngles(ang,pt)*FRandom[ExploS](.0,.3);
|
|
flare.lifetime = Random[ExploS](120,240);
|
|
flare.size = FRandom[ExploS](2.,4.);
|
|
flare.sizestep = FRandom[ExploS](-.02,-.01);
|
|
flare.pos = ppos;
|
|
flare.vel = pvel;
|
|
flare.accel = (0,0,FRandom[ExploS](.05,.1));
|
|
flare.startalpha = FRandom[ExploS](.15,.3)*alph;
|
|
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 = SWWMAnimSprite.SpawnAt('SWWMSmallSmoke',pos);
|
|
s.vel = pvel;
|
|
s.scolor = Color(1,2,3)*int(Random[ExploS](64,85)*alpha);
|
|
s.SetRenderStyle(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 = SWWMTeleportSparkle(SWWMStaticSprite.SpawnAt('SWWMTeleportSparkle',spos));
|
|
s.scale *= FRandom[ExploS](.8,1.2);
|
|
s.scalestep = FRandom[ExploS](.93,.97);
|
|
s.alphastep = 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);
|
|
}
|
|
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 = SWWMAnimSprite.SpawnAt('SWWMSmallSmoke',pos);
|
|
s.vel = pvel;
|
|
s.scolor = Color(Args[1],Args[2],Args[3])*Random[ExploS](64,85);
|
|
s.SetRenderStyle(STYLE_AddShaded);
|
|
s.scale *= 3.;
|
|
s.alpha *= .5;
|
|
}
|
|
}
|
|
// needed since cube roots scale very badly when there's thousands of these on the map
|
|
private static double AlphaCubeRoot( double alpha )
|
|
{
|
|
static const double lut[] =
|
|
{
|
|
0.000000000, 0.250244738, 0.297592823, 0.329340597, 0.353899503, 0.374203165, 0.391654181, 0.407042226,
|
|
0.420859807, 0.433436601, 0.445005066, 0.455735780, 0.465757939, 0.475171947, 0.484057512, 0.492479061,
|
|
0.500489477, 0.508132748, 0.515445890, 0.522460372, 0.529203190, 0.535697696, 0.541964232, 0.548020638,
|
|
0.553882655, 0.559564246, 0.565077860, 0.570434647, 0.575644637, 0.580716886, 0.585659603, 0.590480253,
|
|
0.595185647, 0.599782016, 0.604275079, 0.608670097, 0.612971920, 0.617185033, 0.621313591, 0.625361451,
|
|
0.629332199, 0.633229179, 0.637055512, 0.640814114, 0.644507720, 0.648138893, 0.651710042, 0.655223431,
|
|
0.658681194, 0.662085345, 0.665437783, 0.668740305, 0.671994612, 0.675202314, 0.678364941, 0.681483943,
|
|
0.684560698, 0.687596518, 0.690592652, 0.693550290, 0.696470567, 0.699354564, 0.702203318, 0.705017817,
|
|
0.707799006, 0.710547792, 0.713265041, 0.715951586, 0.718608224, 0.721235720, 0.723834810, 0.726406200,
|
|
0.728950569, 0.731468570, 0.733960833, 0.736427963, 0.738870544, 0.741289137, 0.743684287, 0.746056516,
|
|
0.748406329, 0.750734215, 0.753040646, 0.755326076, 0.757590947, 0.759835686, 0.762060704, 0.764266402,
|
|
0.766453167, 0.768621373, 0.770771384, 0.772903552, 0.775018219, 0.777115716, 0.779196366, 0.781260480,
|
|
0.783308363, 0.785340308, 0.787356603, 0.789357525, 0.791343346, 0.793314328, 0.795270729, 0.797212796,
|
|
0.799140774, 0.801054897, 0.802955396, 0.804842496, 0.806716415, 0.808577364, 0.810425553, 0.812261184,
|
|
0.814084452, 0.815895552, 0.817694672, 0.819481993, 0.821257696, 0.823021954, 0.824774940, 0.826516818,
|
|
0.828247753, 0.829967903, 0.831677424, 0.833376467, 0.835065182, 0.836743713, 0.838412204, 0.840070792,
|
|
0.841719614, 0.843358803, 0.844988489, 0.846608801, 0.848219862, 0.849821795, 0.851414720, 0.852998754,
|
|
0.854574013, 0.856140608, 0.857698650, 0.859248247, 0.860789506, 0.862322530, 0.863847421, 0.865364279,
|
|
0.866873203, 0.868374287, 0.869867628, 0.871353317, 0.872831445, 0.874302101, 0.875765373, 0.877221347,
|
|
0.878670107, 0.880111737, 0.881546317, 0.882973927, 0.884394645, 0.885808550, 0.887215717, 0.888616220,
|
|
0.890010132, 0.891397525, 0.892778470, 0.894153037, 0.895521294, 0.896883307, 0.898239144, 0.899588868,
|
|
0.900932545, 0.902270236, 0.903602004, 0.904927909, 0.906248011, 0.907562370, 0.908871043, 0.910174088,
|
|
0.911471559, 0.912763514, 0.914050005, 0.915331088, 0.916606813, 0.917877235, 0.919142403, 0.920402368,
|
|
0.921657180, 0.922906887, 0.924151539, 0.925391181, 0.926625862, 0.927855627, 0.929080522, 0.930300591,
|
|
0.931515878, 0.932726428, 0.933932282, 0.935133484, 0.936330074, 0.937522094, 0.938709585, 0.939892586,
|
|
0.941071137, 0.942245277, 0.943415044, 0.944580475, 0.945741609, 0.946898482, 0.948051130, 0.949199588,
|
|
0.950343894, 0.951484081, 0.952620183, 0.953752235, 0.954880270, 0.956004322, 0.957124423, 0.958240605,
|
|
0.959352900, 0.960461340, 0.961565955, 0.962666776, 0.963763834, 0.964857158, 0.965946779, 0.967032724,
|
|
0.968115023, 0.969193704, 0.970268796, 0.971340326, 0.972408321, 0.973472809, 0.974533817, 0.975591370,
|
|
0.976645495, 0.977696218, 0.978743564, 0.979787559, 0.980828227, 0.981865593, 0.982899681, 0.983930516,
|
|
0.984958121, 0.985982520, 0.987003736, 0.988021791, 0.989036710, 0.990048513, 0.991057224, 0.992062865,
|
|
0.993065456, 0.994065020, 0.995061577, 0.996055150, 0.997045758, 0.998033422, 0.999018163, 1.000000000
|
|
};
|
|
alpha *= 255;
|
|
int low = int(floor(alpha));
|
|
int high = int(ceil(alpha));
|
|
double theta = alpha-low;
|
|
return lut[low]*(1.-theta)+lut[high]*theta;
|
|
}
|
|
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));
|
|
double camdist = Distance3DSquared(players[consoleplayer].Camera);
|
|
if ( camdist <= 40000. ) alpha = 0.;
|
|
else
|
|
{
|
|
if ( (camdist-40000.) >= 160000000. ) alpha = 1.;
|
|
else alpha = AlphaCubeRoot(clamp(max(0,camdist-40000.)/160000000.,0.,1.));
|
|
alpha *= FRandom[ClientSparkles](.9,1.);
|
|
}
|
|
// nearby sparkles
|
|
if ( camdist >= 250000. ) return;
|
|
double alph = clamp((250000.-camdist)/250000.,0.,1.);
|
|
if ( !flare.texture )
|
|
{
|
|
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;
|
|
}
|
|
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
|
|
{
|
|
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;
|
|
|
|
static play void DoTrail( Actor target, Vector3 pos, Vector3 dir, double dist, int bubblesparse, bool smoky = false )
|
|
{
|
|
let t = new('SWWMBulletTrail');
|
|
t.WaterHitList.Clear();
|
|
t.ShootThroughList.Clear();
|
|
t.Trace(pos,level.PointInSector(pos.xy),dir,dist,0,ignore:target);
|
|
foreach ( l:t.ShootThroughList )
|
|
{
|
|
// have to do both separately, for whatever reason
|
|
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();
|
|
}
|
|
Vector3 ppos;
|
|
SWWMAnimSprite b;
|
|
bool bInWater;
|
|
for ( int i=5; i<t.Results.Distance; i+=10 )
|
|
{
|
|
if ( Random[Boolet](0,bubblesparse) ) continue;
|
|
if ( !Random[Boolet](0,i/100) ) continue; // fall off w/ distance
|
|
ppos = level.Vec3Offset(pos,dir*i);
|
|
bInWater = SWWMUtility.PointInWater(ppos);
|
|
if ( smoky && !bInWater ) b = SWWMAnimSprite.SpawnAt('SWWMHalfSmoke',ppos);
|
|
else if ( !smoky && bInWater ) b = SWWMAnimSprite.SpawnAt('SWWMHalfBubble',ppos);
|
|
else continue;
|
|
b.Scale *= FRandom[Boolet](.4,.6);
|
|
}
|
|
t.Destroy();
|
|
}
|
|
|
|
override ETraceStatus TraceCallback()
|
|
{
|
|
// liquid splashes
|
|
if ( Results.CrossedWater )
|
|
{
|
|
let hl = new('WaterHit');
|
|
hl.hitpos = Results.CrossedWaterPos;
|
|
WaterHitList.Push(hl);
|
|
}
|
|
else if ( Results.Crossed3DWater )
|
|
{
|
|
let hl = new('WaterHit');
|
|
hl.hitpos = Results.Crossed3DWaterPos;
|
|
WaterHitList.Push(hl);
|
|
}
|
|
if ( Results.HitType == TRACE_HitActor )
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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 "00 00 00";
|
|
}
|
|
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(0xFF4060FF,0xFFA0C0FF,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,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
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(0xFF4060FF,0xFFA0C0FF,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,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
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(0xFF800000,0xFF600000,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,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
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(0xFF800000,0xFF600000,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,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
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(0xFF405040,0xFF303030,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,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
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(0xFF405040,0xFF303030,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,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
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(0xFF504020,0xFF302010,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,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
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(0xFF504020,0xFF302010,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,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
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(0xFF00FF00,0xFF008000,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,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
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(0xFF00FF00,0xFF008000,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,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
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(0xFFFFC040,0xFFFF4020,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(0xFFFFC040,0xFFFF4020,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<4; i++ )
|
|
{
|
|
ang = FRandom[ExploS](0,360);
|
|
pt = FRandom[ExploS](-90,-30);
|
|
dir = SWWMUtility.Vec3FromAngles(ang,pt);
|
|
str = FRandom[ExploS](.5,2.);
|
|
let s = SWWMAnimSprite.SpawnAt('SWWMSmallSmoke',pos);
|
|
s.vel = dir*str+(0,0,.4);
|
|
s.scolor = Color(1,1,1)*Random[ExploS](192,224);
|
|
s.scale *= 8.;
|
|
s.alpha *= .4;
|
|
s.SetRenderStyle(STYLE_AddShaded);
|
|
s.framestep = Random[ExploS](1,3);
|
|
}
|
|
A_AlertMonsters(swwm_uncapalert?0:1200,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
Class SWWMSizzleSmoke2 : 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](.25,1.);
|
|
let s = SWWMAnimSprite.SpawnAt('SWWMSmallSmoke',pos);
|
|
s.vel = dir*str+(0,0,.15);
|
|
s.scolor = Color(1,1,1)*Random[ExploS](192,224);
|
|
s.scale *= 4.;
|
|
s.alpha *= .3;
|
|
s.SetRenderStyle(STYLE_AddShaded);
|
|
s.framestep = Random[ExploS](1,2);
|
|
}
|
|
A_AlertMonsters(swwm_uncapalert?0:300,AMF_EMITFROMTARGET);
|
|
}
|
|
}
|
|
|
|
// Hexen thing
|
|
Class SWWMCrushedSpike : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 20;
|
|
Height 16;
|
|
+SOLID;
|
|
+FLOORCLIP;
|
|
+NOTELEPORT;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TSPK X -1;
|
|
Stop;
|
|
}
|
|
}
|