1011 lines
24 KiB
Text
1011 lines
24 KiB
Text
// Gore FX ported over from Soundless Mound, with some edits
|
|
|
|
// Base blood actor
|
|
Class mkBlood : SWWMNonInteractiveActor
|
|
{
|
|
Default
|
|
{
|
|
+PUFFGETSOWNER;
|
|
}
|
|
void A_Bleed( int str = 1 )
|
|
{
|
|
if ( !target ) return;
|
|
let b = Spawn('mkBloodSpray',pos);
|
|
Vector2 dirto = target.Vec2To(self).unit();
|
|
b.angle = atan2(dirto.y,dirto.x);
|
|
b.pitch = FRandom[Blood](-60,30);
|
|
b.translation = translation;
|
|
b.target = target;
|
|
b.args[0] = str;
|
|
if ( target.bloodcolor ) b.SetShade(Color(target.bloodcolor.r/2,target.bloodcolor.g/2,target.bloodcolor.b/2));
|
|
else b.SetShade(Color(80,0,0));
|
|
b.CopyBloodColor(target);
|
|
int numpt = Random[Blood](2,4)+str*2;
|
|
double sstr = (2.5+str)/3.;
|
|
Vector3 vdir = SWWMUtility.Vec3FromAngles(b.angle,b.pitch);
|
|
TextureID pufftex[8];
|
|
for ( int i=0; i<8; i++ ) pufftex[i] = TexMan.CheckForTexture("graphics/Particles/xpuff"..i..".png");
|
|
FSpawnParticleParams puff;
|
|
puff.color1 = b.fillcolor;
|
|
puff.style = STYLE_Shaded;
|
|
puff.accel = (0,0,-.25);
|
|
puff.startalpha = .75;
|
|
puff.fadestep = -1;
|
|
puff.pos = pos;
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 ndir = (vdir+SWWMUtility.Vec3FromAngles(FRandom[Blood](0,360),FRandom[Blood](-90,90))*.5).unit()*FRandom[Blood](.2,2.)*sstr;
|
|
for ( double j=1.; j>=.5; j-=.125 )
|
|
{
|
|
puff.texture = pufftex[Random[Blood](0,7)];
|
|
puff.lifetime = int(12*sstr);
|
|
puff.size = 2.*str*FRandom[Blood](.6,1.4)*j;
|
|
puff.vel = ndir*j;
|
|
puff.sizestep = -.02*sstr*j;
|
|
level.SpawnParticle(puff);
|
|
}
|
|
}
|
|
let s = mkBloodSmoke(SWWMStaticSprite.SpawnAt('mkBloodSmoke',pos));
|
|
s.scolor = b.fillcolor;
|
|
s.scale *= .4*str;
|
|
s.thickness = str-1;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1 NoDelay A_Bleed(3);
|
|
Stop;
|
|
TNT1 A 1 A_Bleed(2);
|
|
Stop;
|
|
TNT1 A 1 A_Bleed(1);
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// a burst of blood attached to a bleeding actor
|
|
Class mkBloodSpray : SWWMNonInteractiveActor
|
|
{
|
|
double str;
|
|
int cnt;
|
|
Vector3 attachofs;
|
|
double baseang;
|
|
color shadecol;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
if ( !target )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
str = FRandom[Blood](2.,4.)*args[0];
|
|
cnt = Random[Blood](2,3)*args[0];
|
|
attachofs.xy = RotateVector(target.Vec2To(self),-target.angle);
|
|
attachofs.z = pos.z-target.pos.z;
|
|
baseang = angle-target.angle;
|
|
}
|
|
private bool IsTargetFlying()
|
|
{
|
|
if ( !target ) return false;
|
|
if ( !target.bNOGRAVITY && (target.pos.z > target.floorz) && target.TestMobjZ() ) return true;
|
|
if ( ((!target.bNOGRAVITY && target.bBlasted) || (target.health <= 0)) && (target.vel.length() > 10.) ) return true;
|
|
return false;
|
|
}
|
|
override void Tick()
|
|
{
|
|
if ( !target )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( freezetics > 0 )
|
|
{
|
|
freezetics--;
|
|
return;
|
|
}
|
|
if ( isFrozen() ) return;
|
|
Vector3 setofs = SWWMUtility.RotateVector3(attachofs,target.angle);
|
|
SetOrigin(level.Vec3Offset(target.pos,setofs),false);
|
|
int sz = max(1,args[0]/2);
|
|
double ang, pt;
|
|
int cnt = sz-Random[Blood](0,4);
|
|
for ( int i=0; i<cnt; i++ )
|
|
{
|
|
let d = Spawn('mkBloodDrop',pos);
|
|
d.SetShade(fillcolor);
|
|
d.CopyBloodColor(self);
|
|
ang = baseang+target.angle+FRandom[Blood](-15.,15.)*str;
|
|
pt = pitch+FRandom[Blood](-15.,15.)*str;
|
|
Vector3 dir = SWWMUtility.Vec3FromAngles(ang,pt);
|
|
d.vel = dir*str*FRandom[Blood](.8,1.8);
|
|
d.scale *= str*.15*FRandom[Blood](.6,1.4);
|
|
}
|
|
bool flying = IsTargetFlying();
|
|
if ( flying ) str *= 1.-(.15/sz);
|
|
else str *= 1.-(.4/sz);
|
|
if ( (str <= .05) || ((cnt-- <= 0) && !flying) ) Destroy();
|
|
}
|
|
}
|
|
|
|
// drop of salsa
|
|
// becomes a decal on crash
|
|
Class mkBloodDrop : SWWMNonInteractiveActor
|
|
{
|
|
bool killme;
|
|
bool dead, onceiling;
|
|
mkBloodDrop prevblod, nextblod;
|
|
Sector tracksector;
|
|
int trackplane;
|
|
transient TextureID pufftex[8];
|
|
FSpawnParticleParams puff;
|
|
|
|
Default
|
|
{
|
|
+MISSILE;
|
|
+FORCEXYBILLBOARD;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
Scale .35;
|
|
Mass 1;
|
|
RenderStyle 'Translucent';
|
|
}
|
|
// try to reduce overhead as much as possible with this
|
|
override void Tick()
|
|
{
|
|
prev = pos; // for interpolation
|
|
if ( freezetics > 0 )
|
|
{
|
|
freezetics--;
|
|
return;
|
|
}
|
|
if ( isFrozen() ) return;
|
|
if ( killme ) A_FadeOut(.01);
|
|
if ( dead )
|
|
{
|
|
// do nothing but follow floor movement and eventually fade out
|
|
if ( tracksector )
|
|
{
|
|
double trackz;
|
|
if ( trackplane ) trackz = tracksector.ceilingplane.ZAtPoint(pos.xy)-.1;
|
|
else trackz = tracksector.floorplane.ZAtPoint(pos.xy);
|
|
if ( trackz != pos.z )
|
|
{
|
|
SetZ(trackz);
|
|
UpdateWaterLevel(false);
|
|
}
|
|
}
|
|
if ( (waterlevel > 0) || GetFloorTerrain().isliquid )
|
|
{
|
|
scale *= 1.005;
|
|
A_FadeOut(.01);
|
|
}
|
|
if ( onceiling && (special2 < 200) && (scale.x > .2) )
|
|
{
|
|
if ( special1-- ) return;
|
|
special2 += 10;
|
|
special1 = Random[Blood](20,30)+special2;
|
|
let d = Spawn('mkBloodDrop',Vec3Offset(0,0,-2));
|
|
d.master = self;
|
|
d.SetShade(fillcolor);
|
|
d.CopyBloodColor(self);
|
|
d.scale = Scale*FRandom[Blood](.4,.6);
|
|
}
|
|
return; // we don't need to update states when we're dead
|
|
}
|
|
else
|
|
{
|
|
if ( !pufftex[0] )
|
|
{
|
|
for ( int i=0; i<8; i++ ) pufftex[i] = TexMan.CheckForTexture("graphics/Particles/xpuff"..i..".png");
|
|
puff.color1 = fillcolor;
|
|
puff.style = STYLE_Shaded;
|
|
puff.fadestep = -1;
|
|
}
|
|
// gravitational pull
|
|
if ( waterlevel <= 0 ) vel.z -= GetGravity();
|
|
// linetrace-based movement (hopefully more reliable than traditional methods)
|
|
Vector3 dir = vel;
|
|
double spd = vel.length();
|
|
dir /= spd;
|
|
FLineTraceData d;
|
|
Vector3 newpos = pos;
|
|
newpos.z = clamp(newpos.z,floorz,ceilingz);
|
|
double ang = atan2(dir.y,dir.x);
|
|
double pt = asin(-dir.z);
|
|
LineTrace(ang,spd,pt,TRF_THRUACTORS|TRF_THRUHITSCAN|TRF_ABSPOSITION,newpos.z,newpos.x,newpos.y,d);
|
|
if ( d.HitType != TRACE_HitNone )
|
|
{
|
|
Vector3 hitnormal = SWWMUtility.GetLineTraceHitNormal(d);
|
|
newpos = d.HitLocation+hitnormal;
|
|
}
|
|
else newpos = level.Vec3Offset(newpos,vel);
|
|
newpos.z = clamp(newpos.z,floorz,ceilingz);
|
|
Vector3 ndiff = level.Vec3Diff(newpos,pos);
|
|
double ndist = ndiff.length();
|
|
ndiff /= ndist;
|
|
puff.lifetime = 10;
|
|
puff.startalpha = .5*alpha;
|
|
puff.sizestep = -1.*scale.x;
|
|
puff.accel = (0,0,-.5);
|
|
for ( int i=0; i<ndist; i++ )
|
|
{
|
|
if ( Random[Blood](0,2) ) continue;
|
|
puff.texture = pufftex[Random[Blood](0,7)];
|
|
puff.size = 10.*scale.x*FRandom[Blood](.6,1.4);
|
|
puff.pos = level.Vec3Offset(pos,ndiff*i);
|
|
puff.vel = vel*.1+(FRandom[Blood](-.1,.1),FRandom[Blood](-.1,.1),FRandom[Blood](-.1,.1));
|
|
level.SpawnParticle(puff);
|
|
}
|
|
SetOrigin(newpos,true);
|
|
if ( (d.HitType == TRACE_HitFloor) || (pos.z <= floorz) )
|
|
{
|
|
// hacky workaround
|
|
if ( !d.HitSector )
|
|
{
|
|
d.HitSector = floorsector;
|
|
d.HitTexture = floorsector.GetTexture(0);
|
|
}
|
|
if ( d.HitTexture == skyflatnum )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
// landed on floor
|
|
SetOrigin(d.HitLocation,true);
|
|
HitFloor();
|
|
A_StartSound("misc/blooddrop",volume:.1);
|
|
if ( master )
|
|
{
|
|
// assume we dropped onto the previous spot
|
|
if ( master.tracer )
|
|
{
|
|
Vector3 floordir;
|
|
if ( d.Hit3DFloor ) floordir = -d.Hit3DFloor.model.ceilingplane.Normal;
|
|
else floordir = d.HitSector.floorplane.Normal;
|
|
int numpt = Random[Blood](2,4);
|
|
puff.lifetime = 20;
|
|
puff.startalpha = .5*alpha;
|
|
puff.pos = pos;
|
|
puff.accel = (0,0,-.25);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 ndir = (floordir+SWWMUtility.Vec3FromAngles(FRandom[Blood](0,360),FRandom[Blood](-90,90))*.5).unit()*FRandom[Blood](.5,8.);
|
|
for ( double j=1.; j>=.5; j-=.125 )
|
|
{
|
|
puff.texture = pufftex[Random[Blood](0,7)];
|
|
puff.size = 10.*scale.x*FRandom[Blood](.6,1.4)*j;
|
|
puff.pos = pos;
|
|
puff.vel = ndir*j;
|
|
puff.sizestep = -1.*scale.x*j;
|
|
level.SpawnParticle(puff);
|
|
}
|
|
}
|
|
Destroy();
|
|
return;
|
|
}
|
|
master.tracer = self;
|
|
}
|
|
bMISSILE = false;
|
|
dead = true;
|
|
Vector3 floordir;
|
|
if ( d.Hit3DFloor )
|
|
{
|
|
tracksector = d.Hit3DFloor.model;
|
|
trackplane = 1;
|
|
floordir = -d.Hit3DFloor.model.ceilingplane.Normal;
|
|
}
|
|
else
|
|
{
|
|
tracksector = d.HitSector;
|
|
trackplane = 0;
|
|
floordir = d.HitSector.floorplane.Normal;
|
|
}
|
|
SWWMUtility.SetToSlope(self,FRandom[Blood](0,360));
|
|
A_SetRenderStyle(1.,STYLE_Shaded);
|
|
frame = Random[Blood](5,12);
|
|
int numpt = Random[Blood](4,8);
|
|
puff.lifetime = 20;
|
|
puff.startalpha = .5*alpha;
|
|
puff.pos = pos;
|
|
puff.accel = (0,0,-.25);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 ndir = (floordir+SWWMUtility.Vec3FromAngles(FRandom[Blood](0,360),FRandom[Blood](-90,90))*.5).unit()*FRandom[Blood](.5,8.);
|
|
for ( double j=1.; j>=.5; j-=.125 )
|
|
{
|
|
puff.texture = pufftex[Random[Blood](0,7)];
|
|
puff.size = 10.*scale.x*FRandom[Blood](.6,1.4)*j;
|
|
puff.vel = ndir*j;
|
|
puff.sizestep = -1.*scale.x*j;
|
|
level.SpawnParticle(puff);
|
|
}
|
|
}
|
|
vel *= 0;
|
|
return;
|
|
}
|
|
if ( (d.HitType == TRACE_HitCeiling) || (pos.z >= ceilingz) )
|
|
{
|
|
// hacky workaround
|
|
if ( !d.HitSector )
|
|
{
|
|
d.HitSector = ceilingsector;
|
|
d.HitTexture = ceilingsector.GetTexture(1);
|
|
}
|
|
if ( (d.HitTexture == skyflatnum) || master )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
// hit the ceiling
|
|
SetOrigin(d.HitLocation-(0,0,1),true);
|
|
A_StartSound("misc/blooddrop",volume:.1);
|
|
bMISSILE = false;
|
|
dead = true;
|
|
onceiling = true;
|
|
Vector3 floordir;
|
|
if ( d.Hit3DFloor )
|
|
{
|
|
tracksector = d.Hit3DFloor.model;
|
|
trackplane = 0;
|
|
floordir = -d.Hit3DFloor.model.floorplane.Normal;
|
|
}
|
|
else
|
|
{
|
|
tracksector = d.HitSector;
|
|
trackplane = 1;
|
|
floordir = d.HitSector.ceilingplane.Normal;
|
|
}
|
|
SWWMUtility.SetToSlope(self,FRandom[Blood](0,360),true);
|
|
A_SetRenderStyle(1.,STYLE_Shaded);
|
|
frame = Random[Blood](13,20);
|
|
int numpt = Random[Blood](4,8);
|
|
puff.lifetime = 20;
|
|
puff.startalpha = .5*alpha;
|
|
puff.pos = pos;
|
|
puff.accel = (0,0,-.25);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 ndir = (floordir+SWWMUtility.Vec3FromAngles(FRandom[Blood](0,360),FRandom[Blood](-90,90))*.5).unit()*FRandom[Blood](.5,8.);
|
|
for ( double j=1.; j>=.5; j-=.125 )
|
|
{
|
|
puff.texture = pufftex[Random[Blood](0,7)];
|
|
puff.size = 10.*scale.x*FRandom[Blood](.6,1.4)*j;
|
|
puff.vel = ndir*j;
|
|
puff.sizestep = -1.*scale.x*j;
|
|
level.SpawnParticle(puff);
|
|
}
|
|
}
|
|
vel *= 0;
|
|
return;
|
|
}
|
|
if ( d.HitType == TRACE_HitWall )
|
|
{
|
|
if ( d.HitLine.backsector && (d.HitLine.backsector.GetTexture(1) == skyflatnum) && (d.HitLocation.z >= d.HitLine.backsector.ceilingplane.ZAtPoint(d.HitLocation.xy)) )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
// hit wall
|
|
Vector2 walldir = (-d.HitLine.delta.y,d.HitLine.delta.x).unit();
|
|
if ( d.LineSide ) walldir *= -1;
|
|
SetOrigin(d.HitLocation-walldir*8,true);
|
|
TraceBleedAngle(20,atan2(walldir.y,walldir.x),0);
|
|
A_StartSound("misc/blooddrop",volume:.1);
|
|
int numpt = Random[Blood](4,8);
|
|
puff.lifetime = 20;
|
|
puff.startalpha = .5*alpha;
|
|
puff.pos = pos;
|
|
puff.accel = (0,0,-.25);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 ndir = (-(walldir.x,walldir.y,0)+SWWMUtility.Vec3FromAngles(FRandom[Blood](0,360),FRandom[Blood](-90,90))*.5).unit()*FRandom[Blood](.5,8.);
|
|
for ( double j=1.; j>=.5; j-=.125 )
|
|
{
|
|
puff.texture = pufftex[Random[Blood](0,7)];
|
|
puff.size = 10.*scale.x*FRandom[Blood](.6,1.4)*j;
|
|
puff.vel = ndir*j;
|
|
puff.sizestep = -1.*scale.x*j;
|
|
level.SpawnParticle(puff);
|
|
}
|
|
}
|
|
Destroy();
|
|
return;
|
|
}
|
|
UpdateWaterLevel();
|
|
if ( waterlevel > 0 ) A_FadeOut();
|
|
scale *= .99;
|
|
if ( scale.x <= 0. )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
}
|
|
if ( !CheckNoDelay() || (tics == -1) ) return;
|
|
if ( tics > 0 ) tics--;
|
|
while ( !tics )
|
|
{
|
|
if ( !SetState(CurState.NextState) )
|
|
return;
|
|
}
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
SWWMHandler.QueueBlod(self);
|
|
int jumps = Random[Blood](0,3);
|
|
state dest = ResolveState('Spawn');
|
|
SetState(dest+jumps);
|
|
}
|
|
override void OnDestroy()
|
|
{
|
|
SWWMHandler.DeQueueBlod(self);
|
|
Super.OnDestroy();
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
SBLD ABCD 2;
|
|
Loop;
|
|
}
|
|
}
|
|
|
|
// chunky salsa in the air
|
|
Class mkBloodSmoke : SWWMStaticSprite
|
|
{
|
|
int thickness;
|
|
|
|
override void SetupSprite()
|
|
{
|
|
texture = TexMan.CheckForTexture(String.Format("MSMK%c0",0x41+Random[Blood](0,7)));
|
|
Scale = (.5,.5);
|
|
Alpha = .35;
|
|
SetRenderStyle(STYLE_Shaded);
|
|
scolor = gameinfo.defaultbloodcolor;
|
|
Flags |= SPF_ROLL;
|
|
bCheckWater = true;
|
|
bWallStop = true;
|
|
}
|
|
|
|
override void OnTick()
|
|
{
|
|
if ( lastwater ) alpha = max(0.,alpha-.1);
|
|
alpha = max(0.,alpha-(.04/max(1.,thickness)));
|
|
scale *= 1.+(.04/max(1.,thickness));
|
|
vel *= 1.-(.04/max(1.,thickness));
|
|
if ( alpha <= 0. ) Destroy();
|
|
}
|
|
}
|
|
|
|
// flying gibs
|
|
Class mkFlyingGib : Actor
|
|
{
|
|
Mixin SWWMMissileFix;
|
|
|
|
bool killme;
|
|
int lastbleed;
|
|
color shadecol;
|
|
bool bleeding;
|
|
double rollvel, pitchvel;
|
|
mkFlyingGib prevmeat, nextmeat;
|
|
Vector3 oldpos;
|
|
TextureID pufftex[8];
|
|
FSpawnParticleParams puff;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
for ( int i=0; i<8; i++ ) pufftex[i] = TexMan.CheckForTexture("graphics/Particles/xpuff"..i..".png");
|
|
puff.style = STYLE_Shaded;
|
|
puff.lifetime = 40;
|
|
puff.accel = (0,0,-.25);
|
|
puff.fadestep = -1;
|
|
frame = Random[Blood](0,7);
|
|
double ang = FRandom[Gibs](0,360);
|
|
double pt = FRandom[Gibs](-60,20);
|
|
Vector3 dir = SWWMUtility.Vec3FromAngles(ang,pt);
|
|
vel += dir*FRandom[Gibs](4.,8.);
|
|
if ( master )
|
|
{
|
|
vel += master.vel;
|
|
CopyBloodColor(master);
|
|
}
|
|
rollvel = FRandom[Gibs](10,50)*RandomPick[Gibs](-1,1)*clamp(vel.length()/10.,.25,4.);
|
|
pitchvel = FRandom[Gibs](10,50)*RandomPick[Gibs](-1,1)*clamp(vel.length()/10.,.25,4.);
|
|
scale *= FRandom[Gibs](.5,1.5);
|
|
if ( master && master.bloodcolor ) shadecol = Color(master.bloodcolor.r/2,master.bloodcolor.g/2,master.bloodcolor.b/2);
|
|
else shadecol = Color(80,0,0);
|
|
puff.color1 = shadecol;
|
|
bleeding = true;
|
|
if ( Random[Blood](0,1) ) bXFlip = true;
|
|
SWWMHandler.QueueMeat(self);
|
|
}
|
|
|
|
override void OnDestroy()
|
|
{
|
|
SWWMHandler.DeQueueMeat(self);
|
|
Super.OnDestroy();
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
oldpos = pos;
|
|
Super.Tick();
|
|
if ( isFrozen() || (freezetics > 0) ) return;
|
|
if ( killme ) A_FadeOut(.01);
|
|
if ( CurState == ResolveState('Death2') )
|
|
{
|
|
if ( vel.length() < .1 )
|
|
bleeding = false;
|
|
return;
|
|
}
|
|
roll += rollvel;
|
|
pitch += pitchvel;
|
|
if ( waterlevel > 0 )
|
|
{
|
|
rollvel *= .99;
|
|
pitchvel *= .99;
|
|
return;
|
|
}
|
|
if ( !bleeding ) return;
|
|
Vector3 ndiff = level.Vec3Diff(pos,oldpos);
|
|
double ndist = ndiff.length();
|
|
ndiff /= ndist;
|
|
for ( int i=0; i<ndist; i++ )
|
|
{
|
|
if ( Random[Blood](0,2) ) continue;
|
|
puff.texture = pufftex[Random[Blood](0,7)];
|
|
puff.size = 20.*scale.x*FRandom[Blood](.6,1.4);
|
|
puff.pos = level.Vec3Offset(oldpos,ndiff*i);
|
|
puff.vel = vel*.1+(FRandom[Blood](-.1,.1),FRandom[Blood](-.1,.1),FRandom[Blood](-.1,.1));
|
|
puff.startalpha = .5*alpha;
|
|
puff.sizestep = -.5*scale.x;
|
|
level.SpawnParticle(puff);
|
|
}
|
|
}
|
|
|
|
override bool CanCollideWith(Actor other, bool passive)
|
|
{
|
|
if ( other == master ) return false;
|
|
return true;
|
|
}
|
|
|
|
void A_Bleed()
|
|
{
|
|
if ( vel.length() < .5 ) ExplodeMissile(null,null);
|
|
else
|
|
{
|
|
Vector3 dir = vel;
|
|
double spd = dir.length();
|
|
dir /= spd;
|
|
dir = (dir+SWWMUtility.Vec3FromAngles(FRandom[Blood](0,360),FRandom[Blood](-90,90))*.3).unit();
|
|
vel = dir*spd;
|
|
}
|
|
rollvel = FRandom[Gibs](10,50)*RandomPick[Gibs](-1,1)*clamp(vel.length()/10.,.25,4.);
|
|
pitchvel = FRandom[Gibs](10,50)*RandomPick[Gibs](-1,1)*clamp(vel.length()/10.,.25,4.);
|
|
if ( lastbleed > level.maptime ) return;
|
|
lastbleed = level.maptime+5;
|
|
TraceBleedAngle(int(vel.length()),angle+180,-pitch);
|
|
}
|
|
|
|
Default
|
|
{
|
|
Radius 4;
|
|
Height 4;
|
|
Mass 10;
|
|
Scale .75;
|
|
Gravity .5;
|
|
BounceType 'Doom';
|
|
BounceFactor .2;
|
|
+MISSILE;
|
|
+DROPOFF;
|
|
+NOBLOCKMAP;
|
|
+USEBOUNCESTATE;
|
|
+CANBOUNCEWATER;
|
|
-BOUNCEAUTOOFF;
|
|
+MOVEWITHSECTOR;
|
|
+THRUACTORS;
|
|
+NOTELEPORT;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+INTERPOLATEANGLES;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 # 1;
|
|
Wait;
|
|
Bounce:
|
|
XZW1 # 0
|
|
{
|
|
A_Bleed();
|
|
A_StartSound("misc/gibhit",CHAN_BODY,CHANF_OVERLAP);
|
|
// gib stuck!
|
|
if ( (floorz >= ceilingz) || !level.IsPointInLevel(pos) )
|
|
Destroy();
|
|
}
|
|
Goto Spawn;
|
|
Death:
|
|
XZW1 # 1 A_JumpIf(pos.z<=floorz,'Death2');
|
|
Wait;
|
|
Death2:
|
|
XZW1 # -1
|
|
{
|
|
A_StartSound("misc/gibhit",CHAN_BODY,CHANF_OVERLAP);
|
|
pitch = Random[Gibs](-5,5);
|
|
if ( abs(roll) < abs(180-roll) ) roll = Random[Gibs](-5,5);
|
|
else roll = Random[Gibs](175,185);
|
|
A_Stop();
|
|
// floor drop
|
|
let b = Spawn('mkBloodDrop',pos);
|
|
b.scale *= 2.0;
|
|
b.SetShade(shadecol);
|
|
}
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Manually added gibbing
|
|
Class mkGibber : SWWMNonInteractiveActor
|
|
{
|
|
Actor Gibbed;
|
|
int gibcount, gibsize;
|
|
int delay;
|
|
color shadecol;
|
|
meta Class<Actor> gibtype; // allow custom gib types (will be used for monster pack)
|
|
bool psnd;
|
|
bool mksplat;
|
|
|
|
Property GibType: gibtype;
|
|
|
|
virtual void BurstGibs()
|
|
{
|
|
double ang, pt;
|
|
Vector3 dir;
|
|
int bloodthrottle = 0, gibthrottle = 0;
|
|
let hnd = SWWMHandler(EventHandler.Find('SWWMHandler'));
|
|
if ( hnd )
|
|
{
|
|
if ( hnd.oldmaxblood != 0 )
|
|
bloodthrottle = max((hnd.blods_realcnt-hnd.oldmaxblood)/100,0);
|
|
if ( hnd.oldmaxgibs != 0 )
|
|
gibthrottle = max((hnd.meats_realcnt-hnd.oldmaxgibs)/100,0);
|
|
}
|
|
for ( int i=0; i<gibsize; i++ )
|
|
{
|
|
if ( bloodthrottle && (!i || (i%bloodthrottle)) ) continue;
|
|
let a = mkBloodSmoke(SWWMStaticSprite.SpawnAt('mkBloodSmoke',pos+(FRandom[Gibs](-.8,.8)*radius,FRandom[Gibs](-.8,.8)*radius,FRandom[Gibs](0.,.9)*height)));
|
|
a.scale = (.5,.5);
|
|
a.alpha = 1.;
|
|
ang = FRandom[Gibs](0,360);
|
|
pt = FRandom[Gibs](-90,90);
|
|
dir = SWWMUtility.Vec3FromAngles(ang,pt);
|
|
a.vel = vel*.1;
|
|
if ( a.vel.length() > 2. ) a.vel = a.vel.unit()*2.;
|
|
a.vel += dir*FRandom[Gibs](.2,.8);
|
|
a.scolor = shadecol;
|
|
a.thickness = Random[Gibs](1,3);
|
|
}
|
|
for ( int i=0; i<gibsize; i++ )
|
|
{
|
|
if ( gibthrottle && (!i || (i%gibthrottle)) ) continue;
|
|
let [b, a] = A_SpawnItemEx(gibtype,FRandom[Gibs](-.5,.5)*radius,FRandom[Gibs](-.5,.5)*radius,FRandom[Gibs](.1,.9)*height,flags:SXF_ABSOLUTEANGLE|SXF_USEBLOODCOLOR);
|
|
if ( !b ) continue;
|
|
a.translation = translation;
|
|
a.CopyBloodColor(self);
|
|
a.scale *= scale.x;
|
|
a.master = gibbed;
|
|
if ( special1 )
|
|
{
|
|
ang = FRandom[Gibs](0,360);
|
|
pt = FRandom[Gibs](-90,90);
|
|
dir = SWWMUtility.Vec3FromAngles(ang,pt);
|
|
a.vel += dir*FRandom[Gibs](4.,8.);
|
|
a.vel.z += 16.;
|
|
}
|
|
}
|
|
for ( int i=0; i<gibsize; i++ )
|
|
{
|
|
if ( bloodthrottle && (!i || (i%bloodthrottle)) ) continue;
|
|
let [b, a] = A_SpawnItemEx('mkBloodDrop',FRandom[Gibs](-.8,.8)*radius,FRandom[Gibs](-.8,.8)*radius,FRandom[Gibs](0.,.9)*height,flags:SXF_ABSOLUTEANGLE|SXF_USEBLOODCOLOR);
|
|
if ( !b ) continue;
|
|
ang = FRandom[Gibs](0,360);
|
|
pt = FRandom[Gibs](-90,90);
|
|
dir = SWWMUtility.Vec3FromAngles(ang,pt);
|
|
a.vel = dir*FRandom[Gibs](8.,24.);
|
|
a.vel += vel*.1;
|
|
a.scale *= 2.+FRandom[Gibs](.3,1.6);
|
|
a.SetShade(shadecol);
|
|
a.CopyBloodColor(self);
|
|
}
|
|
reactiontime--;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
gibsize = int(min(max(radius,height)/8,12));
|
|
reactiontime = int(min(max(radius,height)/10,8));
|
|
if ( gibbed && gibbed.bloodcolor ) shadecol = Color(gibbed.bloodcolor.r/2,gibbed.bloodcolor.g/2,gibbed.bloodcolor.b/2);
|
|
else shadecol = Color(80,0,0);
|
|
if ( gibbed ) CopyBloodColor(gibbed);
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
if ( freezetics > 0 )
|
|
{
|
|
freezetics--;
|
|
return;
|
|
}
|
|
if ( isFrozen() ) return;
|
|
if ( !gibbed )
|
|
{
|
|
SetOrigin(level.Vec3Offset(pos,vel),false);
|
|
return;
|
|
}
|
|
SetOrigin(gibbed.pos,false);
|
|
scale = gibbed.scale;
|
|
vel = gibbed.vel;
|
|
if ( delay > 0 )
|
|
{
|
|
delay--;
|
|
return;
|
|
}
|
|
if ( !psnd )
|
|
{
|
|
A_StartSound("misc/gibber",CHAN_VOICE,CHANF_OVERLAP);
|
|
psnd = true;
|
|
}
|
|
if ( mksplat )
|
|
{
|
|
let s = Spawn('mkBloodBlast',pos);
|
|
s.SetShade(shadecol);
|
|
s.master = gibbed;
|
|
mksplat = false;
|
|
}
|
|
BurstGibs();
|
|
if ( reactiontime <= 0 )
|
|
Destroy();
|
|
}
|
|
|
|
Default
|
|
{
|
|
Radius 32;
|
|
Height 16;
|
|
mkGibber.GibType 'mkFlyingGib';
|
|
}
|
|
}
|
|
|
|
// for exploding Cyberdemon/Spider
|
|
Class mkBloodBlast : SWWMNonInteractiveActor
|
|
{
|
|
override void Tick()
|
|
{
|
|
if ( freezetics > 0 )
|
|
{
|
|
freezetics--;
|
|
return;
|
|
}
|
|
if ( isFrozen() ) return;
|
|
double fz = CurSector.floorplane.ZAtPoint(pos.xy);
|
|
if ( fz != pos.z ) SetOrigin((pos.x,pos.y,fz),true);
|
|
if ( (waterlevel > 0) || GetFloorTerrain().isliquid )
|
|
A_FadeOut();
|
|
if ( !master ) A_FadeOut(.01);
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
double fz = CurSector.floorplane.ZAtPoint(pos.xy);
|
|
SetZ(fz);
|
|
prev.z = fz;
|
|
A_QueueCorpse();
|
|
SWWMUtility.SetToSlope(self,FRandom[Blood](0,360));
|
|
}
|
|
default
|
|
{
|
|
RenderStyle 'Shaded';
|
|
StencilColor "FF 00 00";
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// bare actors used for copying blood color to vanilla monsters
|
|
Class GreenBloodReference : Actor
|
|
{
|
|
Default
|
|
{
|
|
BloodColor "00 FF 00";
|
|
}
|
|
}
|
|
Class BlueBloodReference : Actor
|
|
{
|
|
Default
|
|
{
|
|
BloodColor "00 00 FF";
|
|
}
|
|
}
|
|
Class PurpleBloodReference : Actor
|
|
{
|
|
Default
|
|
{
|
|
BloodColor "80 00 FF";
|
|
}
|
|
}
|
|
|
|
// corpse thump handler
|
|
Class CorpseFallTracker : Thinker
|
|
{
|
|
Actor mybody;
|
|
double lastvelz;
|
|
bool wasflying;
|
|
|
|
static void TrackBody( Actor b )
|
|
{
|
|
if ( !b ) return;
|
|
let cft = new('CorpseFallTracker');
|
|
cft.ChangeStatNum(STAT_USER);
|
|
cft.mybody = b;
|
|
cft.lastvelz = b.vel.z;
|
|
cft.wasflying = ((b.pos.z>b.floorz)&&b.TestMobjZ());
|
|
}
|
|
override void Tick()
|
|
{
|
|
if ( !mybody )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
// play fall thumps
|
|
bool isflying = ((mybody.pos.z>mybody.floorz)&&mybody.TestMobjZ());
|
|
if ( wasflying && !isflying && (lastvelz < -10) )
|
|
mybody.A_StartSound("misc/bodythump",CHAN_DAMAGE,CHANF_OVERLAP);
|
|
wasflying = isflying;
|
|
lastvelz = mybody.vel.z;
|
|
// wait until body is dead on floor and at the last state of animation
|
|
if ( (mybody.Health > 0) || isflying || (mybody.tics != -1) || (mybody.vel.length() > 0) )
|
|
return;
|
|
let b = mybody.Spawn('mkBloodPool',mybody.pos);
|
|
Color shadecol;
|
|
if ( mybody.bloodcolor ) shadecol = Color(mybody.bloodcolor.r/2,mybody.bloodcolor.g/2,mybody.bloodcolor.b/2);
|
|
else shadecol = Color(80,0,0);
|
|
mkBloodPool(b).stepcol = shadecol;
|
|
b.master = mybody;
|
|
b.A_SetScale(mybody.default.radius/16.);
|
|
// futureproofing hack (heh)
|
|
let mtype = SWWMUtility.GetParentClassBefore(mybody.GetClass(),'Actor');
|
|
if ( mtype.GetClassName() == 'SWWMMonster' )
|
|
{
|
|
b.A_SetRenderStyle(1.,STYLE_Shaded);
|
|
b.SetShade(shadecol);
|
|
}
|
|
else b.translation = mybody.bloodtranslation;
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
// Blood pool
|
|
Class mkBloodPool : SWWMNonInteractiveActor
|
|
{
|
|
double basesz, sz, accel;
|
|
Color stepcol;
|
|
mkBloodPool prevpool, nextpool;
|
|
bool bRaised; // dead body was revived, fade out faster
|
|
|
|
Property BaseAccel : accel;
|
|
|
|
override void OnDestroy()
|
|
{
|
|
Super.OnDestroy();
|
|
if ( prevpool )
|
|
{
|
|
prevpool.nextpool = nextpool;
|
|
if ( nextpool ) nextpool.prevpool = prevpool;
|
|
}
|
|
let hnd = SWWMHandler(EventHandler.Find('SWWMHandler'));
|
|
if ( !hnd || (hnd.bloodpools != self) ) return;
|
|
hnd.bloodpools = nextpool;
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
double fz = CurSector.floorplane.ZAtPoint(pos.xy);
|
|
SetZ(fz);
|
|
prev.z = fz;
|
|
basesz = scale.x;
|
|
sz = .01;
|
|
A_SetScale(sz);
|
|
A_QueueCorpse();
|
|
SWWMUtility.SetToSlope(self,FRandom[Blood](0,360));
|
|
let hnd = SWWMHandler(EventHandler.Find('SWWMHandler'));
|
|
if ( !hnd ) return;
|
|
nextpool = hnd.bloodpools;
|
|
hnd.bloodpools = self;
|
|
if ( nextpool ) nextpool.prevpool = self;
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
if ( freezetics > 0 )
|
|
{
|
|
freezetics--;
|
|
return;
|
|
}
|
|
if ( isFrozen() ) return;
|
|
double fz = CurSector.floorplane.ZAtPoint(pos.xy);
|
|
if ( fz != pos.z ) SetOrigin((pos.x,pos.y,fz),true);
|
|
if ( (waterlevel > 0) || GetFloorTerrain().isliquid )
|
|
A_FadeOut();
|
|
if ( !master ) A_FadeOut(.01);
|
|
else if ( (master.Health > 0) || bRaised )
|
|
{
|
|
bRaised = true;
|
|
A_FadeOut();
|
|
}
|
|
if ( accel <= 0. ) return;
|
|
sz += accel;
|
|
double fact = min(special1++/1200.,1.);
|
|
accel = SWWMUtility.Lerp(default.accel,0.,fact*fact);
|
|
A_SetScale(basesz*sz);
|
|
A_SetSize(50.*basesz*sz);
|
|
}
|
|
|
|
Default
|
|
{
|
|
Radius 1;
|
|
Height 1;
|
|
StencilColor "FF 00 00";
|
|
mkBloodPool.BaseAccel .0006;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A -1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Bloody footsteps for player
|
|
// (Spawning handled also by player)
|
|
Class mkBloodStep : SWWMNonInteractiveActor
|
|
{
|
|
override void PostBeginPlay()
|
|
{
|
|
double fz = CurSector.floorplane.ZAtPoint(pos.xy);
|
|
SetZ(fz);
|
|
prev.z = fz;
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
if ( freezetics > 0 )
|
|
{
|
|
freezetics--;
|
|
return;
|
|
}
|
|
if ( isFrozen() ) return;
|
|
double fz = CurSector.floorplane.ZAtPoint(pos.xy);
|
|
if ( fz != pos.z ) SetOrigin((pos.x,pos.y,fz),true);
|
|
if ( (waterlevel > 0) || GetFloorTerrain().isliquid )
|
|
A_FadeOut();
|
|
special1++;
|
|
// start fading out after one whole minute
|
|
if ( special1 > 2100 ) A_FadeOut(.01);
|
|
}
|
|
|
|
Default
|
|
{
|
|
RenderStyle 'Shaded';
|
|
StencilColor "FF 00 00";
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A -1;
|
|
Stop;
|
|
}
|
|
}
|