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

@ -38,7 +38,7 @@ server int swwm_revivecooldown = 30; // cooldown after using a revive, in secon
user int swwm_intertype = 0; // 0 - standard bg
// 1 - fanart
// 2 - 4komas
server bool swwm_balluse = false; // lead ball can "press" switches remotely
server bool swwm_balluse = true; // lead ball can "press" switches remotely
user bool swwm_bosshealthbars = true; // display large healthbars for vanilla boss encounters
server bool swwm_upgradebosses = true; // vanilla bosses will be "upgraded" to be less insta-die prone
server bool swwm_extraalert = false; // enables A_AlertMonsters calls for certain things which may be cpu-heavy
@ -57,3 +57,4 @@ user noarchive int swwm_cbtmeme = 0; // easter egg, hidden cvar
user noarchive bool swwm_cbttime = false; // debug: times how long a reload takes
server bool swwm_crouchjump = false; // allows crouch-jumping (which looks weird af but some maps may need it)
user noarchive int swwm_cbtlast = 0; // last selected ammo for the wallbuster
server bool swwm_cbtall = true; // wallbuster breaks any wall, not just movable ones

View file

@ -723,7 +723,7 @@ SWWM_INTERTIP26 = "You can press switches and open doors by punching them. This
SWWM_INTERTIP27 = "The fire from the Spreadgun's Napalm rounds will spread around to nearby enemies (or yourself). Remember, if you're on fire, stop, drop and roll... or just spin around, it works the same.";
SWWM_INTERTIP28 = "Incendiary ammunition is ineffective underwater, for obvious reasons.";
SWWM_INTERTIP29 = "My girlfriends are really cute. Sorry, I had to say it.";
SWWM_INTERTIP30 = "The Silver Bullet fires with such a force that its bullets leave shockwaves as they rip through the air (and enemies), dealing additional damage.";
SWWM_INTERTIP30 = "The Silver Bullet fires with such a force that its bullets leave shockwaves as they rip through the air (and enemies), dealing additional damage. It can also pierce through some walls.";
// intermission fanart
SWWM_FANART = "Art: ";
SWWM_NFANART = "2";

View file

@ -99,6 +99,7 @@ SWWM_BLOOD = "Enable Custom Blood";
SWWM_FUZZ = "Enable Animated Menu BG";
SWWM_BUSTERPAUSE = "Pause on Wallbuster Reload";
SWWM_CROUCHJUMP = "Allow Crouch-Jump";
SWWM_CBTALL = "Unlimited Wallbuster Destruction";
TOOLTIP_SWWM_VOICETYPE = "Sets the voice pack for the player.";
TOOLTIP_SWWM_MUTEVOICE = "Control what gets muted, if you'd rather have a more silent protagonist.";
TOOLTIP_SWWM_FLASHSTRENGTH = "Screen flashes usually happen when firing some weapons, you can lower this if these effects are harmful for you.";
@ -144,6 +145,7 @@ TOOLTIP_SWWM_BLOOD = "Please note that these effects are kinda cheap and perform
TOOLTIP_SWWM_FUZZ = "Toggling this off is recommended if you're recording/streaming as this effect can harm video quality.";
TOOLTIP_SWWM_CBTPAUSE = "The game will pause while the Wallbuster reload menu is open (only in singleplayer).";
TOOLTIP_SWWM_CROUCHJUMP = "Allow the player to crouch-jump (which may be needed on some maps). Note that there are no animations for this and it'll look weird.";
TOOLTIP_SWWM_CBTALL = "Allows the Wallbuster to break pretty much anything, not just doors and platforms. Note that this can break some maps, which is why this option is here.";
// knowledge base
SWWM_COMINGSOON = "(coming soon)";
SWWM_MISSTAB = "Mission";

View file

@ -707,7 +707,7 @@ SWWM_INTERTIP26 = "Puedes activar objetos y abrir puertas a puñetazos. Esto tam
SWWM_INTERTIP27 = "Las llamas de los cartuchos de Napalm del Trabuco se extenderán a otros enemigos cercanos (o a ti). Recuerda, si estás ardiendo, tírate al suelo y rueda... o ponte a dar vueltas, funciona igual.";
SWWM_INTERTIP28 = "La munición incendiaria es inefectiva bajo el agua, por razones obvias.";
SWWM_INTERTIP29 = "Mis novias son muy cuquis. Lo siento, es que tenía que decirlo.";
SWWM_INTERTIP30 = "El Silver Bullet dispara con una fuerza tal que sus balas liberan ondas de choque al atravesar el aire (y los enemigos), causando daño adicional.";
SWWM_INTERTIP30 = "El Silver Bullet dispara con una fuerza tal que sus balas liberan ondas de choque al atravesar el aire (y los enemigos), causando daño adicional. También puede atravesar algunas paredes.";
// intermission fanart
SWWM_FANART = "Arte: ";
// intermission 4komas (TBD)

View file

@ -96,6 +96,7 @@ SWWM_BLOOD = "Habilitar Sangre Personalizada";
SWWM_FUZZ = "Habilitar Fondo Animado de Menú";
SWWM_BUSTERPAUSE = "Pausar en Regarga de Wallbuster";
SWWM_CROUCHJUMP = "Permitir Salto Agachado";
SWWM_CBTALL = "Destrucción Ilimitada de Wallbuster";
TOOLTIP_SWWM_VOICETYPE = "Selecciona el pack de voz para el jugador.";
TOOLTIP_SWWM_MUTEVOICE = "Controla lo que se mutea, si prefieres tener un protagonista más silencioso.";
TOOLTIP_SWWM_FLASHSTRENGTH = "Los destellos en pantalla suelen ocurrir al disparar algunas armas, puedes reducirlo si este tipo de effectos te causan malestar.";
@ -141,6 +142,7 @@ TOOLTIP_SWWM_BLOOD = "Ten en cuenta que estos efectos son un poco cutres y bajan
TOOLTIP_SWWM_FUZZ = "Desactivar ésto es recomendado si estás grabando o haciendo streaming, ya que este effecto puede dañar la calidad del vídeo.";
TOOLTIP_SWWM_CBTPAUSE = "El juego será pausado mientras el menú de recarga de Wallbuster está abierto (sólo en modo de un jugador).";
TOOLTIP_SWWM_CROUCHJUMP = "Permite al jugador agacharse saltando (lo cual puede ser necesario en algunos mapas). Ten en cuenta que no hay animaciones para esto, así que se verá raro.";
TOOLTIP_SWWM_CBTALL = "Permite a la Wallbuster romper casi cualquier cosa, no solo puertas y plataformas. Ten en cuenta que esto puede romper algunos mapas, razón de que haya esta opción.";
// knowledge base
SWWM_COMINGSOON = "(próximamente)";
SWWM_MISSTAB = "Misión";

View file

@ -1,2 +1,2 @@
[default]
SWWM_MODVER="\cxSWWM GZ\c- r364 (Sat 20 Jun 11:49:29 CEST 2020)";
SWWM_MODVER="\cxSWWM GZ\c- r365 (Sat 20 Jun 17:19:13 CEST 2020)";

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++ )