swwmgz_m/zscript/weapons/swwm_cbt_fx.zsc
Marisa the Magician 0fbbb91b8e Revert "Remove ZPolyobject due to licensing concerns."
This reverts commit f134375f7e.

Library is now MIT licensed and can be included.
2022-10-08 15:55:42 +02:00

630 lines
21 KiB
Text

// Wallbuster effects
Class BustedQuake : Actor
{
Default
{
Radius 0.1;
Height 0;
+NOGRAVITY;
+NOCLIP;
+DONTSPLASH;
+NOTELEPORT;
+NOINTERACTION;
}
override void PostBeginPlay()
{
if ( (special1 < 3) || (special1 > 6) )
{
A_StartSound("wallbuster/smallbust",CHAN_VOICE,CHANF_OVERLAP,min(1.,special1*.32),1./max(1.,special1*.35),1.-special1*.05);
A_StartSound("wallbuster/smallbust",CHAN_VOICE,CHANF_OVERLAP,min(1.,special1*.32),1./max(1.,special1*.35),1.-special1*.05);
}
if ( special1 >= 3 )
{
A_StartSound("wallbuster/bigbust",CHAN_VOICE,CHANF_OVERLAP,min(1.,special1*.35),1./max(1.,special1*.35),1.-special1*.01);
A_StartSound("wallbuster/bigbust",CHAN_VOICE,CHANF_OVERLAP,min(1.,special1*.35),1./max(1.,special1*.35),1.-special1*.01);
}
A_QuakeEx(special1,special1,special1,20+special1*5,0,300+special1*90,"",QF_RELATIVE|QF_SCALEDOWN|QF_3D,rollIntensity:special1*.1);
A_AlertMonsters(swwm_uncapalert?0:2500);
}
override void Tick()
{
if ( isFrozen() ) return;
tics--;
if ( tics <= 0 ) Destroy();
}
States
{
Spawn:
TNT1 A 700;
Stop;
}
}
Class BustPoint
{
Vector2 pos;
}
// Bustin' makes me feel good
Class BusterWall : Thinker
{
Sector hitsector;
swwm_PolyobjectHandle hitpoly;
int accdamage;
Array<int> acchits;
int hitplane;
bool busted;
Vector3 bustdir;
int busttics, delay, bustmax;
double cutheight;
// cached
Vector3 boundsmin, boundsmax, step;
Array<BustPoint> polygrid;
override void Tick()
{
if ( busted )
{
busttics++;
if ( busttics > bustmax )
{
Destroy();
return;
}
SpawnDebris();
return;
}
// fade out damage
if ( delay > 0 )
{
delay--;
return;
}
accdamage = int(accdamage*.9-5);
if ( accdamage <= 0 )
{
Destroy();
return;
}
}
private void SpawnDebris( bool initial = false )
{
if ( hitpoly )
{
SpawnDebrisPoly(initial);
return;
}
double x, y, z;
for ( z=boundsmin.z; z<boundsmax.z; z+=step.z )
for ( y=boundsmin.y; y<boundsmax.y; y+=step.y )
for ( x=boundsmin.x; x<boundsmax.x; x+=step.x )
{
Vector3 spot = (x,y,z);
if ( level.PointInSector(spot.xy) != hitsector ) continue;
spot += (FRandom[Wallbuster](-step.x,step.x),FRandom[Wallbuster](-step.y,step.y),FRandom[Wallbuster](-step.z,step.z));
if ( !level.IsPointInLevel(spot) ) continue;
if ( (initial || !(busttics%2)) && !Random[Wallbuster](0,1) )
{
Vector3 pvel = (bustdir+SWWMUtility.Vec3FromAngles(FRandom[Wallbuster](0,360),FRandom[Wallbuster](-90,90))).unit()*FRandom[Wallbuster](-2.,8.);
let s = Actor.Spawn("SWWMHalfSmoke",spot);
s.vel = pvel;
s.scale *= 2.5;
s.special1 = Random[Wallbuster](3,8);
s.SetShade(Color(1,1,1)*Random[Wallbuster](40,120));
}
if ( (!initial && (busttics%3)) || (busttics > (bustmax/2)) ) continue;
int numpt = Random[Wallbuster](-4,1);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (bustdir+SWWMUtility.Vec3FromAngles(FRandom[Wallbuster](0,360),FRandom[Wallbuster](-90,90))).unit()*FRandom[Wallbuster](9.,24.);
let s = Actor.Spawn("SWWMSpark",spot);
s.vel = pvel;
}
numpt = Random[Wallbuster](0,2);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (bustdir+SWWMUtility.Vec3FromAngles(FRandom[Wallbuster](0,360),FRandom[Wallbuster](-90,90))*.6).unit()*FRandom[Wallbuster](2.,16.);
let s = Actor.Spawn("SWWMChip",spot);
s.vel = pvel;
s.scale *= FRandom[Wallbuster](1.5,3.);
s.A_SetTranslation('Rubble');
}
}
}
private void SpawnDebrisPoly( bool initial = false )
{
for ( int i=0; i<polygrid.Size(); i++ ) for ( double z=boundsmin.z; z<boundsmax.z; z+=step.z )
{
Vector3 spot = (polygrid[i].pos.x,polygrid[i].pos.y,z);
spot += (FRandom[Wallbuster](-step.x,step.x),FRandom[Wallbuster](-step.y,step.y),FRandom[Wallbuster](-step.z,step.z));
if ( !level.IsPointInLevel(spot) ) continue;
if ( (initial || !(busttics%2)) && !Random[Wallbuster](0,1) )
{
Vector3 pvel = (bustdir+SWWMUtility.Vec3FromAngles(FRandom[Wallbuster](0,360),FRandom[Wallbuster](-90,90))).unit()*FRandom[Wallbuster](-2.,8.);
let s = Actor.Spawn("SWWMHalfSmoke",spot);
s.vel = pvel;
s.scale *= 2.5;
s.special1 = Random[Wallbuster](3,8);
s.SetShade(Color(1,1,1)*Random[Wallbuster](40,120));
}
if ( (!initial && (busttics%3)) || (busttics > (bustmax/2)) ) continue;
int numpt = Random[Wallbuster](-4,1);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (bustdir+SWWMUtility.Vec3FromAngles(FRandom[Wallbuster](0,360),FRandom[Wallbuster](-90,90))).unit()*FRandom[Wallbuster](9.,24.);
let s = Actor.Spawn("SWWMSpark",spot);
s.vel = pvel;
}
numpt = Random[Wallbuster](0,2);
for ( int i=0; i<numpt; i++ )
{
Vector3 pvel = (bustdir+SWWMUtility.Vec3FromAngles(FRandom[Wallbuster](0,360),FRandom[Wallbuster](-90,90))*.6).unit()*FRandom[Wallbuster](2.,16.);
let s = Actor.Spawn("SWWMChip",spot);
s.vel = pvel;
s.scale *= FRandom[Wallbuster](1.5,3.);
s.A_SetTranslation('Rubble');
}
}
}
private static bool IsIOSWall( Line l )
{
TextureID facetex[9];
facetex[0] = TexMan.CheckForTexture("ZZZFACE1",TexMan.Type_Wall);
facetex[1] = TexMan.CheckForTexture("ZZZFACE2",TexMan.Type_Wall);
facetex[2] = TexMan.CheckForTexture("ZZZFACE3",TexMan.Type_Wall);
facetex[3] = TexMan.CheckForTexture("ZZZFACE4",TexMan.Type_Wall);
facetex[4] = TexMan.CheckForTexture("ZZZFACE5",TexMan.Type_Wall);
facetex[5] = TexMan.CheckForTexture("DBRAIN1",TexMan.Type_Wall);
facetex[6] = TexMan.CheckForTexture("DBRAIN2",TexMan.Type_Wall);
facetex[7] = TexMan.CheckForTexture("DBRAIN3",TexMan.Type_Wall);
facetex[8] = TexMan.CheckForTexture("DBRAIN4",TexMan.Type_Wall);
for ( int i=0; i<9; i++ )
{
for ( int j=0; j<3; j++ )
{
if ( l.sidedef[0].GetTexture(j) == facetex[i] ) return true;
if ( l.sidedef[1] && l.sidedef[1].GetTexture(j) == facetex[i] ) return true;
}
}
return false;
}
static bool ProjectileBust( Actor a, int accdamage, Vector3 x )
{
LineTracer faketracer = new("LineTracer");
F3DFloor ff;
Vector3 HitNormal;
if ( a.BlockingFloor )
{
// find closest 3d floor for its normal
for ( int i=0; i<a.BlockingFloor.Get3DFloorCount(); i++ )
{
if ( !(a.BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
if ( !(a.BlockingFloor.Get3DFloor(i).top.ZAtPoint(a.pos.xy) ~== a.floorz) ) continue;
ff = a.BlockingFloor.Get3DFloor(i);
break;
}
if ( ff )
{
faketracer.Results.ffloor = ff;
HitNormal = -ff.top.Normal;
}
else HitNormal = a.BlockingFloor.floorplane.Normal;
faketracer.Results.HitType = TRACE_HitFloor;
faketracer.Results.HitSector = a.BlockingFloor;
}
else if ( a.BlockingCeiling )
{
// find closest 3d floor for its normal
for ( int i=0; i<a.BlockingCeiling.Get3DFloorCount(); i++ )
{
if ( !(a.BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
if ( !(a.BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(a.pos.xy) ~== a.ceilingz) ) continue;
ff = a.BlockingCeiling.Get3DFloor(i);
break;
}
if ( ff )
{
faketracer.Results.ffloor = ff;
HitNormal = -ff.bottom.Normal;
}
else HitNormal = a.BlockingCeiling.ceilingplane.Normal;
faketracer.Results.HitType = TRACE_HitCeiling;
faketracer.Results.HitSector = a.BlockingCeiling;
}
else if ( a.BlockingLine )
{
HitNormal = (-a.BlockingLine.delta.y,a.BlockingLine.delta.x,0).unit();
int wside = SWWMUtility.PointOnLineSide(a.pos.xy,a.BlockingLine);
if ( !wside ) HitNormal *= -1;
faketracer.Results.HitType = TRACE_HitWall;
faketracer.Results.HitLine = a.BlockingLine;
faketracer.Results.Side = wside;
faketracer.Results.Tier = TIER_Middle;
// guess the tier hit
if ( a.BlockingLine.sidedef[1] )
{
double ceil = a.BlockingLine.sidedef[!wside].sector.ceilingplane.ZAtPoint(a.pos.xy);
double flor = a.BlockingLine.sidedef[!wside].sector.floorplane.ZAtPoint(a.pos.xy);
if ( a.pos.z >= ceil ) faketracer.Results.Tier = TIER_Upper;
else if ( (a.pos.z+a.Height) <= flor ) faketracer.Results.Tier = TIER_Lower;
}
}
else if ( a.BlockingMobj )
{
Vector3 diff = level.Vec3Diff(a.BlockingMobj.Vec3Offset(0,0,a.BlockingMobj.Height/2),a.pos);
HitNormal = diff.unit();
faketracer.Results.HitType = TRACE_HitActor;
}
return Bust(faketracer.Results,accdamage,a.target,x,a.pos.z+a.Height/2.);
}
static bool BustLinetrace( FLineTraceData d, int accdamage, Actor instigator, Vector3 x, double hitz )
{
LineTracer faketracer = new("LineTracer");
faketracer.Results.HitType = d.HitType;
faketracer.Results.HitSector = d.HitSector;
faketracer.Results.HitLine = d.HitLine;
faketracer.Results.ffloor = d.Hit3DFloor;
faketracer.Results.Side = d.LineSide;
faketracer.Results.Tier = (d.LinePart==Side.Top)?TIER_UPPER:(d.LinePart==Side.Bottom)?TIER_LOWER:TIER_Middle;
return Bust(faketracer.Results,accdamage,instigator,x,hitz);
}
static bool BustPolyobj( swwm_PolyobjectHandle p, int accdamage, Actor instigator, Vector3 x )
{
let ti = ThinkerIterator.Create("BusterWall",STAT_USER);
BusterWall iter, bust = null;
while ( iter = BusterWall(ti.Next()) )
{
if ( iter.hitpoly != p ) continue;
bust = iter;
break;
}
bool mnew = false;
if ( !bust )
{
bust = new("BusterWall");
bust.ChangeStatNum(STAT_USER);
bust.hitpoly = p;
bust.accdamage = 0;
bust.bustdir = x;
mnew = true;
}
// multiply damage
if ( instigator ) accdamage = instigator.GetModifiedDamage('Wallbust',accdamage,false,instigator,instigator,0);
bust.delay = max(bust.delay,5+min(20,accdamage>>4));
bust.accdamage += accdamage;
bust.acchits.Push(accdamage);
bust.bustdir = (bust.bustdir+x)*.5;
// skip if already busted
if ( bust.busted ) return true;
// not enough total damage
if ( bust.accdamage < 100 ) return false;
// estimate polyobject volume
Vector3 a = (32767,32767,32767), b = (-32768,-32768,-32768);
for ( int i=0; i<p.lines.Size(); i++ )
{
Line l = p.lines[i];
if ( l.v1.p.x < a.x ) a.x = l.v1.p.x;
if ( l.v2.p.x < a.x ) a.x = l.v2.p.x;
if ( l.v1.p.y < a.y ) a.y = l.v1.p.y;
if ( l.v2.p.y < a.y ) a.y = l.v2.p.y;
if ( l.v1.p.x > b.x ) b.x = l.v1.p.x;
if ( l.v2.p.x > b.x ) b.x = l.v2.p.x;
if ( l.v1.p.y > b.y ) b.y = l.v1.p.y;
if ( l.v2.p.y > b.y ) b.y = l.v2.p.y;
Sector s = level.PointInSector(l.v1.p);
double fz = s.floorplane.ZAtPoint(l.v1.p);
double cz = s.ceilingplane.ZAtPoint(l.v1.p);
if ( fz < a.z ) a.z = fz;
if ( cz > b.z ) b.z = cz;
s = level.PointInSector(l.v2.p);
fz = s.floorplane.ZAtPoint(l.v2.p);
cz = s.ceilingplane.ZAtPoint(l.v2.p);
if ( fz < a.z ) a.z = fz;
if ( cz > b.z ) b.z = cz;
}
double girthitude = (b.x-a.x)*(b.y-a.y)*(b.z-a.z);
// do a grid check to approximate "real" volume
double ystep = (b.y-a.y)/64.;
double xstep = (b.x-a.x)/64.;
int inspot = 0, allspot = 0;
for ( double y=a.y; y<=b.y; y+=ystep ) for ( double x=a.x; x<=b.x; x+=xstep )
{
allspot++;
if ( !SWWMUtility.PointInPolyobj((x,y),p) ) continue;
inspot++;
}
if ( allspot <= 0 ) return false; // what the fuck?
girthitude = (girthitude*inspot)/allspot;
// too fucking huge
if ( (girthitude > 16777216) || (max(b.z-a.z,max(b.x-a.x,b.y-a.y)) > 1024) ) return false;
// not strong enough to bust
if ( bust.accdamage < girthitude/300. ) return false;
// report bust
if ( Instigator && Instigator.player )
{
let s = SWWMStats.Find(Instigator.player);
if ( s ) s.busts++;
SWWMUtility.AchievementProgressInc("bustin",1,Instigator.player);
if ( (Instigator is 'Demolitionist') && !Random[DemoLines](0,3) && (int(girthitude**.15) >= 3) )
SWWMHandler.AddOneliner("bustkill",2,50);
}
// call hit fx for devastation sigil (if any)
AngeryPower as = instigator?AngeryPower(instigator.FindInventory("AngeryPower")):null;
if ( as ) as.DoHitFX();
bust.busted = true;
bust.busttics = 0;
bust.bustmax = min(30,int(12+girthitude**.1));
// quakin'
let q = Actor.Spawn("BustedQuake",(p.LastPos.x,p.LastPos.y,(b.z+a.z)/2));
q.special1 = clamp(int(girthitude**.15),1,9);
// "precache" the grid for busting effects
bust.boundsmin = a;
bust.boundsmax = b;
bust.step = (clamp((b.x-a.x)/4.,2.,32.),clamp((b.y-a.y)/4.,2.,32.),clamp((b.z-a.z)/4.,2.,32.));
for ( double y=a.y; y<=b.y; y+=bust.step.y ) for ( double x=a.x; x<=b.x; x+=bust.step.x )
{
if ( !SWWMUtility.PointInPolyobj((x,y),p) ) continue;
let g = new("BustPoint");
g.pos = (x,y);
bust.polygrid.Push(g);
}
// stop any polyobject movement
level.ExecuteSpecial(Polyobj_Stop,instigator,p.StartLine,Line.Front,p.PolyobjectNum);
if ( p.Mirror ) level.ExecuteSpecial(Polyobj_Stop,instigator,p.Mirror.StartLine,Line.Front,p.Mirror.PolyobjectNum);
// send it to the shadow realm (and ensure it stays there)
if ( !p.FindEffector("SWWMBustedPolyobj") )
{
let yeet = new("SWWMBustedPolyobj");
yeet.whomstdve = instigator;
p.AddEffector(yeet);
}
bust.SpawnDebris(true);
// damnums
Vector3 bcenter = (bust.boundsmin+bust.boundsmax)*.5;
for ( int i=0; i<bust.acchits.Size(); i++ )
SWWMDamNum.Spawn(bust.acchits[i],level.Vec3Offset(bcenter,SWWMUtility.Vec3FromAngles(FRandom[ScoreBits](0,360),FRandom[ScoreBits](-90,90))*8.),'Wallbust');
return true;
}
static bool Bust( TraceResults d, int accdamage, Actor instigator, Vector3 x, double hitz )
{
// we can't blow up 3D floors
if ( d.ffloor ) return false;
Sector hs = d.HitSector;
int hp;
if ( d.HitType == TRACE_HitWall )
{
// check if it's a polyobject line, if so, switch to the other bust method
swwm_PolyObjectHandle p;
if ( SWWMUtility.IsPolyLine(d.HitLine,p) ) return BustPolyobj(p,accdamage,instigator,x);
// no busting the goat
if ( IsIOSWall(d.HitLine) ) return false;
// onesided wall? no bust
if ( !d.HitLine.sidedef[1] ) return false;
// sector is opposite of side hit
hs = d.HitLine.sidedef[!d.Side].sector;
// what part we hit?
if ( d.Tier == TIER_Upper ) hp = 1; // ceiling
else if ( d.Tier == TIER_Lower ) hp = 0; // floor
else return false; // middle ignored
}
else if ( d.HitType == TRACE_HitCeiling )
{
// no busting the goat
for ( int i=0; i<hs.lines.Size(); i++ )
{
if( IsIOSWall(hs.lines[i]) ) return false;
}
hp = 1;
}
else if ( d.HitType == TRACE_HitFloor )
{
// no busting the goat
for ( int i=0; i<hs.lines.Size(); i++ )
{
if( IsIOSWall(hs.lines[i]) ) return false;
}
hp = 0;
}
else return false; // this isn't a valid hit, needs to be world geometry
// Check if it's a door
if ( !swwm_cbtall && !SWWMUtility.IsDoorSector(hs,hp) ) return false;
let ti = ThinkerIterator.Create("BusterWall",STAT_USER);
BusterWall iter, bust = null;
while ( iter = BusterWall(ti.Next()) )
{
if ( (iter.hitsector != hs) || (iter.hitplane != hp) ) continue;
bust = iter;
break;
}
bool mnew = false;
if ( !bust )
{
bust = new("BusterWall");
bust.ChangeStatNum(STAT_USER);
bust.hitsector = hs;
bust.accdamage = 0;
bust.hitplane = hp;
bust.bustdir = x;
mnew = true;
}
// multiply damage
if ( instigator ) accdamage = instigator.GetModifiedDamage('Wallbust',accdamage,false,instigator,instigator,0);
bust.delay = max(bust.delay,5+min(20,accdamage>>4));
bust.accdamage += accdamage;
bust.acchits.Push(accdamage);
bust.bustdir = (bust.bustdir+x)*.5;
double extracut = FRandom[Wallbuster](.01,.04)*bust.accdamage;
// is this actually sticking out?
double thisheight, othersheight, partheight, cutheight;
if ( hp )
{
thisheight = hs.FindLowestCeilingPoint();
othersheight = hs.FindHighestCeilingSurrounding();
if ( (thisheight-othersheight) >= 0. ) return false;
cutheight = min(hitz+extracut,othersheight+4);
}
else
{
thisheight = hs.FindHighestFloorPoint();
othersheight = hs.FindLowestFloorSurrounding();
if ( (thisheight-othersheight) <= 0. ) return false;
cutheight = max(hitz-extracut,othersheight-4);
}
if ( hp ) bust.cutheight = mnew?cutheight:max(bust.cutheight,cutheight);
else bust.cutheight = mnew?cutheight:min(bust.cutheight,cutheight);
partheight = abs(thisheight-bust.cutheight);
// skip if we don't cut off enough
if ( partheight <= 0. ) return false;
// skip if already busted
if ( bust.busted ) return true;
// not enough total damage
if ( bust.accdamage < 100 ) return false;
// estimate sector volume
Vector2 a = (32767,32767), b = (-32768,-32768);
for ( int i=0; i<hs.lines.Size(); i++ )
{
Line l = hs.lines[i];
if ( l.v1.p.x < a.x ) a.x = l.v1.p.x;
if ( l.v2.p.x < a.x ) a.x = l.v2.p.x;
if ( l.v1.p.y < a.y ) a.y = l.v1.p.y;
if ( l.v2.p.y < a.y ) a.y = l.v2.p.y;
if ( l.v1.p.x > b.x ) b.x = l.v1.p.x;
if ( l.v2.p.x > b.x ) b.x = l.v2.p.x;
if ( l.v1.p.y > b.y ) b.y = l.v1.p.y;
if ( l.v2.p.y > b.y ) b.y = l.v2.p.y;
}
double girthitude = (b.x-a.x)*(b.y-a.y)*partheight;
// do a grid check to approximate "real" volume, useful for diagonal doors
double ystep = (b.y-a.y)/64.;
double xstep = (b.x-a.x)/64.;
int inspot = 0, allspot = 0;
for ( double y=a.y; y<=b.y; y+=ystep ) for ( double x=a.x; x<=b.x; x+=xstep )
{
allspot++;
if ( level.PointInSector((x,y)) == hs ) inspot++;
}
if ( allspot <= 0 ) return false; // what the fuck?
girthitude = (girthitude*inspot)/allspot;
// too fucking huge
if ( (girthitude > 16777216) || (max(partheight,max(b.x-a.x,b.y-a.y)) > 1024) ) return false;
// not strong enough to bust
if ( bust.accdamage < girthitude/300. ) return false;
// report bust
if ( Instigator && Instigator.player )
{
let s = SWWMStats.Find(Instigator.player);
if ( s ) s.busts++;
SWWMUtility.AchievementProgressInc("bustin",1,Instigator.player);
if ( (Instigator is 'Demolitionist') && !Random[DemoLines](0,3) && (int(girthitude**.15) >= 3) )
SWWMHandler.AddOneliner("bustkill",2,50);
}
// call hit fx for devastation sigil (if any)
AngeryPower as = instigator?AngeryPower(instigator.FindInventory("AngeryPower")):null;
if ( as ) as.DoHitFX();
bust.busted = true;
bust.busttics = 0;
bust.bustmax = min(30,int(12+girthitude**.1));
// shush
hs.flags |= Sector.SECF_SILENTMOVE;
// filler texture
TextureID rubble = TexMan.CheckForTexture("ASHWALL2");
// equivalents for other iwads
if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("ASHWALL");
if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("LOOSERCK");
if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("WASTE03");
if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("textures/DefaultTexture.png"); // a fun little fallback should none of those exist
// activate all shoot/use specials (not locked) associated with this sector's two-sided lines
for ( int i=0; i<hs.Lines.Size(); i++ )
{
Line l = hs.Lines[i];
int locknum = SWWMUtility.GetLineLock(l);
if ( locknum && (!instigator || !instigator.CheckKeys(locknum,false,true)) ) continue;
if ( !l.sidedef[1] ) continue;
int away = 0;
if ( l.sidedef[0].sector == hs ) away = 1;
l.Activate(instigator,away,SPAC_Use);
l.Activate(instigator,away,SPAC_Impact);
}
// if this is a broken crusher, we need to clear that now that we've busted it
SWWMCrusherBroken.Remove(hp?null:hs,hp?hs:null);
// quakin'
let q = Actor.Spawn("BustedQuake",(hs.centerspot.x,hs.centerspot.y,thisheight));
q.special1 = clamp(int(girthitude**.15),1,9);
if ( hp )
{
// remove any current movers
if ( hs.CeilingData ) hs.CeilingData.Destroy();
// blow up that ceiling
hs.MoveCeiling(abs(partheight),bust.cutheight,0,1,false);
bust.boundsmin = (a.x,a.y,thisheight)+(1,1,1);
bust.boundsmax = (b.x,b.y,bust.cutheight)-(1,1,1);
// prevent any further ceiling movement
level.CreateCeiling(hs,Ceiling.ceilRaiseByValue,null,0.,0.,1.);
hs.StopSoundSequence(CHAN_VOICE);
}
else
{
// remove any current movers
if ( hs.FloorData ) hs.FloorData.Destroy();
// blow up that floor
hs.MoveFloor(abs(partheight),abs(bust.cutheight),0,-1,false,true);
bust.boundsmin = (a.x,a.y,bust.cutheight)+(1,1,1);
bust.boundsmax = (b.x,b.y,thisheight)-(1,1,1);
// prevent any further floor movement
level.CreateFloor(hs,Floor.floorLowerByValue,null,0.,1.);
hs.StopSoundSequence(CHAN_WEAPON);
}
bust.step = (clamp((b.x-a.x)/4.,2.,32.),clamp((b.y-a.y)/4.,2.,32.),clamp(partheight/4.,2.,32.));
bust.SpawnDebris(true);
hs.SetTexture(hp,rubble);
hs.SetXScale(hp,1.);
hs.SetYScale(hp,1.);
hs.SetAngle(hp,0.);
for ( int i=0; i<hs.Lines.Size(); i++ )
{
Line l = hs.Lines[i];
if ( !l.sidedef[1] )
{
if ( hp && !(l.flags&Line.ML_DONTPEGBOTTOM) )
l.sidedef[0].AddTextureYOffset(1,-partheight); // shift down
else if ( !hp && (l.flags&Line.ML_DONTPEGBOTTOM) )
l.sidedef[0].AddTextureYOffset(1,partheight); // shift up
continue;
}
int away = 0;
if ( l.sidedef[0].sector == hs ) away = 1;
for ( int j=0; j<2; j++ )
{
if ( l.sidedef[j].GetTexture(hp?0:2).IsValid() )
{
if ( j == away )
{
if ( hp && !(l.flags&Line.ML_DONTPEGTOP) )
l.sidedef[j].AddTextureYOffset(0,-partheight); // shift down
else if ( !hp && !(l.flags&Line.ML_DONTPEGBOTTOM) )
l.sidedef[j].AddTextureYOffset(2,partheight); // shift up
}
}
else
{
l.sidedef[j].SetTexture(hp?0:2,rubble);
l.sidedef[j].SetTextureXScale(hp?0:2,1.);
l.sidedef[j].SetTextureYScale(hp?0:2,1.);
}
}
}
// damnums
Vector3 bcenter = (bust.boundsmin+bust.boundsmax)*.5;
for ( int i=0; i<bust.acchits.Size(); i++ )
SWWMDamNum.Spawn(bust.acchits[i],level.Vec3Offset(bcenter,SWWMUtility.Vec3FromAngles(FRandom[ScoreBits](0,360),FRandom[ScoreBits](-90,90))*8.),'Wallbust');
return true;
}
}