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:
parent
2184079c07
commit
b5947709f7
11 changed files with 417 additions and 74 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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)";
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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++ )
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue