Final round of tuning for wall busting, it's even more HD-like now.

Add option to disable unrestricted Wallbuster destruction.
Add wall penetration to Silver Bullet JET.
This commit is contained in:
Mari the Deer 2020-06-20 17:19:13 +02:00
commit b5947709f7
11 changed files with 417 additions and 74 deletions

View file

@ -448,6 +448,7 @@ Class BusterWall : Thinker
bool busted;
Vector3 bustdir;
int busttics;
double cutheight;
// cached
Vector3 boundsmin, boundsmax, step;
@ -513,7 +514,7 @@ Class BusterWall : Thinker
}
}
static void Bust( TraceResults d, int accdamage, Actor instigator, Vector3 x )
static void Bust( TraceResults d, int accdamage, Actor instigator, Vector3 x, double hitz )
{
// we can't blow up 3D floors (yet)
if ( d.ffloor ) return;
@ -521,7 +522,7 @@ Class BusterWall : Thinker
int hp;
if ( d.HitType == TRACE_HitWall )
{
// onesided wall? no bust
// onesided wall? no bust (TODO: bust polyobjects)
if ( !d.HitLine.sidedef[1] ) return;
// sector is opposite of side hit
hs = d.HitLine.sidedef[!d.Side].sector;
@ -537,43 +538,54 @@ Class BusterWall : Thinker
int lcnt = 0;
for ( int i=0; i<hs.Lines.Size(); i++ ) if ( hs.Lines[i].sidedef[1] ) lcnt++;
if ( lcnt < 2 ) return;
// is this actually sticking out?
double thisheight, othersheight, partheight;
if ( hp )
{
thisheight = hs.FindLowestCeilingPoint();
othersheight = hs.FindHighestCeilingSurrounding();
if ( (thisheight-othersheight) >= -4. ) return;
}
else
{
thisheight = hs.FindHighestFloorPoint();
othersheight = hs.FindLowestFloorSurrounding();
if ( (thisheight-othersheight) <= 4. ) return;
}
partheight = abs(thisheight-othersheight);
// Check if it's a door
if ( !swwm_cbtall && !SWWMUtility.IsDoorSector(hs,hp) ) return;
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;
bust.accdamage += accdamage;
bust.bustdir = (bust.bustdir+x)*.5;
break;
}
bool mnew = false;
if ( !bust )
{
bust = new("BusterWall");
bust.ChangeStatNum(STAT_USER);
bust.hitsector = hs;
bust.accdamage = accdamage;
bust.accdamage = 0;
bust.hitplane = hp;
bust.bustdir = x;
mnew = true;
}
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) >= -4. ) return;
cutheight = min(hitz+extracut,othersheight-4);
}
else
{
thisheight = hs.FindHighestFloorPoint();
othersheight = hs.FindLowestFloorSurrounding();
if ( (thisheight-othersheight) <= 4. ) return;
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 < 4. ) return;
// skip if already busted
if ( bust.busted ) return;
bust.acchits.Push(accdamage);
// not enough total damage
if ( bust.accdamage < 1000 ) return;
// estimate sector volume
@ -591,8 +603,18 @@ Class BusterWall : Thinker
if ( l.v2.p.y > b.y ) a.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++;
}
girthitude = (girthitude*inspot)/allspot;
// too fucking huge
if ( girthitude > 16777216 ) return;
if ( (girthitude > 16777216) || (max(partheight,max(b.x-a.x,b.y-a.y)) > 1024) ) return;
// not strong enough to bust
if ( bust.accdamage < girthitude/300. ) return;
bust.busted = true;
@ -606,39 +628,6 @@ Class BusterWall : Thinker
if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("LOOSERCK",TexMan.Type_Any);
if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("WASTE03",TexMan.Type_Any);
if ( !rubble.IsValid() ) rubble = TexMan.CheckForTexture("SCHWAL01",TexMan.Type_Any); // Strife has a dedicated "destroyed wall" texture, nice
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 )
{
// shift down
if ( !(l.flags&Line.ML_DONTPEGBOTTOM) )
l.sidedef[0].AddTextureYOffset(1,-abs(othersheight-thisheight)+4);
}
else
{
// shift up
if ( l.flags&Line.ML_DONTPEGBOTTOM )
l.sidedef[0].AddTextureYOffset(1,abs(othersheight-thisheight)-4);
}
continue;
}
int away = 0;
if ( l.sidedef[0].sector == hs ) away = 1;
for ( int j=0; j<2; j++ )
{
if ( (j != away) && l.sidedef[j].GetTexture(hp?0:2).IsValid() ) continue;
l.sidedef[j].SetTexture(hp?0:2,rubble);
l.sidedef[j].SetTextureXScale(hp?0:2,1.);
l.sidedef[j].SetTextureYScale(hp?0:2,1.);
}
}
// activate all shoot/use specials (not locked) associated with this sector's two-sided lines
for ( int i=0; i<hs.Lines.Size(); i++ )
{
@ -648,8 +637,17 @@ Class BusterWall : Thinker
if ( !l.sidedef[1] ) continue;
int away = 0;
if ( l.sidedef[0].sector == hs ) away = 1;
// temporarily set filler texture so switches don't play a sound
TextureID oldtex[3][2];
for ( int j=0; j<3; j++ ) for ( int k=0; k<2; k++ )
{
oldtex[j][k] = l.sidedef[k].GetTexture(j);
l.sidedef[k].SetTexture(j,rubble);
}
l.Activate(instigator,away,SPAC_Use);
l.Activate(instigator,away,SPAC_Impact);
for ( int j=0; j<3; j++ ) for ( int k=0; k<2; k++ )
l.sidedef[k].SetTexture(j,oldtex[j][k]);
// clear any use/impact specials
if ( l.Activation&(SPAC_Use|SPAC_Impact) )
l.Activation &= ~(SPAC_Use|SPAC_Impact);
@ -662,27 +660,62 @@ Class BusterWall : Thinker
if ( se.GetSector() != hs ) continue;
se.Destroy();
}
bust.step = (clamp((b.x-a.x)/4.,2.,32.),clamp((b.y-a.y)/4.,2.,32.),clamp((abs(othersheight-thisheight)-4)/4.,2.,32.));
bust.step = (clamp((b.x-a.x)/4.,2.,32.),clamp((b.y-a.y)/4.,2.,32.),clamp(partheight/4.,2.,32.));
// quakin'
let q = Actor.Spawn("BustedQuake",(hs.centerspot.x,hs.centerspot.y,thisheight));
q.special1 = clamp(int(girthitude**.15),1,9);
if ( hp )
{
// blow up that ceiling
double destheight = othersheight-4;
hs.MoveCeiling(abs(destheight-thisheight),destheight,0,1,false);
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,destheight)-(1,1,1);
bust.boundsmax = (b.x,b.y,bust.cutheight)-(1,1,1);
}
else
{
// blow up that floor
double destheight = othersheight+4;
hs.MoveFloor(abs(destheight-thisheight),abs(destheight),0,-1,false,true);
bust.boundsmin = (a.x,a.y,destheight)+(1,1,1);
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);
}
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;
if ( CVar.GetCVar('swwm_accdamage',players[consoleplayer]).GetBool() )
@ -886,7 +919,7 @@ Class Wallbuster : SWWMWeapon
// Wall busting
int bustdmg = dmg;
if ( t is 'SpreadSlugTracer' ) bustdmg = int(SpreadSlugTracer(t).penetration);
BusterWall.Bust(t.Results,bustdmg,self,t.Results.HitVector);
BusterWall.Bust(t.Results,bustdmg,self,t.Results.HitVector,t.Results.HitPos.z);
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
{
t.ShootThroughList[i].Activate(self,0,SPAC_PCross);

View file

@ -217,6 +217,144 @@ Class SWWMUtility
{
return int(ceil(x/100.)*100.);
}
static play bool IsDoorSector( Sector s, int part )
{
// super-easy mode: check for boss special sectors
if ( (level.mapname ~== "E1M8") || (level.mapname ~== "E2M8") || (level.mapname ~== "E3M8")
|| (level.mapname ~== "E4M6") || (level.mapname ~== "E4M8") || (level.mapname ~== "E5M8")
|| (level.mapname ~== "MAP07") )
{
let si = level.CreateSectorTagIterator(666);
int idx;
while ( (idx = si.Next()) != -1 )
if ( level.Sectors[idx] == s )
return true;
if ( level.mapname ~== "MAP07" )
{
let si2 = level.CreateSectorTagIterator(667);
while ( (idx = si.Next()) != -1 )
if ( level.Sectors[idx] == s )
return true;
}
}
// easy mode: check if it's already moving
if ( part )
{
let ti = ThinkerIterator.Create("MovingCeiling",Thinker.STAT_SECTOREFFECT);
MovingCeiling mc;
while ( mc = MovingCeiling(ti.Next()) )
if ( mc.GetSector() == s )
return true;
}
else
{
let ti = ThinkerIterator.Create("MovingFloor",Thinker.STAT_SECTOREFFECT);
MovingFloor mf;
while ( mf = MovingFloor(ti.Next()) )
if ( mf.GetSector() == s )
return true;
}
// hard mode: look for all lines/actors with movement specials referencing us
for ( int i=0; i<level.Lines.Size(); i++ )
{
Line l = level.Lines[i];
if ( !l.special ) continue;
if ( (part && (l.special >= 10) && (l.special <= 13))
|| (!part && (l.special >= 20) && (l.special <= 25))
|| (!part && (l.special == 28))
|| ((l.special >= 29) && (l.special <= 30))
|| (!part && (l.special >= 35) && (l.special <= 37))
|| (part && (l.special >= 40) && (l.special <= 45))
|| (!part && (l.special == 46))
|| (part && (l.special == 47))
|| (!part && (l.special >= 60) && (l.special <= 68))
|| (part && (l.special == 69))
|| ((l.special >= 94) && (l.special <= 96))
|| (part && (l.special == 97))
|| (!part && (l.special == 99))
|| (part && (l.special == 104))
|| (part && (l.special >= 105) && (l.special <= 106))
|| (part && (l.special >= 168) && (l.special <= 169))
|| (!part && (l.special == 172))
|| (part && (l.special >= 192) && (l.special <= 199))
|| (!part && (l.special == 200))
|| (part && (l.special >= 201) && (l.special <= 202))
|| (!part && (l.special == 203))
|| (part && (l.special == 205))
|| (!part && (l.special >= 206) && (l.special <= 207))
|| (!part && (l.special == 228))
|| (!part && (l.special >= 230) && (l.special <= 231))
|| (!part && (l.special >= 238) && (l.special <= 242))
|| ((l.special >= 245) && (l.special <= 247))
|| (part && (l.special == 249))
|| (!part && (l.special >= 250) && (l.special <= 251))
|| (part && (l.special >= 251) && (l.special <= 255))
|| (!part && (l.special >= 256) && (l.special <= 261))
|| (part && (l.special >= 262) && (l.special <= 269))
|| (!part && (l.special == 275))
|| (part && (l.special == 276))
|| (!part && (l.special == 279))
|| (part && (l.special == 280)) )
{
let si = level.CreateSectorTagIterator(l.Args[0],l);
int idx;
while ( (idx = si.Next()) != -1 )
if ( level.Sectors[idx] == s )
return true;
}
}
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a = Actor(ti.Next()) )
{
if ( !a.special || !a.Args[0] ) continue;
if ( (part && (a.special >= 10) && (a.special <= 13))
|| (!part && (a.special >= 20) && (a.special <= 25))
|| (!part && (a.special == 28))
|| ((a.special >= 29) && (a.special <= 30))
|| (!part && (a.special >= 35) && (a.special <= 37))
|| (part && (a.special >= 40) && (a.special <= 45))
|| (!part && (a.special == 46))
|| (part && (a.special == 47))
|| (!part && (a.special >= 60) && (a.special <= 68))
|| (part && (a.special == 69))
|| ((a.special >= 94) && (a.special <= 96))
|| (part && (a.special == 97))
|| (!part && (a.special == 99))
|| (part && (a.special == 104))
|| (part && (a.special >= 105) && (a.special <= 106))
|| (part && (a.special >= 168) && (a.special <= 169))
|| (!part && (a.special == 172))
|| (part && (a.special >= 192) && (a.special <= 199))
|| (!part && (a.special == 200))
|| (part && (a.special >= 201) && (a.special <= 202))
|| (!part && (a.special == 203))
|| (part && (a.special == 205))
|| (!part && (a.special >= 206) && (a.special <= 207))
|| (!part && (a.special == 228))
|| (!part && (a.special >= 230) && (a.special <= 231))
|| (!part && (a.special >= 238) && (a.special <= 242))
|| ((a.special >= 245) && (a.special <= 247))
|| (part && (a.special == 249))
|| (!part && (a.special >= 250) && (a.special <= 251))
|| (part && (a.special >= 251) && (a.special <= 255))
|| (!part && (a.special >= 256) && (a.special <= 261))
|| (part && (a.special >= 262) && (a.special <= 269))
|| (!part && (a.special == 275))
|| (part && (a.special == 276))
|| (!part && (a.special == 279))
|| (part && (a.special == 280)) )
{
let si = level.CreateSectorTagIterator(a.Args[0]);
int idx;
while ( (idx = si.Next()) != -1 )
if ( level.Sectors[idx] == s )
return true;
}
}
return false;
}
}
// Stats

View file

@ -823,7 +823,7 @@ Class Demolitionist : PlayerPawn
}
if ( dodge == (0,0,0) ) dodge.xy = RotateVector((1,0),angle);
if ( (waterlevel < 2) && !(bFly || bFlyCheat) )
dodge.z += .15;
dodge.z += (player.cmd.buttons&BT_CROUCH)?.05:.15;
if ( (dodge.length() > 0) && (dashcooldown <= 0) && (dashfuel > 20.) && player.cmd.buttons&BT_USER2 && (player.onground || level.IsJumpingAllowed()) )
{
dashdir = dodge.unit();

View file

@ -678,7 +678,7 @@ Class SaltBeam : Actor
}
if ( t.Results.HitType != TRACE_HitNone )
{
if ( args[1] ) BusterWall.Bust(t.Results,85+Accuracy*10,target,x);
if ( args[1] ) BusterWall.Bust(t.Results,85+Accuracy*10,target,x,t.Results.HitPos.z);
if ( t.Results.HitType == TRACE_HitActor )
{
SWWMHandler.DoKnockback(t.Results.HitActor,x,25000);
@ -1515,7 +1515,7 @@ Class TheBall : Actor
if ( special1 == 1 )
{
int dmg = int(oldvel.length()*4.2+heat*80);
BusterWall.Bust(faketracer.Results,dmg,target,oldvel.unit());
BusterWall.Bust(faketracer.Results,dmg,target,oldvel.unit(),pos.z+Height/2.);
}
// undo the bounce, we need to hook in our own
vel = oldvel;

View file

@ -87,8 +87,8 @@ Class SilverImpact : Actor
return;
}
A_StartSound("silverbullet/hit",CHAN_VOICE,CHANF_DEFAULT,1.,.7);
A_SprayDecal("BigBulletChip",-20);
A_SprayDecal("HugeWallCrack",-20);
A_SprayDecal("BigBulletChip",-64);
A_SprayDecal("HugeWallCrack",-64);
int numpt = Random[Silverbullet](15,25);
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
for ( int i=0; i<numpt; i++ )
@ -125,6 +125,163 @@ Class SilverImpact : Actor
}
}
Class WallPenetrate
{
Vector3 hitpos, hitdir, hitnormal;
}
Class AuxiliarySilverBulletTracer : LineTracer
{
override ETraceStatus TraceCallback()
{
if ( Results.HitType == TRACE_HitActor ) return TRACE_Skip;
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
return TRACE_Stop;
return TRACE_Skip;
}
return TRACE_Stop;
}
}
Class SilverBulletTracer : SpreadSlugTracer
{
Array<WallPenetrate> WallPenetrateList;
override ETraceStatus TraceCallback()
{
// liquid splashes
if ( Results.CrossedWater )
{
let hl = new("WaterHit");
hl.sect = Results.CrossedWater;
hl.hitpos = Results.CrossedWaterPos;
WaterHitList.Push(hl);
}
else if ( Results.Crossed3DWater )
{
let hl = new("WaterHit");
hl.sect = Results.Crossed3DWater;
hl.hitpos = Results.Crossed3DWaterPos;
WaterHitList.Push(hl);
}
if ( Results.HitType == TRACE_HitActor )
{
if ( Results.HitActor == ignoreme ) return TRACE_Skip;
if ( Results.HitActor.bSHOOTABLE )
{
let ent = new("HitListEntry");
ent.hitactor = Results.HitActor;
ent.hitlocation = Results.HitPos;
ent.x = Results.HitVector;
ent.hitdamage = min(Results.HitActor.health+int(Results.HitActor.GetSpawnHealth()*gameinfo.gibfactor),int(penetration));
int killdamage = min(Results.HitActor.health,int(penetration));
hitlist.Push(ent);
penetration = max(0,penetration-killdamage);
if ( penetration <= 0 ) return TRACE_Stop;
return TRACE_Skip;
}
return TRACE_Skip;
}
else if ( (Results.HitType == TRACE_HitWall) && (Results.Tier == TIER_Middle) )
{
if ( !Results.HitLine.sidedef[1] || (Results.HitLine.Flags&(Line.ML_BlockHitscan|Line.ML_BlockEverything)) )
{
for ( int i=1; i<int(penetration/16); i++ )
{
Vector3 ofs = level.Vec3Offset(Results.HitPos,Results.HitVector*i);
if ( level.IsPointInLevel(ofs) )
{
penetration = max(0,penetration-i*8);
let wp = new("WallPenetrate");
wp.hitpos = Results.HitPos;
wp.hitdir = Results.HitVector;
wp.hitnormal = (-Results.HitLine.delta.y,Results.HitLine.delta.x,0).unit();
if ( !Results.Side ) wp.hitnormal *= -1;
WallPenetrateList.Push(wp);
if ( Results.HitLine.sidedef[1] )
ShootThroughList.Push(Results.HitLine);
// trace backwards to find exit surface
let at = new("AuxiliarySilverBulletTracer");
at.Trace(ofs,level.PointInSector(ofs.xy),-Results.HitVector,2.,TRACE_HitSky);
if ( at.Results.HitType != TRACE_HitNone )
{
let wp2 = new("WallPenetrate");
wp2.hitpos = at.Results.HitPos;
wp2.hitdir = at.Results.HitVector;
if ( at.Results.HitType == TRACE_HitWall )
{
wp2.hitnormal = (-at.Results.HitLine.delta.y,at.Results.HitLine.delta.x,0).unit();
if ( !at.Results.Side ) wp2.hitnormal *= -1;
if ( at.Results.HitLine.sidedef[1] )
ShootThroughList.Push(at.Results.HitLine);
}
else if ( at.Results.HitType == TRACE_HitCeiling )
wp2.hitnormal = at.Results.HitSector.ceilingplane.Normal;
else if ( at.Results.HitType == TRACE_HitFloor )
wp2.hitnormal = at.Results.HitSector.floorplane.Normal;
WallPenetrateList.Push(wp2);
}
return TRACE_Skip;
}
}
return TRACE_Stop;
}
ShootThroughList.Push(Results.HitLine);
return TRACE_Skip;
}
else if ( Results.HitType != TRACE_HitNone )
{
for ( int i=1; i<int(penetration/16); i++ )
{
Vector3 ofs = level.Vec3Offset(Results.HitPos,Results.HitVector*i);
if ( level.IsPointInLevel(ofs) )
{
penetration = max(0,penetration-i*8);
let wp = new("WallPenetrate");
wp.hitpos = Results.HitPos;
wp.hitdir = Results.HitVector;
if ( Results.HitType == TRACE_HitWall )
{
wp.hitnormal = (-Results.HitLine.delta.y,Results.HitLine.delta.x,0).unit();
if ( !Results.Side ) wp.hitnormal *= -1;
ShootThroughList.Push(Results.HitLine);
}
else if ( Results.HitType == TRACE_HitCeiling )
wp.hitnormal = Results.HitSector.ceilingplane.Normal;
else if ( Results.HitType == TRACE_HitFloor )
wp.hitnormal = Results.HitSector.floorplane.Normal;
WallPenetrateList.Push(wp);
// trace backwards to find exit surface
let at = new("AuxiliarySilverBulletTracer");
at.Trace(ofs,level.PointInSector(ofs.xy),-Results.HitVector,2.,TRACE_HitSky);
if ( at.Results.HitType != TRACE_HitNone )
{
let wp2 = new("WallPenetrate");
wp2.hitpos = at.Results.HitPos;
wp2.hitdir = at.Results.HitVector;
if ( at.Results.HitType == TRACE_HitWall )
{
wp2.hitnormal = (-at.Results.HitLine.delta.y,at.Results.HitLine.delta.x,0).unit();
if ( !at.Results.Side ) wp2.hitnormal *= -1;
if ( at.Results.HitLine.sidedef[1] )
ShootThroughList.Push(at.Results.HitLine);
}
else if ( at.Results.HitType == TRACE_HitCeiling )
wp2.hitnormal = at.Results.HitSector.ceilingplane.Normal;
else if ( at.Results.HitType == TRACE_HitFloor )
wp2.hitnormal = at.Results.HitSector.floorplane.Normal;
WallPenetrateList.Push(wp2);
}
return TRACE_Skip;
}
}
}
return TRACE_Stop;
}
}
Class SilverBullet : SWWMWeapon
{
bool chambered, fired;
@ -226,7 +383,7 @@ Class SilverBullet : SWWMWeapon
else Shader.SetEnabled(players[consoleplayer],"SilverScope",false);
}
action void ProcessTraceHit( SpreadgunTracer t, Vector3 origin, Vector3 dir )
action void ProcessTraceHit( SilverBulletTracer t, Vector3 origin, Vector3 dir )
{
for ( int i=0; i<t.ShootThroughList.Size(); i++ )
{
@ -269,6 +426,15 @@ Class SilverBullet : SWWMWeapon
p.target = self;
}
}
for ( int i=0; i<t.WallPenetrateList.Size(); i++ )
{
Vector3 hitpos = t.WallPenetrateList[i].hitpos;
Vector3 hitnormal = t.WallPenetrateList[i].hitnormal;
let p = Spawn("SilverImpact",hitpos+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.target = self;
}
if ( (t.Results.HitType != TRACE_HitNone) && (t.Results.HitType != TRACE_HasHitSky) && (t.Results.HitType != TRACE_HitActor) )
{
Vector3 hitnormal = -t.Results.HitVector;
@ -316,12 +482,13 @@ Class SilverBullet : SWWMWeapon
Vector3 origin = level.Vec3Offset(Vec2OffsetZ(0,0,player.viewz),10*x+1*y-1*z);
Vector3 x2, y2, z2;
[x2, y2, z2] = swwm_CoordUtil.GetAxes(BulletSlope(),angle,roll);
SpreadSlugTracer sst = new("SpreadSlugTracer");
SilverBulletTracer sst = new("SilverBulletTracer");
sst.ignoreme = self;
sst.penetration = 2000.;
sst.hitlist.Clear();
sst.shootthroughlist.Clear();
sst.waterhitlist.Clear();
sst.wallpenetratelist.Clear();
sst.Trace(origin,level.PointInSector(origin.xy),x2,20000.,TRACE_HitSky);
ProcessTraceHit(sst,origin,x2);
for ( int i=0; i<10; i++ )