1637 lines
36 KiB
Text
1637 lines
36 KiB
Text
// common code goes here
|
|
enum ESWWMGZChannels
|
|
{
|
|
CHAN_YOUDONEFUCKEDUP = 63200, // exception handler
|
|
CHAN_DEMOVOICE = 63201, // demolitionist voices
|
|
CHAN_FOOTSTEP = 63202, // footstep sounds and others
|
|
CHAN_WEAPONEXTRA = 63203, // additional weapon sounds (usually loops)
|
|
CHAN_POWERUP = 63204, // powerup sounds
|
|
CHAN_POWERUPEXTRA = 63205, // additional powerup sounds
|
|
CHAN_JETPACK = 63206, // jetpack sound
|
|
CHAN_ITEMEXTRA = 63207, // additional item sounds
|
|
CHAN_WEAPONEXTRA2 = 63208, // additional weapon sound slot
|
|
CHAN_WEAPONEXTRA3 = 63209, // additional weapon sound slot (again)
|
|
CHAN_DAMAGE = 63210, // used for impact/hit sounds
|
|
CHAN_AMBEXTRA = 63211 // player ambience when submerged
|
|
};
|
|
|
|
// Future planning, will be filled out with AI stuff and whatnot someday
|
|
Class SWWMMonster : Actor abstract
|
|
{
|
|
// integrated fun tags
|
|
virtual clearscope String GetFunTag( String defstr = "" )
|
|
{
|
|
return GetTag(defstr);
|
|
}
|
|
|
|
// the function that should be overriden in subclasses
|
|
virtual int HandleLocationalDamage( Actor inflictor, Actor source, int damage, Name mod, Vector3 HitLocation, int flags = 0, double angle = 0 )
|
|
{
|
|
return damage;
|
|
}
|
|
|
|
// locational damage support, akin to UE1, but hitlocation will be treated as a relative offset, to make things easier
|
|
// this one should be called directly by everything in this mod, when possible
|
|
int LocationalDamageMobj( Actor inflictor, Actor source, int damage, Name mod, Vector3 HitLocation, int flags = 0, double angle = 0 )
|
|
{
|
|
damage = HandleLocationalDamage(inflictor,source,damage,mod,HitLocation,flags,angle);
|
|
return Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
|
|
}
|
|
|
|
// "estimated" locational damage for the vanilla DamageMobj
|
|
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
|
|
{
|
|
Vector3 guesspos = (0,0,Height/2.);
|
|
// use inflictor if available, as it may be a projectile or hitscan puff
|
|
// if damage comes from an item, use owner
|
|
// all of this could be done better (or implemented as an engine feature), but whatever
|
|
Actor whomst = inflictor?inflictor:source;
|
|
if ( whomst is 'Inventory' ) whomst = Inventory(whomst).Owner;
|
|
if ( whomst )
|
|
{
|
|
if ( whomst.bMISSILE || (flags&DMG_INFLICTOR_IS_PUFF) )
|
|
guesspos = level.Vec3Diff(pos,whomst.pos);
|
|
else guesspos = level.Vec3Diff(pos,whomst.Vec3Offset(0,0,whomst.Height/2));
|
|
guesspos.x = clamp(guesspos.x,-radius,radius);
|
|
guesspos.y = clamp(guesspos.y,-radius,radius);
|
|
guesspos.z = clamp(guesspos.z,0,height);
|
|
}
|
|
return LocationalDamageMobj(inflictor,source,damage,mod,guesspos,flags,angle);
|
|
}
|
|
}
|
|
|
|
// Less mean-spirited Keen
|
|
Class SWWMHangingKeen : Actor
|
|
{
|
|
action void A_DropKeen()
|
|
{
|
|
Spawn("SWWMDroppedKeen",Vec3Offset(0,0,8));
|
|
}
|
|
override bool Used( Actor user )
|
|
{
|
|
// test vertical range
|
|
Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2));
|
|
double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2);
|
|
if ( abs(diff.z) > rang ) return false;
|
|
if ( Health > 0 )
|
|
{
|
|
DamageMobj(user,user,Health,'Untie',DMG_FORCED|DMG_THRUSTLESS);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
Default
|
|
{
|
|
Tag "$FN_KEEN";
|
|
Health 100;
|
|
Radius 10;
|
|
Height 54;
|
|
Mass int.max;
|
|
PainChance 256;
|
|
+SOLID;
|
|
+SPAWNCEILING;
|
|
+NOGRAVITY;
|
|
+SHOOTABLE;
|
|
+NOICEDEATH;
|
|
+DONTFALL;
|
|
+NOBLOOD;
|
|
+DONTTHRUST;
|
|
PainSound "keen/pain";
|
|
DeathSound "keen/death";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
KEE2 A -1;
|
|
Stop;
|
|
Death:
|
|
KEE2 A 6;
|
|
KEE2 B 6 A_DropKeen();
|
|
KEE2 C 6 A_Scream();
|
|
KEE2 DE 6;
|
|
KEE2 F 30;
|
|
KEE2 F -1 A_KeenDie();
|
|
Stop;
|
|
Pain:
|
|
KEE2 G 4;
|
|
KEE2 G 8 A_Pain();
|
|
Goto Spawn;
|
|
}
|
|
}
|
|
Class SWWMDroppedKeen : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 10;
|
|
Height 32;
|
|
Gravity .5;
|
|
+NOBLOCKMAP;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
KEE3 A 1 A_JumpIf(pos.z<=floorz,1);
|
|
Wait;
|
|
KEE3 B 1 { vel.z = 4; }
|
|
KEE3 B 1 A_JumpIf(pos.z<=floorz,1);
|
|
Wait;
|
|
KEE3 B 1 { vel.z = 2; }
|
|
KEE3 B 1 A_JumpIf(pos.z<=floorz,1);
|
|
Wait;
|
|
KEE3 B 12;
|
|
TNT1 A 1 { Spawn("TeleportFog",pos,ALLOW_REPLACE); }
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMBossBrainExpl : Actor
|
|
{
|
|
void A_Ignite()
|
|
{
|
|
A_QuakeEx(3,3,3,20,0,400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollintensity:2.);
|
|
A_StartSound("explodium/hit",CHAN_VOICE,CHANF_DEFAULT,.4,.5);
|
|
Scale *= FRandom[ExploS](0.8,1.1);
|
|
Scale.x *= RandomPick[ExploS](-1,1);
|
|
Scale.y *= RandomPick[ExploS](-1,1);
|
|
int numpt = Random[ExploS](8,16);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,6);
|
|
let s = Spawn("SWWMSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
|
|
s.special1 = Random[ExploS](1,4);
|
|
s.scale *= 2.8;
|
|
s.alpha *= .4;
|
|
}
|
|
numpt = Random[ExploS](5,10);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,12);
|
|
let s = Spawn("SWWMSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Random[ExploS](10,15);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](2,24);
|
|
let s = Spawn("SWWMChip",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = int(Random[ExploS](-1,2)+special1);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
let s = Spawn("SWWMBossBrainExplArm",pos);
|
|
s.target = target;
|
|
}
|
|
}
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Scale 2.5;
|
|
Radius .1;
|
|
Height 0.;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+NOINTERACTION;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 10;
|
|
TNT1 A 0 A_Ignite();
|
|
XEX1 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
Class SWWMBossBrainExplArm : Actor
|
|
{
|
|
Default
|
|
{
|
|
PROJECTILE;
|
|
+THRUACTORS;
|
|
+BOUNCEONWALLS;
|
|
+BOUNCEONFLOORS;
|
|
+BOUNCEONCEILINGS;
|
|
-NOGRAVITY;
|
|
Gravity 0.35;
|
|
BounceFactor 1.0;
|
|
Radius 4;
|
|
Height 4;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
reactiontime = Random[ExploS](10,15);
|
|
double ang, pt;
|
|
ang = FRandom[ExploS](0,360);
|
|
pt = FRandom[ExploS](-90,90);
|
|
vel = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*FRandom[ExploS](8.,20.);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1
|
|
{
|
|
A_CountDown();
|
|
Spawn("ExplodiumMagTrail",pos);
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](1,5);
|
|
let s = Spawn("SWWMHalfSmoke",pos);
|
|
s.vel = pvel+vel*.2;
|
|
s.SetShade(Color(1,1,1)*Random[ExploS](64,224));
|
|
s.special1 = Random[ExploS](1,3);
|
|
s.scale *= 2.4;
|
|
s.alpha *= 0.1+.4*(ReactionTime/15.);
|
|
}
|
|
Wait;
|
|
}
|
|
}
|
|
Class SWWMBossBrainPain : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius .1;
|
|
Height 0.;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
MBRN B 1 Bright A_FadeOut(.05);
|
|
Wait;
|
|
}
|
|
}
|
|
Class SWWMBossBrain : BossBrain
|
|
{
|
|
bool eyeless;
|
|
int smallcooldown;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
let ti = ThinkerIterator.Create("BossEye");
|
|
if ( ti.Next() ) eyeless = false;
|
|
else eyeless = true;
|
|
}
|
|
|
|
void SpawnBrainExpl( bool death = false )
|
|
{
|
|
if ( death )
|
|
{
|
|
// big explosions throughout
|
|
for ( int x=-350; x<=350; x+=5 )
|
|
{
|
|
let s = Spawn("SWWMBossBrainExpl",Vec2OffsetZ(x,-280,Random[BrainExplode](120,500)));
|
|
s.tics = Random[BrainExplode](5,120);
|
|
s.special1 = Random[BrainExplode](0,3);
|
|
}
|
|
return;
|
|
}
|
|
if ( level.maptime < smallcooldown ) return;
|
|
smallcooldown = level.maptime+10;
|
|
// small explosion on brain hole
|
|
for ( int x=-40; x<=40; x+=10 )
|
|
{
|
|
let s = Spawn("SWWMBossBrainExpl",Vec2OffsetZ(x,-280,Random[BrainExplode](380,420)));
|
|
s.tics = Random[BrainExplode](1,8);
|
|
s.scale *= .5;
|
|
}
|
|
}
|
|
|
|
// kill every single monster in the map, burn away spawn cubes, remove eyes
|
|
// just let players have their 100% kills in peace
|
|
void EverythingDies()
|
|
{
|
|
let ti = ThinkerIterator.Create("Actor");
|
|
Actor a;
|
|
while ( a = Actor(ti.Next()) )
|
|
{
|
|
if ( a is 'BossEye' ) a.SetStateLabel("Null");
|
|
else if ( a is 'SpawnShot' )
|
|
{
|
|
a.Spawn("SpawnFire",a.pos,ALLOW_REPLACE);
|
|
a.SetStateLabel("Null");
|
|
}
|
|
else if ( (a.Health > 0) && (a.bBossSpawned || a.bCOUNTKILL) )
|
|
a.DamageMobj(self,self,int.max,'EndMii',DMG_FORCED|DMG_THRUSTLESS);
|
|
}
|
|
|
|
}
|
|
|
|
Default
|
|
{
|
|
Tag "$FN_BOSSBRAIN";
|
|
Radius 20;
|
|
Height 40;
|
|
+NOBLOOD;
|
|
+COUNTKILL;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
MBRN A -1;
|
|
Stop;
|
|
Pain:
|
|
MBRN A 10
|
|
{
|
|
A_StartSound("brain/pain",CHAN_VOICE,attenuation:ATTN_NONE);
|
|
A_QuakeEx(3,3,3,15,0,65535,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.3);
|
|
if ( !eyeless ) SpawnBrainExpl(false);
|
|
Spawn("SWWMBossBrainPain",pos);
|
|
}
|
|
Goto Spawn;
|
|
Death:
|
|
MBRN A 120
|
|
{
|
|
A_StartSound("brain/death",CHAN_VOICE,attenuation:ATTN_NONE);
|
|
A_QuakeEx(9,9,9,120,0,65535,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
|
|
if ( !eyeless )
|
|
{
|
|
SpawnBrainExpl(true);
|
|
EverythingDies();
|
|
}
|
|
Spawn("SWWMBossBrainPain",pos);
|
|
}
|
|
MBRN A -1 A_BrainDie();
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// imitates UE1 light type LT_TexturePaletteOnce/LT_TexturePaletteLoop
|
|
Class PaletteLight : PointLight
|
|
{
|
|
Color pal[256];
|
|
bool IsLooping;
|
|
int InitialReactionTime;
|
|
|
|
Default
|
|
{
|
|
Tag "Explosion";
|
|
Args 0,0,0,80;
|
|
ReactionTime 15;
|
|
}
|
|
private void UpdateLight()
|
|
{
|
|
int index = clamp(255-((255*ReactionTime)/abs(InitialReactionTime)),0,255);
|
|
args[LIGHT_RED] = pal[index].r;
|
|
args[LIGHT_GREEN] = pal[index].g;
|
|
args[LIGHT_BLUE] = pal[index].b;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
InitialReactionTime = ReactionTime;
|
|
String palname = GetTag();
|
|
int sep = palname.IndexOf(",");
|
|
int palnum = 0;
|
|
if ( sep != -1 )
|
|
{
|
|
String palnumstr = palname.Mid(sep+1);
|
|
palnum = palnumstr.ToInt()*768;
|
|
palname.Truncate(sep);
|
|
}
|
|
int lump = Wads.CheckNumForFullname(String.Format("palettes/%s.pal",palname));
|
|
String paldat = Wads.ReadLump(lump);
|
|
for ( int i=0; i<256; i++ )
|
|
{
|
|
pal[i].r = paldat.ByteAt(palnum++);
|
|
pal[i].g = paldat.ByteAt(palnum++);
|
|
pal[i].b = paldat.ByteAt(palnum++);
|
|
}
|
|
if ( ReactionTime < 0 )
|
|
{
|
|
ReactionTime = -ReactionTime;
|
|
IsLooping = true;
|
|
}
|
|
UpdateLight();
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
ReactionTime--;
|
|
if ( ReactionTime < 0 )
|
|
{
|
|
if ( !IsLooping )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
else ReactionTime = abs(InitialReactionTime);
|
|
}
|
|
if ( target ) SetOrigin(target.pos,true);
|
|
UpdateLight();
|
|
}
|
|
}
|
|
|
|
// Generic smoke, lightweight tick
|
|
Class SWWMSmoke : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Shaded";
|
|
StencilColor "FFFFFF";
|
|
Radius 2;
|
|
Height 2;
|
|
Speed 1;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+NOINTERACTION;
|
|
Scale .3;
|
|
FloatBobPhase 0;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](.5,1.5);
|
|
alpha = min(1.,alpha*FRandom[Puff](.5,1.5));
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](.2,.8)*speed;
|
|
roll = Frandom[Puff](0,360);
|
|
scale.x *= RandomPick[Puff](-1,1);
|
|
scale.y *= RandomPick[Puff](-1,1);
|
|
}
|
|
override void Tick()
|
|
{
|
|
prev = pos; // for interpolation
|
|
if ( isFrozen() ) return;
|
|
vel *= .96;
|
|
vel.z += .01;
|
|
// linetrace-based movement (hopefully more reliable than traditional methods)
|
|
Vector3 dir = vel;
|
|
double spd = vel.length();
|
|
dir /= spd;
|
|
double dist = spd;
|
|
FLineTraceData d;
|
|
Vector3 newpos = pos;
|
|
newpos.z = clamp(newpos.z,floorz,ceilingz);
|
|
int nstep = 0;
|
|
while ( dist > 0 )
|
|
{
|
|
// safeguard, too many bounces
|
|
if ( nstep > MAXBOUNCEPERTIC )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
double ang = atan2(dir.y,dir.x);
|
|
double pt = asin(-dir.z);
|
|
LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d);
|
|
Vector3 hitnormal = -d.HitDir;
|
|
if ( d.HitType == TRACE_HitFloor )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
|
|
else hitnormal = d.HitSector.floorplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitCeiling )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
|
|
else hitnormal = d.HitSector.ceilingplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitWall )
|
|
{
|
|
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
|
if ( !d.LineSide ) hitnormal *= -1;
|
|
}
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
dist -= d.Distance;
|
|
// should only happen if we bounced
|
|
dir = d.HitDir-(FRandom[Puff](1.,1.2)*hitnormal*(d.HitDir dot hitnormal));
|
|
vel = dir*spd;
|
|
newpos = d.HitLocation+dir;
|
|
}
|
|
else
|
|
{
|
|
dist = 0.;
|
|
newpos = level.Vec3Offset(newpos,dir*spd);
|
|
}
|
|
nstep++;
|
|
}
|
|
newpos.z = clamp(newpos.z,floorz,ceilingz);
|
|
SetOrigin(newpos,true);
|
|
UpdateWaterLevel();
|
|
if ( (waterlevel > 0) && !bAMBUSH )
|
|
{
|
|
let b = Spawn("SWWMBubble",pos);
|
|
b.scale *= abs(scale.x);
|
|
b.vel = vel;
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1);
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// strictly non-interacting smoke, much lighter tick, used for heavier effects
|
|
Class SWWMHalfSmoke : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Shaded";
|
|
StencilColor "FFFFFF";
|
|
Radius 0.1;
|
|
Height 0;
|
|
Speed 1;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+NOTELEPORT;
|
|
+NOINTERACTION;
|
|
Scale 0.3;
|
|
FloatBobPhase 0;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.5,1.5);
|
|
alpha *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8)*speed;
|
|
roll = Frandom[Puff](0,360);
|
|
scale.x *= RandomPick[Puff](-1,1);
|
|
scale.y *= RandomPick[Puff](-1,1);
|
|
}
|
|
override void Tick()
|
|
{
|
|
prev = pos; // for interpolation
|
|
if ( isFrozen() ) return;
|
|
vel *= 0.96;
|
|
vel.z += 0.01;
|
|
SetOrigin(level.Vec3Offset(pos,vel),true);
|
|
UpdateWaterLevel();
|
|
if ( (waterlevel > 0) && !bAMBUSH )
|
|
{
|
|
let b = Spawn("SWWMBubble",pos);
|
|
b.scale *= abs(scale.x);
|
|
b.vel = vel;
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
XSMK ABCDEFGHIJKLMNOPQRST 1 A_SetTics(1+special1);
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMSmallSmoke : SWWMHalfSmoke
|
|
{
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.1,0.3);
|
|
alpha *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16);
|
|
roll = Frandom[Puff](0,360);
|
|
scale.x *= RandomPick[Puff](-1,1);
|
|
scale.y *= RandomPick[Puff](-1,1);
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
QSM6 ABCDEFGHIJKLMNOPQR 1 A_SetTics(1+special1);
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMBubble : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
+NOTELEPORT;
|
|
+THRUACTORS;
|
|
+NOINTERACTION;
|
|
Scale 0.5;
|
|
FloatBobPhase 0;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
double ang, pt;
|
|
scale *= FRandom[Puff](0.5,1.5);
|
|
ang = FRandom[Puff](0,360);
|
|
pt = FRandom[Puff](-90,90);
|
|
vel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.2,0.8);
|
|
if ( waterlevel <= 0 ) Destroy();
|
|
SetState(ResolveState("Spawn")+Random[Puff](0,19));
|
|
}
|
|
override void Tick()
|
|
{
|
|
prev = pos;
|
|
if ( isFrozen() ) return;
|
|
vel *= 0.96;
|
|
vel.z += 0.05;
|
|
// linetrace-based movement (hopefully more reliable than traditional methods)
|
|
Vector3 dir = vel;
|
|
double spd = vel.length();
|
|
dir /= spd;
|
|
double dist = spd;
|
|
FLineTraceData d;
|
|
Vector3 newpos = pos;
|
|
newpos.z = clamp(newpos.z,floorz,ceilingz);
|
|
int nstep = 0;
|
|
while ( dist > 0 )
|
|
{
|
|
// safeguard, too many bounces
|
|
if ( nstep > MAXBOUNCEPERTIC )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
double ang = atan2(dir.y,dir.x);
|
|
double pt = asin(-dir.z);
|
|
LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d);
|
|
Vector3 hitnormal = -d.HitDir;
|
|
if ( d.HitType == TRACE_HitFloor )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
|
|
else hitnormal = d.HitSector.floorplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitCeiling )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
|
|
else hitnormal = d.HitSector.ceilingplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitWall )
|
|
{
|
|
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
|
if ( !d.LineSide ) hitnormal *= -1;
|
|
}
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
dist -= d.Distance;
|
|
// should only happen if we bounced
|
|
dir = d.HitDir-(FRandom[Puff](1.,1.2)*hitnormal*(d.HitDir dot hitnormal));
|
|
vel = dir*spd;
|
|
newpos = d.HitLocation+dir;
|
|
}
|
|
else
|
|
{
|
|
dist = 0.;
|
|
newpos = level.Vec3Offset(newpos,dir*spd);
|
|
}
|
|
nstep++;
|
|
}
|
|
newpos.z = clamp(newpos.z,floorz,ceilingz);
|
|
SetOrigin(newpos,true);
|
|
UpdateWaterLevel();
|
|
if ( (waterlevel <= 0) || !Random[Puff](0,100) ) Destroy();
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XBUB ABCDEFGHIJKLMNOPQRST 1;
|
|
Loop;
|
|
}
|
|
}
|
|
|
|
Class SWWMSparkTrail : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius .1;
|
|
Height 0.;
|
|
+FORCEXYBILLBOARD;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+NOINTERACTION;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
override void Tick()
|
|
{
|
|
if ( isFrozen() ) return;
|
|
A_SetScale(scale.x*.9,scale.y);
|
|
A_FadeOut(.06);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A -1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMSpark : Actor
|
|
{
|
|
bool dead;
|
|
Sector tracksector;
|
|
int trackplane;
|
|
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+FORCEXYBILLBOARD;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+NOINTERACTION;
|
|
Gravity 0.2;
|
|
Scale 0.05;
|
|
FloatBobPhase 0;
|
|
}
|
|
override void Tick()
|
|
{
|
|
prev = pos;
|
|
if ( isFrozen() ) return;
|
|
if ( dead )
|
|
{
|
|
// do nothing but follow floor movement
|
|
if ( tracksector )
|
|
{
|
|
double trackz;
|
|
if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy);
|
|
else trackz = tracksector.floorplane.ZAtPoint(pos.xy);
|
|
if ( trackz != pos.z ) SetZ(trackz);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vel.z -= GetGravity();
|
|
// linetrace-based movement (hopefully more reliable than traditional methods)
|
|
Vector3 dir = vel;
|
|
double spd = vel.length();
|
|
dir /= spd;
|
|
double dist = spd;
|
|
FLineTraceData d;
|
|
Vector3 newpos = pos;
|
|
newpos.z = clamp(newpos.z,floorz,ceilingz);
|
|
int nstep = 0;
|
|
while ( dist > 0 )
|
|
{
|
|
Vector3 oldpos = newpos;
|
|
// safeguard, too many bounces
|
|
if ( nstep > MAXBOUNCEPERTIC )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
double ang = atan2(dir.y,dir.x);
|
|
double pt = asin(-dir.z);
|
|
LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d);
|
|
Vector3 hitnormal = -d.HitDir;
|
|
if ( d.HitType == TRACE_HitFloor )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
|
|
else hitnormal = d.HitSector.floorplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitCeiling )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
|
|
else hitnormal = d.HitSector.ceilingplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitWall )
|
|
{
|
|
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
|
if ( !d.LineSide ) hitnormal *= -1;
|
|
}
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
dist -= d.Distance;
|
|
// should only happen if we bounced
|
|
dir = d.HitDir-(2*hitnormal*(d.HitDir dot hitnormal));
|
|
spd *= .4;
|
|
dist *= .4;
|
|
vel = dir*spd;
|
|
newpos = d.HitLocation+dir;
|
|
}
|
|
else
|
|
{
|
|
dist = 0.;
|
|
newpos = level.Vec3Offset(newpos,dir*spd);
|
|
}
|
|
if ( ((spd < 1.) || (dir.z <= 0.)) && ((d.HitType == TRACE_HitFloor) || (newpos.z <= floorz)) )
|
|
{
|
|
// lose speed and die
|
|
if ( d.Hit3DFloor )
|
|
{
|
|
newpos.z = d.Hit3DFloor.top.ZAtPoint(newpos.xy);
|
|
tracksector = d.Hit3DFloor.model;
|
|
trackplane = 1;
|
|
}
|
|
else
|
|
{
|
|
// hacky workaround
|
|
if ( !d.HitSector ) d.HitSector = floorsector;
|
|
newpos.z = d.HitSector.floorplane.ZAtPoint(newpos.xy);
|
|
tracksector = d.HitSector;
|
|
trackplane = 0;
|
|
}
|
|
vel = (0,0,0);
|
|
pitch = 0;
|
|
roll = 0;
|
|
dead = true;
|
|
SetStateLabel("Death");
|
|
break;
|
|
}
|
|
nstep++;
|
|
}
|
|
newpos.z = clamp(newpos.z,floorz,ceilingz);
|
|
Vector3 taildir = level.Vec3Diff(newpos,pos);
|
|
double taillen = taildir.length();
|
|
if ( (taillen > 0.) && (alpha > .3) )
|
|
{
|
|
taildir /= taillen;
|
|
let t = Spawn("SWWMSparkTrail",newpos);
|
|
t.alpha = alpha*.3;
|
|
t.scale.y = taillen;
|
|
t.angle = atan2(taildir.y,taildir.x);
|
|
t.pitch = asin(-taildir.z)+90;
|
|
}
|
|
SetOrigin(newpos,true);
|
|
}
|
|
UpdateWaterLevel();
|
|
if ( waterlevel > 0 )
|
|
{
|
|
let b = Spawn("SWWMBubble",pos);
|
|
b.vel = vel;
|
|
b.scale *= 0.3;
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BLPF A 1 Bright A_FadeOut(0.01);
|
|
Wait;
|
|
Death:
|
|
BLPF A 1 Bright A_FadeOut(0.05);
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class SWWMChip : Actor
|
|
{
|
|
SWWMChip prevchip, nextchip;
|
|
bool killme;
|
|
double anglevel, pitchvel, rollvel;
|
|
bool dead;
|
|
Sector tracksector;
|
|
int trackplane;
|
|
|
|
Default
|
|
{
|
|
Radius 1;
|
|
Height 1;
|
|
+NOBLOCKMAP;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+INTERPOLATEANGLES;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+FORCEXYBILLBOARD;
|
|
+NOINTERACTION;
|
|
Gravity 0.35;
|
|
Scale 0.2;
|
|
FloatBobPhase 0;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
frame = Random[Junk](0,7);
|
|
scale *= Frandom[Junk](0.8,1.2);
|
|
SWWMHandler.QueueChip(self);
|
|
}
|
|
override void OnDestroy()
|
|
{
|
|
SWWMHandler.DeQueueChip(self);
|
|
Super.OnDestroy();
|
|
}
|
|
override void Tick()
|
|
{
|
|
prev = pos; // for interpolation
|
|
if ( isFrozen() ) return;
|
|
if ( dead )
|
|
{
|
|
// do nothing but follow floor movement
|
|
if ( tracksector )
|
|
{
|
|
double trackz;
|
|
if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy);
|
|
else trackz = tracksector.floorplane.ZAtPoint(pos.xy);
|
|
if ( trackz != pos.z )
|
|
{
|
|
SetZ(trackz);
|
|
UpdateWaterLevel(false);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( waterlevel <= 0 ) vel.z -= GetGravity();
|
|
// linetrace-based movement (hopefully more reliable than traditional methods)
|
|
Vector3 dir = vel;
|
|
double spd = vel.length();
|
|
dir /= spd;
|
|
double dist = spd;
|
|
FLineTraceData d;
|
|
Vector3 newpos = pos;
|
|
newpos.z = clamp(newpos.z,floorz,ceilingz);
|
|
int nstep = 0;
|
|
while ( dist > 0 )
|
|
{
|
|
// safeguard, too many bounces
|
|
if ( nstep > MAXBOUNCEPERTIC )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
double ang = atan2(dir.y,dir.x);
|
|
double pt = asin(-dir.z);
|
|
LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d);
|
|
Vector3 hitnormal = -d.HitDir;
|
|
if ( d.HitType == TRACE_HitFloor )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.top.Normal;
|
|
else hitnormal = d.HitSector.floorplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitCeiling )
|
|
{
|
|
if ( d.Hit3DFloor ) hitnormal = -d.Hit3DFloor.bottom.Normal;
|
|
else hitnormal = d.HitSector.ceilingplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitWall )
|
|
{
|
|
hitnormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
|
if ( !d.LineSide ) hitnormal *= -1;
|
|
}
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
dist -= d.Distance;
|
|
// should only happen if we bounced
|
|
dir = d.HitDir-(2*hitnormal*(d.HitDir dot hitnormal));
|
|
spd *= .3;
|
|
dist *= .3;
|
|
vel = dir*spd;
|
|
newpos = d.HitLocation+dir;
|
|
SetStateLabel("Bounce");
|
|
}
|
|
else
|
|
{
|
|
dist = 0.;
|
|
newpos = level.Vec3Offset(newpos,dir*spd);
|
|
}
|
|
newpos.z = clamp(newpos.z,floorz,ceilingz);
|
|
if ( ((spd < 1.) || (dir.z <= 0.)) && ((d.HitType == TRACE_HitFloor) || (newpos.z <= floorz)) )
|
|
{
|
|
// lose speed and die
|
|
if ( d.Hit3DFloor )
|
|
{
|
|
newpos.z = d.Hit3DFloor.top.ZAtPoint(newpos.xy);
|
|
tracksector = d.Hit3DFloor.model;
|
|
trackplane = 1;
|
|
}
|
|
else
|
|
{
|
|
// hacky workaround
|
|
if ( !d.HitSector ) d.HitSector = floorsector;
|
|
newpos.z = d.HitSector.floorplane.ZAtPoint(newpos.xy);
|
|
tracksector = d.HitSector;
|
|
trackplane = 0;
|
|
}
|
|
vel = (0,0,0);
|
|
pitch = 0;
|
|
roll = 0;
|
|
dead = true;
|
|
SetStateLabel("Death");
|
|
break;
|
|
}
|
|
nstep++;
|
|
}
|
|
SetOrigin(newpos,true);
|
|
UpdateWaterLevel();
|
|
}
|
|
if ( killme ) A_FadeOut(.01);
|
|
if ( waterlevel > 0 )
|
|
{
|
|
vel *= .98;
|
|
anglevel *= .98;
|
|
pitchvel *= .98;
|
|
rollvel *= .98;
|
|
}
|
|
if ( !CheckNoDelay() || (tics == -1) ) return;
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 # 1
|
|
{
|
|
angle += anglevel;
|
|
pitch += pitchvel;
|
|
roll += rollvel;
|
|
}
|
|
Loop;
|
|
Bounce:
|
|
XZW1 # 0
|
|
{
|
|
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
rollvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
}
|
|
Goto Spawn;
|
|
Death:
|
|
XZW2 # -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMNothing : Actor
|
|
{
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class PoofLight : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "Yellow";
|
|
ReactionTime 5;
|
|
Args 0,0,0,60;
|
|
}
|
|
}
|
|
Class PoofLight2 : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "Yellow";
|
|
ReactionTime 20;
|
|
Args 0,0,0,90;
|
|
}
|
|
}
|
|
|
|
Class SWWMItemFog : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+NOINTERACTION;
|
|
+FORCEXYBILLBOARD;
|
|
FloatBobPhase 0;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BLPF A 2 Bright NoDelay
|
|
{
|
|
// offset up
|
|
SetOrigin(Vec3Offset(0,0,16),false);
|
|
roll = FRandom[ExploS](0,360);
|
|
scale *= FRandom[ExploS](0.9,1.1);
|
|
scale.x *= RandomPick[ExploS](-1,1);
|
|
scale.y *= RandomPick[ExploS](-1,1);
|
|
int numpt = Random[ExploS](8,12);
|
|
if ( bAMBUSH ) numpt *= 2;
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8);
|
|
let s = Spawn(bAMBUSH?"SWWMSmoke":"SWWMSmallSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(3,2,1)*Random[ExploS](64,85));
|
|
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
|
|
s.scale *= 3.;
|
|
s.alpha *= bAMBUSH?.4:.2;
|
|
}
|
|
Spawn(bAMBUSH?"PoofLight2":"PoofLight",pos);
|
|
}
|
|
BLPF A 1 Bright A_FadeOut(.3);
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class TeleLight : PaletteLight
|
|
{
|
|
Default
|
|
{
|
|
Tag "ImpactWav";
|
|
ReactionTime 10;
|
|
Args 0,0,0,150;
|
|
}
|
|
}
|
|
|
|
Class SWWMTeleportSparkle : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Scale 0.3;
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+FORCEXYBILLBOARD;
|
|
+NOINTERACTION;
|
|
FloatBobPhase 0;
|
|
}
|
|
override void Tick()
|
|
{
|
|
if ( isFrozen() ) return;
|
|
A_SetScale(scale.x*specialf1);
|
|
A_FadeOut(specialf2);
|
|
if ( vel != (0,0,0) )
|
|
{
|
|
SetOrigin(level.Vec3Offset(pos,vel),true);
|
|
vel *= .98;
|
|
}
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BLPF C -1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMTeleportDest : Actor
|
|
{
|
|
Default
|
|
{
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOINTERACTION;
|
|
Radius .1;
|
|
Height 0.;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
special1 = Random[ExploS](0,10);
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
if ( isFrozen() ) return;
|
|
if ( (level.maptime+special1)%10 ) return;
|
|
int numpt = Random[ExploS](0,2);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
double ang = FRandom[ExploS](0,360);
|
|
double pt = FRandom[ExploS](-90,90);
|
|
A_SpawnParticle("88 AA FF",SPF_FULLBRIGHT,Random[ExploS](120,240),FRandom[ExploS](2.,4.),0,0,0,28,FRandom[ExploS](-.8,.8),FRandom[ExploS](-.8,.8),FRandom[ExploS](-.8,.8),0,0,0,FRandom[ExploS](.15,.3),-1,FRandom[ExploS](-.02,-.01));
|
|
}
|
|
}
|
|
}
|
|
|
|
Class SWWMTeleportLine : Actor
|
|
{
|
|
Line tline;
|
|
|
|
Default
|
|
{
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOINTERACTION;
|
|
Radius .1;
|
|
Height 0.;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
special1 = Random[ExploS](0,5);
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
if ( !tline )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( isFrozen() ) return;
|
|
if ( (level.maptime+special1)%5 ) return;
|
|
Vector3 apos, bpos;
|
|
apos.xy = tline.v1.p;
|
|
bpos.xy = tline.v2.p;
|
|
apos.z = max(tline.frontsector.floorplane.ZAtPoint(apos.xy),tline.backsector.floorplane.ZAtPoint(apos.xy));
|
|
bpos.z = max(tline.frontsector.floorplane.ZAtPoint(bpos.xy),tline.backsector.floorplane.ZAtPoint(bpos.xy));
|
|
int numpt = Random[ExploS](0,2);
|
|
numpt *= int(clamp((apos-bpos).length()/32,1,8));
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
double ang = FRandom[ExploS](0,360);
|
|
double pt = FRandom[ExploS](-90,90);
|
|
double d = FRandom[ExploS](0.,1.);
|
|
Vector3 ppos = bpos*d+apos*(1.-d)+(0,0,FRandom[ExploS](1,4));
|
|
Vector3 rpos = level.Vec3Diff(pos,ppos);
|
|
A_SpawnParticle("88 AA FF",SPF_FULLBRIGHT,Random[ExploS](120,240),FRandom[ExploS](2.,4.),0,rpos.x,rpos.y,rpos.z,FRandom[ExploS](-.3,.3),FRandom[ExploS](-.3,.3),FRandom[ExploS](-.3,.3),0,0,FRandom[ExploS](.05,.1),FRandom[ExploS](.15,.3),-1,FRandom[ExploS](-.02,-.01));
|
|
}
|
|
}
|
|
}
|
|
|
|
Class SWWMTeleportFog : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOINTERACTION;
|
|
+FORCEXYBILLBOARD;
|
|
Radius .1;
|
|
Height 0.;
|
|
FloatBobPhase 0;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_StartSound("misc/teleport",CHAN_VOICE);
|
|
Spawn("TeleLight",pos);
|
|
if ( swwm_simplefog ) SetStateLabel("Simple");
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1
|
|
{
|
|
int numpt = int(Random[ExploS](6,12)*alpha);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8)*alpha;
|
|
let s = Spawn("SWWMSmallSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(1,2,3)*int(Random[ExploS](64,85)*alpha));
|
|
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
|
|
s.scale *= 3.*alpha;
|
|
s.alpha *= alpha;
|
|
}
|
|
numpt = int(Random[ExploS](4,8));
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
double ang = FRandom[ExploS](0,360);
|
|
double pt = FRandom[ExploS](-90,90);
|
|
double dist = (FRandom[ExploS](5,10)+60*(1.-alpha));
|
|
if ( LineTrace(ang,dist,pt,TRF_THRUACTORS|TRF_THRUHITSCAN) ) continue;
|
|
Vector3 ofs = (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*dist;
|
|
Vector3 spos = level.Vec3Offset(pos,ofs);
|
|
let s = Spawn("SWWMTeleportSparkle",spos);
|
|
s.scale *= FRandom[ExploS](.8,1.2);
|
|
s.specialf1 = FRandom[ExploS](.93,.97);
|
|
s.specialf2 = FRandom[ExploS](.02,.04);
|
|
s.roll = FRandom[ExploS](0,360);
|
|
}
|
|
A_FadeOut(.07);
|
|
}
|
|
Wait;
|
|
Simple:
|
|
SPEX ABCDEFGHIJKLMNOPQRSTUVWXYZ[\] 1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMPickupFlash : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Args 0,3,2,1;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+NOINTERACTION;
|
|
+FORCEXYBILLBOARD;
|
|
FloatBobPhase 0;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
frame = Args[0];
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BLPF # 1 Bright NoDelay
|
|
{
|
|
// offset up
|
|
SetOrigin(Vec3Offset(0,0,16),false);
|
|
roll = FRandom[ExploS](0,360);
|
|
scale *= FRandom[ExploS](0.9,1.1);
|
|
scale.x *= RandomPick[ExploS](-1,1);
|
|
scale.y *= RandomPick[ExploS](-1,1);
|
|
int numpt = Random[ExploS](8,10);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[ExploS](-1,1),FRandom[ExploS](-1,1),FRandom[ExploS](-1,1)).unit()*FRandom[ExploS](.3,8);
|
|
let s = Spawn("SWWMSmallSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(Args[1],Args[2],Args[3])*Random[ExploS](64,85));
|
|
s.A_SetRenderStyle(s.alpha,STYLE_AddShaded);
|
|
s.scale *= 3.;
|
|
s.alpha *= .5;
|
|
}
|
|
}
|
|
BLPF # 1 Bright A_FadeOut(.2);
|
|
Wait;
|
|
}
|
|
}
|
|
Class SWWMPinkPickupFlash : SWWMPickupFlash
|
|
{
|
|
Default
|
|
{
|
|
Args 1,3,1,2;
|
|
}
|
|
}
|
|
Class SWWMCyanPickupFlash : SWWMPickupFlash
|
|
{
|
|
Default
|
|
{
|
|
Args 2,1,2,3;
|
|
}
|
|
}
|
|
Class SWWMGreenPickupFlash : SWWMPickupFlash
|
|
{
|
|
Default
|
|
{
|
|
Args 3,1,3,1;
|
|
}
|
|
}
|
|
Class SWWMBluePickupFlash : SWWMPickupFlash
|
|
{
|
|
Default
|
|
{
|
|
Args 4,1,1,3;
|
|
}
|
|
}
|
|
Class SWWMPurplePickupFlash : SWWMPickupFlash
|
|
{
|
|
Default
|
|
{
|
|
Args 5,2,1,3;
|
|
}
|
|
}
|
|
Class SWWMRedPickupFlash : SWWMPickupFlash
|
|
{
|
|
Default
|
|
{
|
|
Args 6,3,1,1;
|
|
}
|
|
}
|
|
Class SWWMWhitePickupFlash : SWWMPickupFlash
|
|
{
|
|
Default
|
|
{
|
|
Args 7,3,3,3;
|
|
}
|
|
}
|
|
|
|
// Bullet trails from DT
|
|
Class WaterHit
|
|
{
|
|
Sector sect;
|
|
Vector3 hitpos;
|
|
}
|
|
|
|
Class InvisibleSplasher : Actor
|
|
{
|
|
Default
|
|
{
|
|
Mass 100;
|
|
VSpeed -2;
|
|
Radius 2;
|
|
Radius 2;
|
|
+NOBLOCKMAP; // needed to prevent infinite loops with some 3D floor water (yes, you read that right)
|
|
FloatBobPhase 0;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 2;
|
|
Stop;
|
|
}
|
|
}
|
|
Class SmolInvisibleSplasher : InvisibleSplasher
|
|
{
|
|
Default
|
|
{
|
|
Mass 5;
|
|
}
|
|
}
|
|
|
|
Class SWWMBulletTrail : LineTracer
|
|
{
|
|
Array<WaterHit> WaterHitList;
|
|
Array<Line> ShootThroughList;
|
|
Actor ignoreme;
|
|
|
|
static play void DoTrail( Actor target, Vector3 pos, Vector3 dir, double dist, int bubblechance, bool smoky = false )
|
|
{
|
|
let t = new("SWWMBulletTrail");
|
|
t.ignoreme = target;
|
|
t.WaterHitList.Clear();
|
|
t.ShootThroughList.Clear();
|
|
t.Trace(pos,level.PointInSector(pos.xy),dir,dist,0);
|
|
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
|
|
{
|
|
// have to do both because WOW, HOW THE FUCK IS THIS INTENTIONAL???
|
|
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
|
|
t.ShootThroughList[i].Activate(target,0,SPAC_Impact);
|
|
}
|
|
for ( int i=0; i<t.WaterHitList.Size(); i++ )
|
|
{
|
|
let b = Actor.Spawn("InvisibleSplasher",t.WaterHitList[i].hitpos);
|
|
b.A_CheckTerrain();
|
|
}
|
|
for ( int i=5; i<t.Results.Distance; i+=10 )
|
|
{
|
|
if ( !Random[Boolet](0,bubblechance) ) continue;
|
|
let b = Actor.Spawn(smoky?"SWWMSmallSmoke":"SWWMBubble",level.Vec3Offset(pos,dir*i));
|
|
b.Scale *= FRandom[Boolet](.4,.6);
|
|
}
|
|
t.Destroy();
|
|
}
|
|
|
|
override ETraceStatus TraceCallback()
|
|
{
|
|
// liquid splashes
|
|
if ( Results.CrossedWater )
|
|
{
|
|
let hl = new("WaterHit");
|
|
hl.sect = Results.CrossedWater;
|
|
hl.hitpos = Results.CrossedWaterPos;
|
|
WaterHitList.Push(hl);
|
|
}
|
|
else if ( Results.Crossed3DWater )
|
|
{
|
|
let hl = new("WaterHit");
|
|
hl.sect = Results.Crossed3DWater;
|
|
hl.hitpos = Results.Crossed3DWaterPos;
|
|
WaterHitList.Push(hl);
|
|
}
|
|
if ( Results.HitType == TRACE_HitActor )
|
|
{
|
|
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
|
|
if ( Results.HitActor.bSHOOTABLE ) return TRACE_Stop;
|
|
return TRACE_Skip;
|
|
}
|
|
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
|
|
{
|
|
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
|
|
return TRACE_Stop;
|
|
ShootThroughList.Push(Results.HitLine);
|
|
return TRACE_Skip;
|
|
}
|
|
return TRACE_Stop;
|
|
}
|
|
}
|
|
|
|
// Blob shadows
|
|
Class SWWMShadow : Actor
|
|
{
|
|
Sector oldfloor;
|
|
|
|
static void Track( Actor other )
|
|
{
|
|
// prevent infinite recursion
|
|
if ( other is 'SWWMShadow' ) return;
|
|
// no shadows for overlay actors
|
|
if ( other is 'GhostArtifactX' ) return;
|
|
let s = SWWMShadow(Spawn("SWWMShadow",other.pos));
|
|
s.target = other;
|
|
s.Update(true);
|
|
}
|
|
private void Update( bool nointerpolate = false )
|
|
{
|
|
// update scale / alpha
|
|
if ( ((target is 'Inventory') && Inventory(target).Owner) || target.bKILLED || target.bINVISIBLE || (target.sprite == target.GetSpriteIndex('TNT1')) || (target.sprite == target.GetSpriteIndex('ACLO')) || (target.CurSector.GetTexture(0) == skyflatnum) )
|
|
alpha = 0.;
|
|
else
|
|
{
|
|
alpha = 1.-min(1.,.006*abs(target.pos.z-pos.z));
|
|
alpha *= target.alpha;
|
|
double relz = target.pos.z-pos.z;
|
|
if ( target.bFLOATBOB ) relz += BobSin(target.FloatBobPhase)*target.FloatBobStrength;
|
|
double bscale = (target.radius/16.)*(1.-min(1.,.003*relz));
|
|
// hax
|
|
if ( target is 'CompanionLamp' ) bscale *= 2.;
|
|
A_SetScale(bscale);
|
|
}
|
|
// update position
|
|
double curz = target.CurSector.NextLowestFloorAt(target.pos.x,target.pos.y,target.pos.z);
|
|
if ( (target.pos.xy == pos.xy) && (pos.z == curz) ) return;
|
|
SetOrigin((target.pos.x,target.pos.y,curz),true);
|
|
if ( nointerpolate )
|
|
prev = pos;
|
|
else if ( oldfloor != target.CurSector )
|
|
prev.z = pos.z; // prevent interpolation of steep height changes
|
|
// update slope alignment
|
|
if ( !oldfloor || (oldfloor != target.CurSector) )
|
|
SWWMUtility.SetToSlope(self,0);
|
|
oldfloor = target.CurSector;
|
|
}
|
|
override void Tick()
|
|
{
|
|
if ( !target )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
Update();
|
|
}
|
|
default
|
|
{
|
|
RenderStyle "Shaded";
|
|
StencilColor "000000";
|
|
DistanceCheck 'swwm_shadowdist';
|
|
Radius .1;
|
|
Height 0.;
|
|
+NOBLOCKMAP;
|
|
+NOINTERACTION;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
FloatBobPhase 0;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A -1;
|
|
Stop;
|
|
}
|
|
}
|