Restore weapon precaching. Add precaching for gesture items. Fix broken physics of light actors. Add light actor physics to confetti. Fix composite items not keeping their tracers on travel. Fix ground distance reporting meters when it should be showing kilometers. Reattach player shadow on travel. Fix tip 37 erroneously listing slots from 6 and up as FOILINVUL when it should be 7 and up.
1511 lines
33 KiB
Text
1511 lines
33 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;
|
|
}
|
|
}
|
|
|
|
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);
|
|
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;
|
|
+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);
|
|
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;
|
|
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;
|
|
}
|
|
dist -= d.Distance;
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
// 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+hitnormal;
|
|
}
|
|
else newpos = level.Vec3Offset(newpos,dir*spd);
|
|
nstep++;
|
|
}
|
|
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;
|
|
+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);
|
|
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;
|
|
}
|
|
}
|
|
|
|
// visual smoke, very strictly non-interacting, updates even when game is frozen
|
|
Class SWWMViewSmoke : SWWMHalfSmoke
|
|
{
|
|
Vector3 ofs, vvel;
|
|
|
|
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);
|
|
vvel += (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[Puff](0.04,0.16);
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
prev = pos; // for interpolation
|
|
if ( !target || !target.player )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
Vector3 x, y, z;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
|
|
Vector3 origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*ofs.x+y*ofs.y+z*ofs.z);
|
|
SetOrigin(origin,true);
|
|
UpdateWaterLevel();
|
|
bInvisible = (players[consoleplayer].camera != target);
|
|
ofs += vvel;
|
|
vvel *= 0.96;
|
|
vvel.z += 0.01;
|
|
if ( (waterlevel > 0) && !bAMBUSH ) Destroy();
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
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;
|
|
}
|
|
dist -= d.Distance;
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
// 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+hitnormal;
|
|
}
|
|
else newpos = level.Vec3Offset(newpos,dir*spd);
|
|
nstep++;
|
|
}
|
|
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 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
|
|
{
|
|
if ( pos.z > floorz ) 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;
|
|
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;
|
|
}
|
|
dist -= d.Distance;
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
// 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+hitnormal;
|
|
}
|
|
else newpos = level.Vec3Offset(newpos,dir*spd);
|
|
if ( (spd < 1.) && ((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 ( 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 2;
|
|
Height 2;
|
|
+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) && (pos.z > floorz) ) 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;
|
|
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;
|
|
}
|
|
dist -= d.Distance;
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
// 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+hitnormal;
|
|
SetStateLabel("Bounce");
|
|
}
|
|
else newpos = level.Vec3Offset(newpos,dir*spd);
|
|
if ( (spd < 1.) && ((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);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BLPF C -1 Bright;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
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, int 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++ )
|
|
{
|
|
t.ShootThroughList[i].Activate(target,0,SPAC_PCross);
|
|
if ( t.ShootThroughList[i].special == GlassBreak ) // fuck glass
|
|
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;
|
|
transient CVar sdist;
|
|
|
|
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.bSPECIAL) || target.bKILLED || target.bINVISIBLE || (target.sprite == target.GetSpriteIndex('TNT1')) )
|
|
alpha = 0.;
|
|
else
|
|
{
|
|
alpha = 1.-min(1.,.006*abs(target.pos.z-pos.z));
|
|
alpha *= target.alpha;
|
|
if ( !sdist ) sdist = CVar.GetCVar('swwm_shadowdist',players[consoleplayer]);
|
|
int sd = sdist.GetInt();
|
|
// breaks with chasecam, but it's fine
|
|
if ( sd > 0 ) alpha *= clamp(4.*((sd-Distance3D(players[consoleplayer].Camera))/sd),0.,1.);
|
|
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;
|
|
}
|
|
}
|