2000 lines
48 KiB
Text
2000 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);
|
|
}
|
|
override String GetSelfObituary( Actor inflictor, Name mod )
|
|
{
|
|
if ( master ) return master.GetSelfObituary(master,mod);
|
|
return Super.GetSelfObituary(inflictor,mod);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|