swwmgz_m/zscript/swwm_blod.zsc
Marisa Kirisame eb2ee7b29f Push to master all the current WIP stuff in 0.9.11b:
- Reduce number of collectibles (some might come back in the future).
 - Merge both DLC weaponsets into one, removing redundant weapons.
 - Readjust prices of some items.
 - Initial work on collectibles (currently Frispy Corn is done).
 - Added bigfont for main menu, based on Source Han Sans.
 - Reduced default HUD margin to 10.
 - Added blob shadows.
 - Added precise crosshair drawing.
 - Tweaked decals, imported more stuff from UT.
 - Swapped the Ynykron impact decal for something better.
 - Fixes to slope alignment code.
 - Implemented headpats for MBF Helper Dogs and Cacodemons.
 - Implemented partial HDoom support, with love and headpats.
 - Fix various string functions breaking on unicode.
 - Added cracktro-style text scroll to Titlemap.
 - Fixed handling of healthbars for friendly monsters.
 - Workaround for maps that use the old author name hack (" - by: " separator).
 - Fixed Silver Bullet not autoswitching on first pickup.
 - Fixed misalignment of Silver Bullet zoomed aim.
 - Silver Bullet is unchambered on first pickup, consistent with Candygun.
 - Adjusted collision sizes of all items across the board.
 - Implemented "Use To Pickup" to work around any issues introduced by the previous change.
 - Swapped CHANF_LOOPING for CHANF_LOOP in many cases, this was a typo.
 - Tweaked Biospark arc lengths, for balance and higher performance.
 - Fix misaligned fire offsets of some weapons (most noticeable on Wallbuster).
 - Prettified the loading disclaimers for BD and HDoom.
 - Add pickup flash to all items.
 - Add custom key models for Doom and Heretic.
 - Fix blown kisses giving you "need key" messages.
 - Fix worn armor and embiggeners not being removed on scripted inventory resets.
 - Remove all references to the no longer planned Radio.
 - Workaround for gzdoom devbuild quirk where MenuSound changed its argument type.
 - Added timezone to fake clock.
 - Fix some times and dates in said clock.
 - SWWM blood now also hits ceilings.
 - Added default properties to DLC ammo and weapon stubs.
 - Lore entries for collectibles and dlc weapons (incomplete).
 - Massive amount of typo fixes across the board.
2020-10-20 11:37:14 +02:00

940 lines
22 KiB
Text

// Gore FX ported over from Soundless Mound, with some edits
// Base blood actor
Class mkBlood : Actor
{
Default
{
+NOBLOCKMAP;
+NOGRAVITY;
+NOTELEPORT;
+PUFFGETSOWNER;
}
action 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);
}
override void Tick()
{
if ( isFrozen() ) return;
if ( !CheckNoDelay() || (tics == -1) ) return;
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
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 : Actor
{
double str;
int cnt;
Vector3 attachofs;
double baseang;
color shadecol;
Default
{
+NOBLOCKMAP;
+NOGRAVITY;
+NOTELEPORT;
+NOINTERACTION;
}
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 ( isFrozen() ) return;
Vector3 setofs;
setofs = (cos(target.angle)*attachofs.x+sin(target.angle)*attachofs.y,sin(target.angle)*attachofs.x-cos(target.angle)*attachofs.y,attachofs.z);
SetOrigin(level.Vec3Offset(target.pos,setofs),false);
int sz = max(1,args[0]/2);
double ang, pt;
for ( int i=0; i<sz; 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 = (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(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.-(.3/sz);
if ( (str <= .05) || ((cnt-- <= 0) && !flying) ) Destroy();
}
}
// drop of salsa
// becomes a decal on crash
Class mkBloodDrop : Actor
{
bool killme;
bool dead, onceiling;
mkBloodDrop prevblod, nextblod;
Sector tracksector;
F3DFloor trackffloor;
int trackplane;
Default
{
+MISSILE;
+NOBLOCKMAP;
+DROPOFF;
+NOTELEPORT;
+THRUACTORS;
+FORCEXYBILLBOARD;
+ROLLSPRITE;
+ROLLCENTER;
+NOINTERACTION;
Scale .35;
Radius 2;
Height 2;
Mass 1;
RenderStyle "Shaded";
}
// try to reduce overhead as much as possible with this
override void Tick()
{
prev = pos; // for interpolation
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 ( trackffloor )
{
if ( trackplane ) trackz = trackffloor.bottom.ZAtPoint(pos.xy)-.1;
else trackz = trackffloor.top.ZAtPoint(pos.xy);
}
else
{
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
{
FCheckPosition tm;
// gravitational pull
vel.z -= GetGravity();
// movement subdivision (these damn things are tiny)
int steps = 8;
while ( (abs(vel.x) >= radius*steps) || (abs(vel.y) >= radius*steps) || (abs(vel.z) >= height*steps) )
steps++;
bool domove = (vel!=(0,0,0))||((pos.z!=floorz)&&(pos.z!=ceilingz-height));
if ( domove )
{
Vector3 steppy = vel/steps;
int changexy = steppy.X||steppy.Y;
for ( int i=0; i<steps; i++ )
{
if ( changexy && !TryMove(Vec2Offset(steppy.x,steppy.y),1,false,tm) )
{
let l = tm.CeilingLine;
if ( l && l.backsector && l.backsector.GetTexture(1) == skyflatnum )
{
let posr = PosRelative(l.backsector);
if ( pos.z >= l.backsector.ceilingplane.ZAtPoint(posr.xy) )
{
Destroy();
return;
}
}
// hit wall
Vector3 dir = vel.unit();
TraceBleedAngle(20,atan2(dir.y,dir.x),asin(-dir.z));
A_StartSound("misc/blooddrop",volume:.1);
Destroy();
return;
}
AddZ(steppy.z);
UpdateWaterLevel();
if ( pos.z <= floorz )
{
if ( floorpic == skyflatnum )
{
Destroy();
return;
}
// landed on floor
SetZ(floorz);
HitFloor();
A_StartSound("misc/blooddrop",volume:.1);
if ( master )
{
// assume we dropped onto the previous spot
if ( master.tracer )
{
Destroy();
return;
}
master.tracer = self;
}
dead = true;
SWWMUtility.SetToSlope(self,FRandom[Blood](0,360));
tracksector = FloorSector;
trackplane = 0;
F3DFloor ff;
for ( int i=0; i<FloorSector.Get3DFloorCount(); i++ )
{
if ( !(FloorSector.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
if ( !(FloorSector.Get3DFloor(i).top.ZAtPoint(pos.xy) ~== floorz) ) continue;
ff = FloorSector.Get3DFloor(i);
break;
}
if ( ff ) trackffloor = ff;
frame = Random[Blood](5,8);
return;
}
if ( pos.z+height > ceilingz )
{
if ( (ceilingpic == skyflatnum) || master )
{
Destroy();
return;
}
// hit the ceiling
SetZ(ceilingz-.1);
A_StartSound("misc/blooddrop",volume:.1);
dead = true;
onceiling = true;
SWWMUtility.SetToSlope(self,FRandom[Blood](0,360),true);
tracksector = CeilingSector;
trackplane = 1;
F3DFloor ff;
for ( int i=0; i<CeilingSector.Get3DFloorCount(); i++ )
{
if ( !(CeilingSector.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
if ( !(CeilingSector.Get3DFloor(i).bottom.ZAtPoint(pos.xy) ~== ceilingz) ) continue;
ff = CeilingSector.Get3DFloor(i);
break;
}
if ( ff ) trackffloor = ff;
frame = Random[Blood](5,8);
special1 = Random[Blood](15,25);
special2 = 0;
return;
}
CheckPortalTransition();
}
}
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();
SWWMGoreHandler.QueueBlod(self);
int jumps = Random[Blood](0,3);
state dest = ResolveState("Spawn");
SetState(dest+jumps);
}
override void OnDestroy()
{
SWWMGoreHandler.DeQueueBlod(self);
Super.OnDestroy();
}
States
{
Spawn:
SBLD ABCD 2;
Loop;
}
}
// chunky salsa in the air
Class mkBloodSmoke : Actor
{
Default
{
+NOBLOCKMAP;
+NOGRAVITY;
+NOINTERACTION;
+NOTELEPORT;
+FORCEXYBILLBOARD;
Scale .5;
Alpha .35;
RenderStyle "Shaded";
}
override void PostBeginPlay()
{
int jumps = Random[Blood](0,19);
state dest = ResolveState("Spawn");
SetState(dest+jumps);
}
override void Tick()
{
if ( isFrozen() ) return;
SetOrigin(level.Vec3Offset(pos,vel),true);
UpdateWaterLevel();
if ( waterlevel > 0 ) A_FadeOut();
A_FadeOut(.03);
A_SetScale(scale.x*1.02);
if ( tics > 0 ) tics--;
while ( !tics )
{
if ( !SetState(CurState.NextState) )
return;
}
}
States
{
Spawn:
BSMK ABCDEFGHIJKLMNOPQRST 2;
Loop;
}
}
Class mkBloodSmoke2 : mkBloodSmoke
{
Default
{
Scale .8;
Alpha 1.;
}
}
// flying gibs
// inspired by Lud's Universal Gibs
Class mkFlyingGib : Actor
{
bool killme;
int lastbleed;
color shadecol;
bool bleeding;
double rollvel;
mkFlyingGib prevmeat, nextmeat;
override void PostBeginPlay()
{
Super.PostBeginPlay();
frame = Random[Blood](0,5);
double ang = FRandom[Gibs](0,360);
double pt = FRandom[Gibs](-60,20);
Vector3 dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
vel += dir*FRandom[Gibs](5.,20.);
if ( master )
{
vel += master.vel*1.5;
CopyBloodColor(master);
}
rollvel = FRandom[Gibs](10,50)*RandomPick[Gibs](-1,1);
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);
bleeding = true;
if ( Random[Blood](0,1) ) bXFlip = true;
SWWMGoreHandler.QueueMeat(self);
}
override void OnDestroy()
{
SWWMGoreHandler.DeQueueMeat(self);
Super.OnDestroy();
}
override void Tick()
{
Super.Tick();
if ( isFrozen() ) return;
if ( killme ) A_FadeOut(.01);
if ( CurState == ResolveState("Death2") )
{
if ( vel.length() < .1 )
bleeding = false;
return;
}
roll += rollvel;
if ( waterlevel > 0 )
{
rollvel *= .98;
vel.xy *= .98;
return;
}
if ( bleeding && !Random[Blood](0,3) )
{
let s = Spawn("mkBloodSmoke",pos);
s.SetShade(shadecol);
}
}
override bool CanCollideWith(Actor other, bool passive)
{
if ( other == master ) return false;
return true;
}
action void A_Bleed()
{
double ang;
double pt;
Vector3 dir;
if ( invoker.lastbleed > level.maptime ) return;
invoker.lastbleed = level.maptime+5;
for ( int i=0; i<8; i++ )
{
let b = Spawn("mkBloodDrop",pos);
ang = FRandom[Gibs](0,360);
pt = FRandom[Gibs](-60,30);
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
b.vel = dir*FRandom[Gibs](0.3,2.2)*vel.length();
b.scale *= 2.0;
b.SetShade(invoker.shadecol);
}
TraceBleedAngle(int(vel.length()),angle+180,-pitch);
}
Default
{
Radius 4;
Height 4;
Mass 10;
Scale .65;
Gravity .5;
BounceType "Doom";
BounceFactor .6;
+MISSILE;
+DROPOFF;
+NOBLOCKMAP;
+USEBOUNCESTATE;
+BOUNCEONCEILINGS;
+MOVEWITHSECTOR;
+THRUACTORS;
+NOTELEPORT;
+ROLLSPRITE;
+ROLLCENTER;
+INTERPOLATEANGLES;
}
States
{
Spawn:
SGIB # 1;
Wait;
Bounce:
SGIB # 0
{
A_Bleed();
A_StartSound("misc/gibhit",CHAN_BODY,CHANF_OVERLAP);
}
Goto Spawn;
Death:
SGIB # 1 A_JumpIf(pos.z<=floorz,"Death2");
Wait;
Death2:
SGIB # -1
{
A_StartSound("misc/gibhit",CHAN_BODY,CHANF_OVERLAP);
roll = RandomPick[Gibs](0,180)+Random[Gibs](-5,5);
A_Stop();
}
Stop;
}
}
// Manually added gibbing
Class mkGibber : Actor
{
Actor Gibbed;
int gibcount, gibsize;
int delay;
color shadecol;
virtual void BurstGibs()
{
Actor a;
double ang, pt;
Vector3 dir;
bool dummy;
for ( int i=0; i<gibsize; i++ )
{
a = Spawn("mkBloodSmoke2",pos+(FRandom[Gibs](-.8,.8)*radius,FRandom[Gibs](-.8,.8)*radius,FRandom[Gibs](0.,.9)*height));
a.SetShade(shadecol);
}
for ( int i=0; i<2*gibsize; i++ )
{
[dummy, a] = A_SpawnItemEx("mkFlyingGib",FRandom[Gibs](-.5,.5)*radius,FRandom[Gibs](-.5,.5)*radius,FRandom[Gibs](.1,.9)*height,flags:SXF_ABSOLUTEANGLE|SXF_USEBLOODCOLOR);
a.translation = translation;
a.CopyBloodColor(self);
a.scale *= scale.x;
a.master = gibbed;
}
for ( int i=0; i<3*gibsize; i++ )
{
[dummy, a] = A_SpawnItemEx("mkBloodDrop",FRandom[Gibs](-.8,.8)*radius,FRandom[Gibs](-.8,.8)*radius,FRandom[Gibs](0.,.9)*height,flags:SXF_ABSOLUTEANGLE|SXF_USEBLOODCOLOR);
ang = FRandom[Gibs](0,360);
pt = FRandom[Gibs](-90,90);
dir = (cos(pt)*cos(ang),cos(pt)*sin(ang),sin(-pt));
a.vel = dir*FRandom[Gibs](8.,24.);
a.scale *= 2.+FRandom[Gibs](.3,1.6);
a.SetShade(shadecol);
a.CopyBloodColor(self);
}
reactiontime--;
}
override void PostBeginPlay()
{
gibsize = int(min(max(radius,height)/15,6));
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 ( 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;
}
A_StartSound("misc/gibber");
BurstGibs();
if ( reactiontime <= 0 )
Destroy();
}
Default
{
+NOBLOCKMAP;
+NOGRAVITY;
+NOTELEPORT;
+DONTSPLASH;
+NOINTERACTION;
Radius 32;
Height 16;
}
}
// bare actors used for copying blood color to vanilla monsters
Class GreenBloodReference : Actor
{
Default
{
BloodColor "Green";
}
}
Class BlueBloodReference : Actor
{
Default
{
BloodColor "Blue";
}
}
Class PurpleBloodReference : Actor
{
Default
{
BloodColor "Purple";
}
}
// bare actor used for extra gib deaths
Class ExtraGibDeaths : Actor
{
StateLabel gibstate;
static void GibThis( Actor a, Statelabel st )
{
if ( !a ) return;
let g = ExtraGibDeaths(Spawn("ExtraGibDeaths"));
g.master = a;
g.gibstate = st;
}
void A_DoGib()
{
if ( !master ) return;
master.SetState(FindState(gibstate));
}
States
{
Spawn:
TNT1 A 1 NoDelay;
TNT1 A 1 A_DoGib();
Stop;
DemonXDeath:
SARX A 5;
SARX B 5 A_XScream();
SARX C 5 A_NoBlocking();
SARX DEF 5;
SARX G -1;
Stop;
KnightXDeath:
BO2X A 5;
BO2X B 5 A_XScream();
BO2X C 5;
BO2X D 5 A_NoBlocking();
BO2X EFGH 5;
BO2X I -1;
Stop;
BaronXDeath:
BOSX A 5;
BOSX B 5 A_XScream();
BOSX C 5;
BOSX D 5 A_NoBlocking();
BOSX EFGH 5;
BOSX I -1 A_BossDeath();
Stop;
CacoXDeath:
CACX A 5;
CACX B 5 A_XScream();
CACX C 5 A_NoBlocking();
CACX D 4;
CACX E 3;
CACX F 4;
CACX G 5;
CACX H -1;
Stop;
BonerXDeath:
REVX A 3;
REVX B 4 A_XScream();
REVX C 5 A_NoBlocking();
REVX DE 5;
REVX F -1;
Stop;
VileXDeath:
VILX A 5;
VILX B 5 A_XScream();
VILX C 5 A_NoBlocking();
VILX DEF 5;
VILX G -1;
Stop;
}
}
// 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;
Destroy();
}
}
Class SWWMGoreHandler : EventHandler
{
mkBloodDrop blods, blods_end;
int blods_cnt, oldmaxblood;
mkFlyingGib meats, meats_end;
int meats_cnt, oldmaxgibs;
override void WorldLoaded( WorldEvent e )
{
// recheck queues in case limits changed
while ( blods && (blods_cnt > swwm_maxblood) )
DeQueueBlod(blods);
while ( meats && (meats_cnt > swwm_maxgibs) )
DeQueueMeat(meats);
}
static void QueueBlod( mkBloodDrop b )
{
let hnd = SWWMGoreHandler(EventHandler.Find("SWWMGoreHandler"));
if ( !hnd ) return;
hnd.blods_cnt++;
if ( !hnd.blods )
{
// this is the initial one
hnd.blods = b;
hnd.blods_end = b;
}
else
{
hnd.blods_end.nextblod = b;
b.prevblod = hnd.blods_end;
hnd.blods_end = b;
}
while ( hnd.blods && (swwm_maxblood >= 0) && (hnd.blods_cnt > swwm_maxblood) )
DeQueueBlod(hnd.blods);
}
static void DeQueueBlod( mkBloodDrop b )
{
let hnd = SWWMGoreHandler(EventHandler.Find("SWWMGoreHandler"));
if ( !hnd || !hnd.blods ) return;
if ( (hnd.blods != b) && !b.prevblod && !b.nextblod ) return;
hnd.blods_cnt--;
if ( !b.prevblod ) hnd.blods = b.nextblod;
else b.prevblod.nextblod = b.nextblod;
if ( b == hnd.blods_end ) hnd.blods_end = b.prevblod;
if ( b.nextblod ) b.nextblod.prevblod = b.prevblod;
b.killme = true;
b.prevblod = null;
b.nextblod = null;
}
static void QueueMeat( mkFlyingGib m )
{
let hnd = SWWMGoreHandler(EventHandler.Find("SWWMGoreHandler"));
if ( !hnd ) return;
hnd.meats_cnt++;
if ( !hnd.meats )
{
// this is the initial one
hnd.meats = m;
hnd.meats_end = m;
}
else
{
hnd.meats_end.nextmeat = m;
m.prevmeat = hnd.meats_end;
hnd.meats_end = m;
}
while ( hnd.meats && (swwm_maxgibs >= 0) && (hnd.meats_cnt > swwm_maxgibs) )
DeQueueMeat(hnd.meats);
}
static void DeQueueMeat( mkFlyingGib m )
{
let hnd = SWWMGoreHandler(EventHandler.Find("SWWMGoreHandler"));
if ( !hnd || !hnd.meats ) return;
if ( (hnd.meats != m) && !m.prevmeat && !m.nextmeat ) return;
hnd.meats_cnt--;
if ( !m.prevmeat ) hnd.meats = m.nextmeat;
else m.prevmeat.nextmeat = m.nextmeat;
if ( m == hnd.meats_end ) hnd.meats_end = m.prevmeat;
if ( m.nextmeat ) m.nextmeat.prevmeat = m.prevmeat;
m.killme = true;
m.prevmeat = null;
m.nextmeat = null;
}
override void WorldTick()
{
if ( swwm_maxblood != oldmaxblood )
{
while ( blods && (swwm_maxblood >= 0) && (blods_cnt > swwm_maxblood) )
DeQueueBlod(blods);
}
if ( swwm_maxgibs != oldmaxgibs )
{
while ( meats && (swwm_maxgibs >= 0) && (meats_cnt > swwm_maxgibs) )
DeQueueMeat(meats);
}
oldmaxblood = swwm_maxblood;
oldmaxgibs = swwm_maxgibs;
}
override void CheckReplacement( ReplaceEvent e )
{
// only replace vanilla blood if no other gore mod is doing it
if ( (e.Replacee == "Blood") && (!e.Replacement || e.Replacement == "Blood") && swwm_blood )
e.Replacement = "mkBlood";
}
override void WorldThingSpawned( WorldEvent e )
{
// vanilla blood color changes
if ( (e.Thing.GetClass() == "BaronOfHell") || (e.Thing.GetClass() == "HellKnight") || (e.Thing.GetClass() == "Bishop") || (e.Thing.GetClass() == "Korax") )
{
let gb = Actor.Spawn("GreenBloodReference");
e.Thing.CopyBloodColor(gb);
gb.Destroy();
}
else if ( e.Thing.GetClass() == "Cacodemon" )
{
let bb = Actor.Spawn("BlueBloodReference");
e.Thing.CopyBloodColor(bb);
bb.Destroy();
}
else if ( (e.Thing.GetClass() == "Wizard") || (e.Thing.GetClass() == "Heresiarch") || (e.Thing.GetClass() == "Sorcerer2") )
{
let pb = Actor.Spawn("PurpleBloodReference");
e.Thing.CopyBloodColor(pb);
pb.Destroy();
}
else if ( e.Thing.GetClass() == "LostSoul" )
e.Thing.bNOBLOOD = true;
}
override void WorldThingDied( WorldEvent e )
{
// force insert gib animations on some vanilla Doom monsters
int gibhealth = e.Thing.GetGibHealth();
bool gotgibbed = (!e.Thing.bDONTGIB && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH));
if ( !gotgibbed ) return;
if ( (e.Thing.GetClass() == "Demon") || (e.Thing.GetClass() == "Spectre") )
ExtraGibDeaths.GibThis(e.Thing,"DemonXDeath");
else if ( e.Thing.GetClass() == "HellKnight" )
ExtraGibDeaths.GibThis(e.Thing,"KnightXDeath");
else if ( e.Thing.GetClass() == "BaronOfHell" )
ExtraGibDeaths.GibThis(e.Thing,"BaronXDeath");
else if ( e.Thing.GetClass() == "Cacodemon" )
ExtraGibDeaths.GibThis(e.Thing,"CacoXDeath");
else if ( e.Thing.GetClass() == "Revenant" )
ExtraGibDeaths.GibThis(e.Thing,"BonerXDeath");
else if ( e.Thing.GetClass() == "Archvile" )
ExtraGibDeaths.GibThis(e.Thing,"VileXDeath");
}
override void WorldThingDamaged( WorldEvent e )
{
if ( e.Thing.Health > 0 ) return;
// no gib if it was erased
if ( e.DamageType == 'Ynykron' ) return;
int gibhealth = e.Thing.GetGibHealth();
bool gotgibbed = (!e.Thing.bDONTGIB && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH));
bool forcegibbed = false;
// force gib availability for some vanilla Doom monsters
if ( gotgibbed && ((e.Thing.GetClass() == "Demon") || (e.Thing.GetClass() == "Spectre") || (e.Thing.GetClass() == "HellKnight") || (e.Thing.GetClass() == "BaronOfHell") || (e.Thing.GetClass() == "Cacodemon") || (e.Thing.GetClass() == "Revenant") || (e.Thing.GetClass() == "Archvile")) )
forcegibbed = true;
if ( !e.Thing.FindState("XDeath",true) && !e.Thing.FindState("Death.Extreme",true) && !forcegibbed )
gotgibbed = false;
// only do special handling if they use our blood
if ( (e.Thing.GetBloodType(0) != "mkBlood") || e.Thing.bNOBLOOD )
return;
CorpseFallTracker.TrackBody(e.Thing);
bool b;
Actor a;
// special handling of some monsters
if ( e.Thing.GetClass() == "Cyberdemon" )
{
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
if ( !b ) return;
mkGibber(a).gibbed = e.Thing;
mkGibber(a).delay = 40;
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
return;
}
else if ( e.Thing.GetClass() == "SpiderMastermind" )
{
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
if ( !b ) return;
mkGibber(a).gibbed = e.Thing;
mkGibber(a).delay = 60;
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
return;
}
// override gibbing
if ( gotgibbed )
{
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
if ( !b ) return;
mkGibber(a).gibbed = e.Thing;
a.A_SetSize(e.Thing.default.radius,e.Thing.default.height);
}
}
}