Updated blood and gore code, it's less WIP now. Updated readme with some additional notes. Removed Heretic compat player classes as they are no longer needed.
605 lines
13 KiB
Text
605 lines
13 KiB
Text
// Blood
|
|
Class UTBlood : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Scale 1.1;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
+PUFFGETSOWNER;
|
|
}
|
|
void A_Bleed( int str = 1 )
|
|
{
|
|
Scale *= 0.7+0.3*str;
|
|
vel *= 0;
|
|
let b = Spawn("UTBloodSpurt",pos);
|
|
b.angle = angle+FRandom[Blud](-15,15);
|
|
b.pitch = pitch+FRandom[Blud](-15,15);
|
|
b.translation = translation;
|
|
b.args[0] = str;
|
|
// transfer blood color for decals
|
|
if ( target && target.bloodcolor ) b.SetShade(target.bloodcolor);
|
|
else b.SetShade(gameinfo.defaultbloodcolor);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 0 NoDelay A_Bleed(3);
|
|
Goto Puff;
|
|
TNT1 A 0 A_Bleed(2);
|
|
Goto Puff;
|
|
TNT1 A 0 A_Bleed(1);
|
|
Goto Puff;
|
|
Puff:
|
|
TNT1 A 0
|
|
{
|
|
int dec = Random[Blod](0,2);
|
|
if ( dec == 1 )
|
|
return ResolveState("Puff1");
|
|
if ( dec == 2 )
|
|
return ResolveState("Puff2");
|
|
return ResolveState("Puff3");
|
|
}
|
|
Puff1:
|
|
BPF1 ABCDEFGHIJK 1;
|
|
Stop;
|
|
Puff2:
|
|
BPF2 ABCDEFGH 1;
|
|
Stop;
|
|
Puff3:
|
|
BPF3 ABCDEFGHIJ 1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// single drop of blod
|
|
Class UTBloodDrop : Actor
|
|
{
|
|
Default
|
|
{
|
|
Scale 0.24;
|
|
Gravity 0.8;
|
|
Mass 1;
|
|
Radius 2;
|
|
Height 2;
|
|
RenderStyle "Translucent";
|
|
+MISSILE;
|
|
+NOTELEPORT;
|
|
+THRUACTORS;
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
SetState(FindState("Spawn")+Random[Blod](0,4));
|
|
Scale *= FRandom[Blod](0.6,1.4);
|
|
}
|
|
void A_BloodDie()
|
|
{
|
|
// TODO leave decals
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
BDRP ABCDE -1;
|
|
Stop;
|
|
Death:
|
|
TNT1 A 0 A_BloodDie();
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// a burst of blod
|
|
Class UTBloodSpurt : Actor
|
|
{
|
|
double str;
|
|
|
|
Default
|
|
{
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
+THRUACTORS;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
str = FRandom[Blood](2.0,3.2)*args[0];
|
|
int sz = 7*args[0];
|
|
double ang, pt;
|
|
if ( !flak_blood ) sz /= 3;
|
|
for ( int i=0; i<sz; i++ )
|
|
{
|
|
Actor d;
|
|
if ( flak_blood ) d = Spawn("UTBloodDrop",pos);
|
|
else d = Spawn("Blood",pos,ALLOW_REPLACE);
|
|
d.translation = translation;
|
|
d.SetShade(fillcolor);
|
|
ang = angle+FRandom[Blood](-3,3)*str;
|
|
pt = pitch+FRandom[Blood](-3,3)*str;
|
|
Vector3 dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt));
|
|
d.vel = dir*str*FRandom[Blood](0.4,0.8);
|
|
d.vel.z += str*0.5;
|
|
d.scale *= str*0.15*FRandom[Blood](0.6,1.4);
|
|
}
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
// for chunks
|
|
Class UTBloodTrail : Actor
|
|
{
|
|
double str;
|
|
|
|
Default
|
|
{
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+THRUACTORS;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
if ( !target ) return;
|
|
str = FRandom[Blood](2.0,4.0);
|
|
}
|
|
private bool IsTargetFlying()
|
|
{
|
|
if ( !target ) return false;
|
|
if ( target.InStateSequence(target.CurState,target.FindState("Spawn")) ) return true;
|
|
return false;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
if ( !target ) return;
|
|
SetOrigin(target.pos,true);
|
|
double ang, pt;
|
|
int max = flak_blood?3:Random[Blood](-2,1);
|
|
for ( int i=0; i<max; i++ )
|
|
{
|
|
Actor d;
|
|
if ( flak_blood ) d = Spawn("UTBloodDrop",pos);
|
|
else d = Spawn("Blood",pos,ALLOW_REPLACE);
|
|
d.translation = translation;
|
|
d.SetShade(fillcolor);
|
|
ang = FRandom[Blood](0,360);
|
|
pt = FRandom[Blood](-90,90);
|
|
Vector3 dir = -target.vel*0.2;
|
|
dir += (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*0.8;
|
|
d.vel = dir*str*FRandom[Blood](0.8,1.2);
|
|
d.scale *= str*0.15*FRandom[Blood](0.6,1.4);
|
|
}
|
|
bool flying = IsTargetFlying();
|
|
if ( !flying ) Destroy();
|
|
}
|
|
}
|
|
|
|
// Poof
|
|
Class UTBloodPuff : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Scale 1.1;
|
|
Alpha 0.4;
|
|
+NOBLOCKMAP;
|
|
+NOGRAVITY;
|
|
+NOTELEPORT;
|
|
+DONTSPLASH;
|
|
+FORCEXYBILLBOARD;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 0 NoDelay
|
|
{
|
|
int dec = Random[Blod](0,2);
|
|
if ( dec == 1 )
|
|
return ResolveState("Puff1");
|
|
if ( dec == 2 )
|
|
return ResolveState("Puff2");
|
|
return ResolveState("Puff3");
|
|
}
|
|
Puff1:
|
|
BPF1 ABCDEFGHIJK 2;
|
|
Stop;
|
|
Puff2:
|
|
BPF2 ABCDEFGH 2;
|
|
Stop;
|
|
Puff3:
|
|
BPF3 ABCDEFGHIJ 2;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
// Chunkers
|
|
Class UTGibber : Actor
|
|
{
|
|
Actor Gibbed;
|
|
int gibcount, gibsize;
|
|
color shadecol;
|
|
Vector3 rvel;
|
|
|
|
virtual void BurstGibs()
|
|
{
|
|
static const class<Actor> gibcls[] = {"UTGenericGib1","UTGenericGib2","UTGenericGib3","UTGenericGib4"};
|
|
Actor a;
|
|
double ang, pt;
|
|
Vector3 dir;
|
|
bool dummy;
|
|
for ( int i=0; i<gibsize; i++ )
|
|
{
|
|
let a = Spawn(gibcls[Random[Blod](0,3)],Vec3Offset(FRandom[Blod](-0.5,0.5)*radius,FRandom[Blod](-0.5,0.5)*radius,FRandom[Blod](0.2,0.8)*height));
|
|
a.translation = translation;
|
|
a.SetShade(fillcolor);
|
|
a.scale *= FRandom[Blod](1.7,2.3)*scale.x;
|
|
ang = FRandom[Blod](0,360);
|
|
pt = FRandom[Blod](-90,90);
|
|
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
|
a.vel = rvel*0.6+dir*FRandom[Blod](8.0,12.0);
|
|
}
|
|
for ( int i=0; i<gibsize; i++ )
|
|
{
|
|
let a = Spawn("UTBloodPuff",Vec3Offset(FRandom[Blod](-12,12),FRandom[Blod](-12,12),FRandom[Blod](0.1,0.9)*height));
|
|
a.translation = translation;
|
|
a.SetShade(fillcolor);
|
|
a.scale *= FRandom[Blod](1.8,2.3)*scale.x;
|
|
ang = FRandom[Blod](0,360);
|
|
pt = FRandom[Blod](-90,90);
|
|
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
|
a.vel = rvel*0.2+dir*FRandom[Blod](1.5,3.0);
|
|
}
|
|
A_CountDown();
|
|
}
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
gibsize = int(min(max(radius,height)/24,3));
|
|
reactiontime = int(min(max(radius,height)/10,4));
|
|
rvel = vel;
|
|
vel *= 0;
|
|
}
|
|
|
|
Default
|
|
{
|
|
+NOCLIP;
|
|
+NOGRAVITY;
|
|
+NOTELEPORT;
|
|
Radius 32;
|
|
Height 16;
|
|
}
|
|
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 0 NoDelay A_PlaySound("misc/gibbed");
|
|
TNT1 A 1 BurstGibs();
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class UTFemaleLegGibber : UTGibber
|
|
{
|
|
bool firstgib;
|
|
|
|
override void BurstGibs()
|
|
{
|
|
static const class<Actor> parts[] = {"UTFemaleFoot","UTThigh"};
|
|
static const double partofsy[] = {9,8};
|
|
static const double partofsz[] = {9,20};
|
|
Actor a;
|
|
double ang, pt;
|
|
Vector3 dir;
|
|
if ( !firstgib )
|
|
{
|
|
firstgib = true;
|
|
for ( int i=0; i<2; i++ )
|
|
{
|
|
Actor a = Spawn(parts[i],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
|
|
ang = FRandom[Blod](0,360);
|
|
pt = FRandom[Blod](-90,90);
|
|
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
|
a.vel = rvel*0.6+dir*FRandom[Blod](8.0,12.0);
|
|
}
|
|
}
|
|
for ( int i=0; i<gibsize; i++ )
|
|
{
|
|
Vector3 box = (FRandom[Blod](-8,8),FRandom[Blod](7,12),FRandom[Blod](5,18));
|
|
let a = Spawn("UTBloodPuff",Vec3Offset(box.x*cos(angle)-box.y*sin(angle),box.x*sin(angle)+box.y*cos(angle),box.z));
|
|
ang = FRandom[Blod](0,360);
|
|
pt = FRandom[Blod](-90,90);
|
|
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
|
a.vel = rvel*0.2+dir*FRandom[Blod](1.5,3.0);
|
|
}
|
|
A_CountDown();
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
gibsize = 3;
|
|
reactiontime = 5;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 0 NoDelay; // no sound, done by player
|
|
TNT1 A 1 BurstGibs();
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
Class UTPlayerGibber : UTGibber
|
|
{
|
|
bool firstgib;
|
|
|
|
override void BurstGibs()
|
|
{
|
|
static const class<Actor> bossparts[] = {"UTBossArm","UTBossArm","UTBossFoot","UTBossFoot","UTMaleTorso","UTHeadBoss"};
|
|
static const class<Actor> maleparts[] = {"UTMaleArm","UTMaleArm","UTMaleFoot","UTMaleFoot","UTMaleTorso","UTHeadMale"};
|
|
static const class<Actor> femaleparts[] = {"UTFemaleArm","UTFemaleArm","UTFemaleFoot","UTFemaleFoot","UTFemaleTorso","UTHeadFemale"};
|
|
static const class<Actor> extraparts[] = {"UTThigh","UTThigh","UTHeart","UTLiver","UTStomach"};
|
|
static const double partofsy[] =
|
|
{
|
|
14,
|
|
-14,
|
|
9,
|
|
-9,
|
|
0,
|
|
0,
|
|
8,
|
|
-8,
|
|
-2,
|
|
5,
|
|
-3
|
|
};
|
|
static const double partofsz[] =
|
|
{
|
|
36,
|
|
36,
|
|
9,
|
|
9,
|
|
32,
|
|
48,
|
|
20,
|
|
20,
|
|
40,
|
|
32,
|
|
35
|
|
};
|
|
Actor a;
|
|
double ang, pt;
|
|
Vector3 dir;
|
|
if ( !firstgib )
|
|
{
|
|
firstgib = true;
|
|
for ( int i=0; i<11; i++ )
|
|
{
|
|
Actor a;
|
|
if ( i < 6 )
|
|
{
|
|
if ( UTPlayer(Gibbed).DollType == UTPlayer.DOLL_Boss )
|
|
a = Spawn(bossparts[i],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
|
|
else if ( UTPlayer(Gibbed).DollType == UTPlayer.DOLL_Female )
|
|
a = Spawn(femaleparts[i],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
|
|
else a = Spawn(maleparts[i],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
|
|
}
|
|
else a = Spawn(extraparts[i-6],Vec3Offset(-partofsy[i]*sin(angle),partofsy[i]*cos(angle),partofsz[i]));
|
|
ang = FRandom[Blod](0,360);
|
|
pt = FRandom[Blod](-90,90);
|
|
if ( a is 'UTHead' )
|
|
{
|
|
pt = FRandom[Blod](-90,-15);
|
|
if ( UTPlayer(Gibbed).player )
|
|
{
|
|
UTPlayer(Gibbed).player.Camera = a;
|
|
UTHead(a).headowner = UTPlayer(Gibbed).player;
|
|
}
|
|
}
|
|
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
|
a.vel = rvel*0.6+dir*FRandom[Blod](8.0,12.0);
|
|
}
|
|
}
|
|
for ( int i=0; i<gibsize; i++ )
|
|
{
|
|
let a = Spawn("UTBloodPuff",Vec3Offset(FRandom[Blod](-0.8,0.8)*radius,FRandom[Blod](-0.8,0.8)*radius,FRandom[Blod](0.1,0.9)*height));
|
|
ang = FRandom[Blod](0,360);
|
|
pt = FRandom[Blod](-90,90);
|
|
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
|
|
a.vel = rvel*0.2+dir*FRandom[Blod](1.5,3.0);
|
|
}
|
|
A_CountDown();
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
gibsize = 3;
|
|
reactiontime = 5;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 0 NoDelay; // no sound, done by player
|
|
TNT1 A 1 BurstGibs();
|
|
Wait;
|
|
}
|
|
}
|
|
|
|
// Chunks
|
|
Class UTGib : Actor
|
|
{
|
|
double rollvel, anglevel, pitchvel;
|
|
|
|
Default
|
|
{
|
|
Radius 4;
|
|
Height 4;
|
|
BounceType "Hexen";
|
|
BounceFactor 0.8;
|
|
WallBounceFactor 0.8;
|
|
Gravity 0.6;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
+INTERPOLATEANGLES;
|
|
+CANBOUNCEWATER;
|
|
+MISSILE;
|
|
+THRUACTORS;
|
|
+USEBOUNCESTATE;
|
|
+NOTELEPORT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
tracer = Spawn("UTBloodTrail",pos);
|
|
tracer.target = self;
|
|
if ( bloodcolor ) tracer.SetShade(bloodcolor);
|
|
else tracer.SetShade(gameinfo.defaultbloodcolor);
|
|
tracer.translation = bloodtranslation;
|
|
rollvel = FRandom[Blod](3,6)*RandomPick[Blod](-1,1);
|
|
anglevel = FRandom[Blod](3,6)*RandomPick[Blod](-1,1);
|
|
pitchvel = FRandom[Blod](3,6)*RandomPick[Blod](-1,1);
|
|
}
|
|
void A_HandleBounce()
|
|
{
|
|
double spd = vel.length();
|
|
if ( spd > double.epsilon )
|
|
vel = (vel.unit()+(FRandom[Blod](-0.2,0.2),FRandom[Blod](-0.2,0.2),FRandom[Blod](-0.2,0.2))).unit()*spd;
|
|
if ( BlockingFloor && (vel.xy.length() < 1) )
|
|
{
|
|
ClearBounce();
|
|
ExplodeMissile();
|
|
}
|
|
if ( spd < 10 )
|
|
{
|
|
if ( tracer ) tracer.Destroy();
|
|
}
|
|
else if ( spd > 20 ) vel *= 0.8;
|
|
A_PlaySound("misc/gibp");
|
|
double ang, pt;
|
|
int max = flak_blood?6:2;
|
|
for ( int i=0; i<max; i++ )
|
|
{
|
|
Actor d;
|
|
if ( flak_blood ) d = Spawn("UTBloodDrop",pos);
|
|
else d = Spawn("Blood",pos,ALLOW_REPLACE);
|
|
d.translation = bloodtranslation;
|
|
if ( bloodcolor ) d.SetShade(bloodcolor);
|
|
else d.SetShade(gameinfo.defaultbloodcolor);
|
|
ang = FRandom[Blood](0,360);
|
|
pt = FRandom[Blood](-90,90);
|
|
Vector3 dir = -vel*0.4;
|
|
dir += (cos(ang)*cos(pt),sin(ang)*cos(pt),-sin(pt))*1.3;
|
|
d.vel = dir*FRandom[Blood](0.8,1.2);
|
|
d.scale *= 0.75*FRandom[Blood](0.6,1.4);
|
|
}
|
|
rollvel = FRandom[Blod](3,6)*RandomPick[Blod](-1,1);
|
|
anglevel = FRandom[Blod](3,6)*RandomPick[Blod](-1,1);
|
|
pitchvel = FRandom[Blod](3,6)*RandomPick[Blod](-1,1);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
UGIB A 1
|
|
{
|
|
roll += rollvel;
|
|
angle += anglevel;
|
|
pitch += pitchvel;
|
|
}
|
|
Wait;
|
|
Bounce:
|
|
UGIB A 0 A_HandleBounce();
|
|
Goto Spawn;
|
|
Death:
|
|
UGIB A 1 A_SetTics(Random[Blod](12,18)*15);
|
|
UGIB A 1 A_FadeOut(0.05);
|
|
Wait;
|
|
}
|
|
}
|
|
Class UTGenericGib1 : UTGib {}
|
|
Class UTGenericGib2 : UTGib {}
|
|
Class UTGenericGib3 : UTGib {}
|
|
Class UTGenericGib4 : UTGib {}
|
|
Class UTThigh : UTGib {}
|
|
Class UTFemaleArm : UTGib {}
|
|
Class UTFemaleFoot : UTGib {}
|
|
Class UTFemaleTorso : UTGib {}
|
|
Class UTMaleArm : UTGib {}
|
|
Class UTMaleFoot : UTGib {}
|
|
Class UTMaleTorso : UTGib {}
|
|
Class UTBossArm : UTGib {}
|
|
Class UTBossFoot : UTGib {}
|
|
Class UTBossTorso : UTGib {}
|
|
Class UTHeart : UTGib
|
|
{
|
|
States
|
|
{
|
|
Spawn:
|
|
UGIB ABCDEF 1
|
|
{
|
|
roll += rollvel;
|
|
angle += anglevel;
|
|
pitch += pitchvel;
|
|
}
|
|
Loop;
|
|
Death:
|
|
UGIB ABCDEFABCDEFABCDEF 2;
|
|
UGIB ABCDEFABCDEF 4;
|
|
UGIB ABCDEF 8;
|
|
UGIB A 1 A_SetTics(Random[Blod](12,18)*15);
|
|
UGIB A 1 A_FadeOut(0.05);
|
|
Wait;
|
|
}
|
|
}
|
|
Class UTLiver : UTGib {}
|
|
Class UTStomach : UTGib {}
|
|
Class UTHead : UTGib
|
|
{
|
|
PlayerInfo headowner;
|
|
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
UTMainHandler.DoFlash(self,Color(128,255,0,0),150);
|
|
UTMainHandler.DoFlash(self,Color(96,255,0,0),0);
|
|
}
|
|
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( headowner && headowner.mo && (headowner.mo.Health > 0) && (headowner.Camera == self) )
|
|
{
|
|
headowner.Camera = headowner.mo;
|
|
headowner = null;
|
|
}
|
|
}
|
|
|
|
override void OnDestroy()
|
|
{
|
|
if ( headowner && (headowner.Camera == self) && headowner.mo )
|
|
{
|
|
headowner.Camera = headowner.mo;
|
|
headowner = null;
|
|
}
|
|
Super.OnDestroy();
|
|
}
|
|
States
|
|
{
|
|
Death:
|
|
UGIB A 1 A_SetTics(Random[Blod](12,18)*15);
|
|
UGIB A 0 A_JumpIf(headowner,"Death");
|
|
UGIB A 1 A_FadeOut(0.05);
|
|
Wait;
|
|
}
|
|
}
|
|
Class UTHeadFemale : UTHead {}
|
|
Class UTHeadMale : UTHead {}
|
|
Class UTHeadBoss : UTHead {}
|