Implement polyobject busting.
This commit is contained in:
parent
d532ddc3fa
commit
8094c98fe5
7 changed files with 323 additions and 8 deletions
|
|
@ -1,3 +1,3 @@
|
|||
[default]
|
||||
SWWM_MODVER="\chSWWM \czGZ\c- \cw1.1.10 r2 \cu(Tue 28 Sep 15:54:58 CEST 2021)\c-";
|
||||
SWWM_SHORTVER="\cw1.1.10 r2 \cu(2021-09-28 15:54:58)\c-";
|
||||
SWWM_MODVER="\chSWWM \czGZ\c- \cw1.1.10 r2 \cu(Tue 28 Sep 18:43:27 CEST 2021)\c-";
|
||||
SWWM_SHORTVER="\cw1.1.10 r2 \cu(2021-09-28 18:43:27)\c-";
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ class swwm_PolyobjectHandle: Thinker
|
|||
// Line defining the polyobject (Polyobj_StartLine, or one of Polyobj_ExplicitLine)
|
||||
Line StartLine;
|
||||
|
||||
// [MK] All lines belonging to the polyobject
|
||||
Array<Line> Lines;
|
||||
|
||||
// Initial angle of StartLine
|
||||
double StartAngle;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ class swwm_PolyobjectHandlePostProcessor: LevelPostProcessor
|
|||
{
|
||||
// Ignore every thing that isn't a Polyobject StartSpot
|
||||
int ednum = GetThingEdNum(i);
|
||||
// [MK] hotfix for this to recognize hexen polyobjects
|
||||
if (gameinfo.gametype&GAME_Hexen)
|
||||
{
|
||||
if (ednum == 3001) ednum = swwm_PolyobjectHandle.POTYP_NORMAL;
|
||||
else if (ednum == 3002) ednum = swwm_PolyobjectHandle.POTYP_CRUSH;
|
||||
}
|
||||
if (ednum < swwm_PolyobjectHandle.POTYP_NORMAL || ednum > swwm_PolyobjectHandle.POTYP_HURT)
|
||||
continue;
|
||||
|
||||
|
|
@ -79,6 +85,39 @@ class swwm_PolyobjectHandlePostProcessor: LevelPostProcessor
|
|||
|
||||
// Store the line
|
||||
handle.StartLine = line;
|
||||
|
||||
// [MK] the library doesn't store ALL lines belonging to the polyobject, but we need them
|
||||
handle.Lines.Push(line);
|
||||
|
||||
// [MK] collect all connected lines if this is Polyobj_StartLine
|
||||
if ( line.Special != Polyobj_StartLine )
|
||||
continue;
|
||||
|
||||
bool newlines;
|
||||
do
|
||||
{
|
||||
newlines = false;
|
||||
for (int j = 0; j < Level.Lines.Size(); j++)
|
||||
{
|
||||
Line linea = Level.Lines[j];
|
||||
if (handle.Lines.Find(linea) < handle.Lines.Size())
|
||||
continue;
|
||||
bool nomatches = true;
|
||||
for (int k = 0; k < handle.Lines.Size(); k++)
|
||||
{
|
||||
Line lineb = handle.Lines[k];
|
||||
if ((linea.v1 != lineb.v1) && (linea.v1 != lineb.v2) && (linea.v2 != lineb.v1) && (linea.v2 != lineb.v2))
|
||||
continue;
|
||||
nomatches = false;
|
||||
break;
|
||||
}
|
||||
if (nomatches)
|
||||
continue;
|
||||
newlines = true;
|
||||
handle.Lines.Push(linea);
|
||||
}
|
||||
}
|
||||
while (newlines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1579,6 +1579,47 @@ Class Demolitionist : PlayerPawn
|
|||
}
|
||||
SWWMUtility.MarkAchievement('swwm_achievement_crush',player);
|
||||
}
|
||||
private void CheckBreakPolyobject( int dmg )
|
||||
{
|
||||
// see if there are any crushing polyobjects currently "encroaching" the player
|
||||
Array<Line> touching;
|
||||
BlockLinesIterator bl = BlockLinesIterator.Create(self,radius+8);
|
||||
double tbox[4];
|
||||
// top, bottom, left, right
|
||||
tbox[0] = pos.y+(radius+8);
|
||||
tbox[1] = pos.y-(radius+8);
|
||||
tbox[2] = pos.x-(radius+8);
|
||||
tbox[3] = pos.x+(radius+8);
|
||||
while ( bl.Next() )
|
||||
{
|
||||
Line l = bl.CurLine;
|
||||
if ( !l ) continue;
|
||||
if ( tbox[2] > l.bbox[3] ) continue;
|
||||
if ( tbox[3] < l.bbox[2] ) continue;
|
||||
if ( tbox[0] < l.bbox[1] ) continue;
|
||||
if ( tbox[1] > l.bbox[0] ) continue;
|
||||
if ( SWWMUtility.BoxOnLineSide(tbox[0],tbox[1],tbox[2],tbox[3],l) != -1 ) continue;
|
||||
touching.Push(l);
|
||||
}
|
||||
let pi = swwm_PolyobjectIterator.Create();
|
||||
swwm_PolyobjectHandle p;
|
||||
while ( p = pi.Next() )
|
||||
{
|
||||
if ( (p.Type != swwm_PolyobjectHandle.POTYP_CRUSH) && (p.Type != swwm_PolyobjectHandle.POTYP_HURT) )
|
||||
continue;
|
||||
for ( int i=0; i<touching.Size(); i++ )
|
||||
{
|
||||
if ( p.Lines.Find(touching[i]) >= p.Lines.Size() ) continue;
|
||||
Vector2 diragainst = pos.xy-p.GetPos();
|
||||
double vsiz = diragainst.length();
|
||||
if ( vsiz > 0 ) diragainst /= vsiz;
|
||||
if ( BusterWall.BustPolyobj(p,max(dmg,(100-health)*5),self,(diragainst.x,diragainst.y,0)) )
|
||||
SWWMUtility.MarkAchievement('swwm_achievement_crush',player);
|
||||
if ( p.Mirror && BusterWall.BustPolyobj(p.Mirror,max(dmg,(100-health)*5),self,-(diragainst.x,diragainst.y,0)) )
|
||||
SWWMUtility.MarkAchievement('swwm_achievement_crush',player);
|
||||
}
|
||||
}
|
||||
}
|
||||
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
|
||||
{
|
||||
// we still have to ENSURE ENTIRELY that this gets nullified (TELEFRAG_DAMAGE overrides damage factors somehow)
|
||||
|
|
@ -1592,7 +1633,12 @@ Class Demolitionist : PlayerPawn
|
|||
if ( mod == 'Crush' )
|
||||
{
|
||||
// check if we can break any active crushers
|
||||
if ( !inflictor && !source ) CheckBreakCrusher();
|
||||
// (or polyobjects)
|
||||
if ( !inflictor && !source )
|
||||
{
|
||||
CheckBreakCrusher();
|
||||
CheckBreakPolyobject(damage);
|
||||
}
|
||||
// break a spike trap
|
||||
else if ( source is 'ThrustFloor' )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -151,6 +151,22 @@ Class SWWMDamageAccumulator : Thinker
|
|||
}
|
||||
}
|
||||
|
||||
// ensures a polyobj stays out of bounds FOREVER
|
||||
Class SWWMBustedPolyobj : swwm_PolyobjectEffector
|
||||
{
|
||||
Actor whomstdve;
|
||||
|
||||
override void PolyTick()
|
||||
{
|
||||
if ( Polyobject.GetPos() == (32000,32000) ) return;
|
||||
double dist = (Polyobject.GetPos()-(32000,32000)).length();
|
||||
Level.ExecuteSpecial(Polyobj_Stop,whomstdve,Polyobject.StartLine,Line.Front,Polyobject.PolyobjectNum);
|
||||
if ( Polyobject.Mirror ) Level.ExecuteSpecial(Polyobj_Stop,whomstdve,Polyobject.Mirror.StartLine,Line.Front,Polyobject.Mirror.PolyobjectNum);
|
||||
Level.ExecuteSpecial(Polyobj_MoveTo,whomstdve,Polyobject.StartLine,Line.Front,Polyobject.PolyobjectNum,int(dist*8),32000,32000);
|
||||
if ( Polyobject.Mirror ) Level.ExecuteSpecial(Polyobj_Stop,whomstdve,Polyobject.Mirror.StartLine,Line.Front,Polyobject.Mirror.PolyobjectNum);
|
||||
}
|
||||
}
|
||||
|
||||
// prevents floors/ceilings from ever moving again, as they're "broken crushers"
|
||||
Class SWWMCrusherBroken : Thinker
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1741,12 +1741,62 @@ Class SWWMUtility
|
|||
return false, checkme;
|
||||
}
|
||||
|
||||
static bool IsPolyLine( Line l, out swwm_PolyobjectHandle p )
|
||||
// iterate through polyobjects and see if this line is part of one (returning which, if any)
|
||||
static bool IsPolyLine( Line l, out swwm_PolyobjectHandle o )
|
||||
{
|
||||
// TODO iterate through polyobjects and see if this line is connected to their start line
|
||||
let pi = swwm_PolyobjectIterator.Create();
|
||||
swwm_PolyobjectHandle p;
|
||||
while ( p = pi.Next() )
|
||||
{
|
||||
if ( p.Lines.Find(l) >= p.Lines.Size() ) continue;
|
||||
o = p;
|
||||
return true;
|
||||
}
|
||||
o = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// checks if the specified world coordinate is inside the polyobject
|
||||
// this check is very naive but it should handle most "normal" shapes
|
||||
// (yeah, sorry if you somehow want to play this mod with lilith.pk3)
|
||||
static bool PointInPolyobj( Vector2 p, swwm_PolyobjectHandle o )
|
||||
{
|
||||
// first pass, find which vertex out of all lines is closest
|
||||
Vertex v = o.StartLine.v1;
|
||||
double dist = (v.p-p).length();
|
||||
for ( int i=0; i<o.Lines.Size(); i++ )
|
||||
{
|
||||
Line l = o.Lines[i];
|
||||
double dist2 = (l.v1.p-p).length();
|
||||
if ( dist2 < dist )
|
||||
{
|
||||
v = l.v1;
|
||||
dist = dist2;
|
||||
}
|
||||
dist2 = (l.v2.p-p).length();
|
||||
if ( dist2 < dist )
|
||||
{
|
||||
v = l.v2;
|
||||
dist = dist2;
|
||||
}
|
||||
}
|
||||
// second pass, find which two lines share that vertex
|
||||
// (in theory there should only be two)
|
||||
Line a = null, b = null;
|
||||
for ( int i=0; i<o.Lines.Size(); i++ )
|
||||
{
|
||||
Line l = o.Lines[i];
|
||||
if ( (l.v1 == v) || (l.v2 == v) )
|
||||
{
|
||||
if ( !a ) a = l;
|
||||
else if ( !b ) b = l;
|
||||
else break;
|
||||
}
|
||||
}
|
||||
// is the point behind both lines?
|
||||
return (PointOnLineSide(p,a) && PointOnLineSide(p,b));
|
||||
}
|
||||
|
||||
// full reset of inventory (excluding collectibles, and optionally resetting the score)
|
||||
static play void WipeInventory( Actor mo, bool resetscore = false, bool allplayers = false )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -41,10 +41,16 @@ Class BustedQuake : Actor
|
|||
}
|
||||
}
|
||||
|
||||
Class BustPoint
|
||||
{
|
||||
Vector2 pos;
|
||||
}
|
||||
|
||||
// Bustin' makes me feel good
|
||||
Class BusterWall : Thinker
|
||||
{
|
||||
Sector hitsector;
|
||||
swwm_PolyobjectHandle hitpoly;
|
||||
int accdamage;
|
||||
Array<int> acchits;
|
||||
int hitplane;
|
||||
|
|
@ -54,6 +60,7 @@ Class BusterWall : Thinker
|
|||
double cutheight;
|
||||
// cached
|
||||
Vector3 boundsmin, boundsmax, step;
|
||||
Array<BustPoint> polygrid;
|
||||
|
||||
override void Tick()
|
||||
{
|
||||
|
|
@ -84,6 +91,11 @@ Class BusterWall : Thinker
|
|||
|
||||
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 )
|
||||
|
|
@ -122,6 +134,42 @@ Class BusterWall : Thinker
|
|||
}
|
||||
}
|
||||
|
||||
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+(FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.))).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+(FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.),FRandom[Wallbuster](-1.,1.))).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+(FRandom[Wallbuster](-.6,.6),FRandom[Wallbuster](-.6,.6),FRandom[Wallbuster](-.6,.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];
|
||||
|
|
@ -227,9 +275,122 @@ Class BusterWall : Thinker
|
|||
return Bust(faketracer.Results,accdamage,instigator,x,hitz);
|
||||
}
|
||||
|
||||
private static bool BustPolyobj( swwm_PolyobjectHandle p, int accdamage, Actor instigator, Vector3 x, double hitz )
|
||||
static bool BustPolyobj( swwm_PolyobjectHandle p, int accdamage, Actor instigator, Vector3 x )
|
||||
{
|
||||
return false;
|
||||
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('swwm_progress_bustin',1,Instigator.player);
|
||||
}
|
||||
// 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;
|
||||
if ( swwm_accdamage )
|
||||
SWWMScoreObj.Spawn(-bust.accdamage,level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),ST_Damage);
|
||||
else for ( int i=0; i<bust.acchits.Size(); i++ )
|
||||
SWWMScoreObj.Spawn(-bust.acchits[i],level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),ST_Damage);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Bust( TraceResults d, int accdamage, Actor instigator, Vector3 x, double hitz )
|
||||
|
|
@ -242,7 +403,7 @@ Class BusterWall : Thinker
|
|||
{
|
||||
// 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,hitz);
|
||||
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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue