0.9.10b release (oh boy where do I start):

- New fun options implemented (omnibusting, unlimited fuel, party time)
 - Biospark Carbine gets a requested nerf
 - Candygun combo fire has been buffed (watch out for that splash damage)
 - All powerup effects are additive (stacc 'em)
 - Automap hud respects gzdoom's cvars for toggling certain elements
 - Automap hud shows stats and times in gold when 100% / under par
 - Weapons and ammo are no longer affected by skill amount modifiers, for balance (and to avoid any weird glitches)
 - Sorting improvements for menu (weapons by slot, ammo by weapons, other items by value, etc.)
 - Grilled Cheese Sandwich now saves you from lethal falls properly
 - Blown kisses instakill nazis
 - Added non-violent Keen replacement (based on "Less mean-spirited Keen replacement" by SiFi270)
 - Added gib deaths for hell nobles, pinkies, cacos, revs and viles (sprites by Amuscaria and Ryan Cordell)
 - Blown kisses can activate use switches
 - Gestures can be chained by pressing a gesture button while another is playing
 - Fixed Grilled Cheese Sandwich not avoiding telefrags properly (now also works with voodoo dolls)
 - More precise weapon kill tracking (fixes some ragekit quirks)
 - Merge both DLC weaponsets, removing redundant weapons (see FuturePlans.md)
 - Discarded some collectables for the next updates, to save time
 - Preparation work for collectables update, including some (partial) lore files
 - Remove ammo fabricators from store, makes no sense to have them when you can just buy ammo directly
 - Cosmetic Boss Brain sprite replacements, just for fun
 - 10 more intermission tips, because yes
 - Added option to reduce distance at which enemy healthbars are picked
 - Various minor bugfixes and adjustments (and also some tiny typo fixes)
 - Ragekit now heals over time and with each hit (so it's more rewarding to go wild)
 - PNG optimization pass (again lol)
 - Fix crouched gestures having no facial animation
This commit is contained in:
Mari the Deer 2020-10-05 22:23:55 +02:00
commit aabc9de051
471 changed files with 3003 additions and 749 deletions

View file

@ -1,5 +1,5 @@
// Unissix Crafts Blackfire Igniter (from UnSX 4, cut from initial SWWM GZ release)
// Slot 6, spawns shared with Hellblazer
// Slot 7, spawns shared with Biospark Carbine
Class BlackfireIgniter : SWWMWeapon
{

View file

@ -12,6 +12,18 @@ Class SMW05BigAmmo : SMW05Ammo
{
}
Class SheenAmmo : Ammo
{
}
Class SheenSmallAmmo: SheenAmmo
{
}
Class SheenBigAmmo : SheenAmmo
{
}
Class QuadravolAmmo : Ammo
{
}
@ -24,22 +36,18 @@ Class DarkCanister : Ammo
{
}
Class LargeDarkCanister : DarkCanister
{
}
Class KinylumUnit : Ammo
{
}
Class NokronUnit : Ammo
{
}
Class EMPCore : Ammo
{
}
Class MadCore : Ammo
Class EMPCoreBundleSpawn : Actor
{
}
Class RayBolt : MagAmmo
{
}
Class RayAmmo : Ammo
{
}

View file

@ -1,5 +1,5 @@
// Kmonn Shuna Quadravol (from UnSX series, not a Quadshot reskin anymore)
// Slot 5, spawns shared with Eviscerator
// Slot 6, spawns shared with Hellblazer
Class Quadravol : SWWMWeapon
{

View file

@ -1,6 +0,0 @@
// PROWEL and TADEL artifacts, by Nukritas 2xx of the University of Nos-Kora (from UnSX series, based on nonsensical gibberish)
// Slot 9, spawns shared with Candy Gun
Class PROWELfartsupintheTADEL : SWWMWeapon
{
}

View file

@ -1,6 +0,0 @@
// Tach-Engine Technologies Series x3 Sparkster Carbine (UnSX 2)
// Slot 7, spawns shared with Legacy Sparkster
Class ModernSparkster : SWWMWeapon
{
}

View file

@ -1,37 +0,0 @@
// All DLC weapon ammo pickups
Class SheenAmmo : Ammo
{
}
Class SheenBigAmmo : SheenAmmo
{
}
Class SkullAmmo : Ammo
{
}
Class SkullPack : SkullAmmo
{
}
Class CryoMag : Ammo
{
}
Class CryoBullet : MagAmmo
{
}
Class RayAmmo : Ammo
{
}
Class RayBundle : RayAmmo
{
}
Class AODAmmo : Ammo
{
}

View file

@ -1,6 +0,0 @@
// Nuutek Cryolance (from SWWM Platinum Expansion Pack 2)
// Slot 8, spawns shared with Silver Bullet JET
Class Cryolance : SWWMWeapon
{
}

View file

@ -1,6 +0,0 @@
// Dr. Locke's Spooky Scary Skeleton Shooter aka "Skull Launcher" (from unreleased "Weird Weapons" UT minimod)
// Slot 6, spawns shared with Hellblazer
Class SkullLauncher : SWWMWeapon
{
}

View file

@ -1,6 +0,0 @@
// Angels of Death (from SWWM Platinum Expansion Pack 2)
// Slot 0, spawns shared with Ynykron
Class AODWeapon : SWWMWeapon
{
}

View file

@ -160,6 +160,11 @@ Mixin Class SWWMAmmo
}
return Super.HandlePickup(item);
}
default
{
+INVENTORY.IGNORESKILL;
}
}
// Common code for individual bullets
@ -1248,7 +1253,7 @@ Class AmmoFabricator : Inventory abstract
int maxb = ib?ib.MaxAmount:GetDefaultByType(b).Amount;
double facta = cnta/double(maxa);
double factb = cntb/double(maxb);
return (facta > factb);
return (facta >= factb);
}
private int partition_fabammo( Array<Class<Ammo> > a, int l, int h )
@ -1293,7 +1298,7 @@ Class AmmoFabricator : Inventory abstract
if ( !(def.Stamina) || (def.Stamina > maxunitprice) ) continue;
available.Push(a);
}
// sort by "need weight" (prioritize ammo that the player lacks over ammo that te player has plenty of
// sort by "need weight" (prioritize ammo that the player lacks over ammo that the player has plenty of
qsort_fabammo(available,0,available.Size()-1);
// loop through until we fill the inventory or run out of budget
bool given = false;
@ -1381,7 +1386,6 @@ Class FabricatorTier1 : AmmoFabricator
AmmoFabricator.Budget 5000;
AmmoFabricator.PerType 2;
AmmoFabricator.MaxUnitPrice 2500;
Stamina 3000;
}
}
Class FabricatorTier2 : AmmoFabricator
@ -1397,7 +1401,6 @@ Class FabricatorTier2 : AmmoFabricator
AmmoFabricator.Budget 15000;
AmmoFabricator.PerType 4;
AmmoFabricator.MaxUnitPrice 10000;
Stamina 12000;
}
}
Class FabricatorTier3 : AmmoFabricator
@ -1413,7 +1416,6 @@ Class FabricatorTier3 : AmmoFabricator
AmmoFabricator.Budget 150000;
AmmoFabricator.PerType 8;
AmmoFabricator.MaxUnitPrice 80000;
Stamina 150000;
}
}
Class FabricatorTier4 : AmmoFabricator
@ -1428,7 +1430,6 @@ Class FabricatorTier4 : AmmoFabricator
AmmoFabricator.PerType int.max;
AmmoFabricator.MaxUnitPrice int.max;
-INVENTORY.AUTOACTIVATE;
Stamina 1920000;
}
}

View file

@ -331,6 +331,7 @@ Class HellblazerMissile : Actor
s.scale *= FRandom[Hellblazer](0.9,1.8);
}
Spawn("HellblazerRing",pos);
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,350,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
}
void A_SubExpl( double xscale = 1. )
@ -438,6 +439,7 @@ Class HellblazerCrackshot : HellblazerMissile
c.target = target;
c.ReactionTime += Random[Hellblazer](-10,10);
}
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,300,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
}
States
@ -496,6 +498,7 @@ Class HellblazerRavager : HellblazerMissile
s.scale *= FRandom[Hellblazer](0.9,1.8);
}
Spawn("HellblazerRing",pos);
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,400,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
}
void A_RavagerSub()
@ -574,6 +577,7 @@ Class HellblazerWarhead : HellblazerMissile
s.scale *= FRandom[Hellblazer](0.9,1.8);
}
Spawn("HellblazerRing2",pos);
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,2000,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
}
void A_WarheadSub()

View file

@ -584,6 +584,81 @@ Class PurpleBloodReference : Actor
}
}
// bare actor used for extra gib deaths
Class ExtraGibDeaths : Actor
{
StateLabel gibstate;
static void GibThis( Actor a, Statelabel st )
{
if ( !a ) return;
let g = ExtraGibDeaths(Spawn("ExtraGibDeaths"));
g.master = a;
g.gibstate = st;
}
void A_DoGib()
{
if ( !master ) return;
master.SetState(FindState(gibstate));
}
States
{
Spawn:
TNT1 A 1 NoDelay;
TNT1 A 1 A_DoGib();
Stop;
DemonXDeath:
SARX A 5;
SARX B 5 A_XScream();
SARX C 5 A_NoBlocking();
SARX DEF 5;
SARX G -1;
Stop;
KnightXDeath:
BO2X A 5;
BO2X B 5 A_XScream();
BO2X C 5;
BO2X D 5 A_NoBlocking();
BO2X EFGH 5;
BO2X I -1;
Stop;
BaronXDeath:
BOSX A 5;
BOSX B 5 A_XScream();
BOSX C 5;
BOSX D 5 A_NoBlocking();
BOSX EFGH 5;
BOSX I -1 A_BossDeath();
Stop;
CacoXDeath:
CACX A 5;
CACX B 5 A_XScream();
CACX C 5 A_NoBlocking();
CACX D 4;
CACX E 3;
CACX F 4;
CACX G 5;
CACX H -1;
Stop;
BonerXDeath:
REVX A 3;
REVX B 4 A_XScream();
REVX C 5 A_NoBlocking();
REVX DE 5;
REVX F -1;
Stop;
VileXDeath:
VILX A 5;
VILX B 5 A_XScream();
VILX C 5 A_NoBlocking();
VILX DEF 5;
VILX G -1;
Stop;
}
}
// corpse thump handler
Class CorpseFallTracker : Thinker
{
@ -749,6 +824,28 @@ Class SWWMGoreHandler : EventHandler
e.Thing.CopyBloodColor(pb);
pb.Destroy();
}
else if ( e.Thing.GetClass() == "LostSoul" )
e.Thing.bNOBLOOD = true;
}
override void WorldThingDied( WorldEvent e )
{
// force insert gib animations on some vanilla Doom monsters
int gibhealth = e.Thing.GetGibHealth();
bool gotgibbed = (!e.Thing.bDONTGIB && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH));
if ( !gotgibbed ) return;
if ( (e.Thing.GetClass() == "Demon") || (e.Thing.GetClass() == "Spectre") )
ExtraGibDeaths.GibThis(e.Thing,"DemonXDeath");
else if ( e.Thing.GetClass() == "HellKnight" )
ExtraGibDeaths.GibThis(e.Thing,"KnightXDeath");
else if ( e.Thing.GetClass() == "BaronOfHell" )
ExtraGibDeaths.GibThis(e.Thing,"BaronXDeath");
else if ( e.Thing.GetClass() == "Cacodemon" )
ExtraGibDeaths.GibThis(e.Thing,"CacoXDeath");
else if ( e.Thing.GetClass() == "Revenant" )
ExtraGibDeaths.GibThis(e.Thing,"BonerXDeath");
else if ( e.Thing.GetClass() == "Archvile" )
ExtraGibDeaths.GibThis(e.Thing,"VileXDeath");
}
override void WorldThingDamaged( WorldEvent e )
@ -756,6 +853,14 @@ Class SWWMGoreHandler : EventHandler
if ( e.Thing.Health > 0 ) return;
// no gib if it was erased
if ( e.DamageType == 'Ynykron' ) return;
int gibhealth = e.Thing.GetGibHealth();
bool gotgibbed = (!e.Thing.bDONTGIB && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH));
bool forcegibbed = false;
// force gib availability for some vanilla Doom monsters
if ( gotgibbed && ((e.Thing.GetClass() == "Demon") || (e.Thing.GetClass() == "Spectre") || (e.Thing.GetClass() == "HellKnight") || (e.Thing.GetClass() == "BaronOfHell") || (e.Thing.GetClass() == "Cacodemon") || (e.Thing.GetClass() == "Revenant") || (e.Thing.GetClass() == "Archvile")) )
forcegibbed = true;
if ( !e.Thing.FindState("XDeath",true) && !e.Thing.FindState("Death.Extreme",true) && !forcegibbed )
gotgibbed = false;
// only do special handling if they use our blood
if ( (e.Thing.GetBloodType(0) != "mkBlood") || e.Thing.bNOBLOOD )
return;
@ -782,8 +887,7 @@ Class SWWMGoreHandler : EventHandler
return;
}
// override gibbing
int gibhealth = e.Thing.GetGibHealth();
if ( !e.Thing.bDONTGIB && (e.Thing.FindState("XDeath",true) || e.Thing.FindState("Death.Extreme",true)) && ((e.Inflictor && e.Inflictor.bEXTREMEDEATH) || (e.DamageSource && e.DamageSource.bEXTREMEDEATH) || (e.DamageType == 'Extreme') || (e.Thing.Health < gibhealth)) && (!e.Inflictor || !e.Inflictor.bNOEXTREMEDEATH) && (!e.DamageSource || !e.DamageSource.bNOEXTREMEDEATH) )
if ( gotgibbed )
{
[b,a] = e.Thing.A_SpawnItemEx("mkGibber",flags:SXF_USEBLOODCOLOR);
if ( !b ) return;

View file

@ -429,6 +429,7 @@ Class BustedQuake : Actor
if ( special1 < 3 ) A_StartSound("wallbuster/smallbust",CHAN_VOICE,CHANF_OVERLAP,min(1.,special1*.32),1./max(1.,special1*.35),1.-special1*.05);
else A_StartSound("wallbuster/bigbust",CHAN_VOICE,CHANF_OVERLAP,min(1.,special1*.16),1./max(1.,special1*.35),1.-special1*.03);
A_QuakeEx(special1,special1,special1,20+special1*5,0,300+special1*90,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:special1*.1);
A_AlertMonsters(2500);
}
override void Tick()
{
@ -453,7 +454,7 @@ Class BusterWall : Thinker
int hitplane;
bool busted;
Vector3 bustdir;
int busttics;
int busttics, delay;
double cutheight;
// cached
Vector3 boundsmin, boundsmax, step;
@ -472,7 +473,12 @@ Class BusterWall : Thinker
return;
}
// fade out damage
accdamage = int(accdamage*.9-10);
if ( delay > 0 )
{
delay--;
return;
}
accdamage = int(accdamage*.9-5);
if ( accdamage <= 0 )
{
Destroy();
@ -522,13 +528,17 @@ Class BusterWall : Thinker
private static bool IsIOSWall( Line l )
{
TextureID facetex[5];
TextureID facetex[9];
facetex[0] = TexMan.CheckForTexture("ZZZFACE1",TexMan.Type_Wall);
facetex[1] = TexMan.CheckForTexture("ZZZFACE2",TexMan.Type_Wall);
facetex[2] = TexMan.CheckForTexture("ZZZFACE3",TexMan.Type_Wall);
facetex[3] = TexMan.CheckForTexture("ZZZFACE4",TexMan.Type_Wall);
facetex[4] = TexMan.CheckForTexture("ZZZFACE5",TexMan.Type_Wall);
for ( int i=0; i<5; i++ )
facetex[5] = TexMan.CheckForTexture("DBRAIN1",TexMan.Type_Wall);
facetex[6] = TexMan.CheckForTexture("DBRAIN2",TexMan.Type_Wall);
facetex[7] = TexMan.CheckForTexture("DBRAIN3",TexMan.Type_Wall);
facetex[8] = TexMan.CheckForTexture("DBRAIN4",TexMan.Type_Wall);
for ( int i=0; i<9; i++ )
{
for ( int j=0; j<3; j++ )
{
@ -539,6 +549,88 @@ Class BusterWall : Thinker
return false;
}
static bool ProjectileBust( Actor a, int accdamage, Vector3 x )
{
LineTracer faketracer = new("LineTracer");
F3DFloor ff;
Vector3 HitNormal;
if ( a.BlockingFloor )
{
// find closest 3d floor for its normal
for ( int i=0; i<a.BlockingFloor.Get3DFloorCount(); i++ )
{
if ( !(a.BlockingFloor.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
if ( !(a.BlockingFloor.Get3DFloor(i).top.ZAtPoint(a.pos.xy) ~== a.floorz) ) continue;
ff = a.BlockingFloor.Get3DFloor(i);
break;
}
if ( ff )
{
faketracer.Results.ffloor = ff;
HitNormal = -ff.top.Normal;
}
else HitNormal = a.BlockingFloor.floorplane.Normal;
faketracer.Results.HitType = TRACE_HitFloor;
faketracer.Results.HitSector = a.BlockingFloor;
}
else if ( a.BlockingCeiling )
{
// find closest 3d floor for its normal
for ( int i=0; i<a.BlockingCeiling.Get3DFloorCount(); i++ )
{
if ( !(a.BlockingCeiling.Get3DFloor(i).flags&F3DFloor.FF_SOLID) ) continue;
if ( !(a.BlockingCeiling.Get3DFloor(i).bottom.ZAtPoint(a.pos.xy) ~== a.ceilingz) ) continue;
ff = a.BlockingCeiling.Get3DFloor(i);
break;
}
if ( ff )
{
faketracer.Results.ffloor = ff;
HitNormal = -ff.bottom.Normal;
}
else HitNormal = a.BlockingCeiling.ceilingplane.Normal;
faketracer.Results.HitType = TRACE_HitCeiling;
faketracer.Results.HitSector = a.BlockingCeiling;
}
else if ( a.BlockingLine )
{
HitNormal = (-a.BlockingLine.delta.y,a.BlockingLine.delta.x,0).unit();
int wside = SWWMUtility.PointOnLineSide(a.pos.xy,a.BlockingLine);
if ( !wside ) HitNormal *= -1;
faketracer.Results.HitType = TRACE_HitWall;
faketracer.Results.HitLine = a.BlockingLine;
faketracer.Results.Side = wside;
faketracer.Results.Tier = TIER_Middle;
// guess the tier hit
if ( a.BlockingLine.sidedef[1] )
{
double ceil = a.BlockingLine.sidedef[!wside].sector.ceilingplane.ZAtPoint(a.pos.xy);
double flor = a.BlockingLine.sidedef[!wside].sector.floorplane.ZAtPoint(a.pos.xy);
if ( a.pos.z >= ceil ) faketracer.Results.Tier = TIER_Upper;
else if ( (a.pos.z+a.Height) <= flor ) faketracer.Results.Tier = TIER_Lower;
}
}
else if ( a.BlockingMobj )
{
Vector3 diff = level.Vec3Diff(a.BlockingMobj.Vec3Offset(0,0,a.BlockingMobj.Height/2),a.pos);
HitNormal = diff.unit();
faketracer.Results.HitType = TRACE_HitActor;
}
return Bust(faketracer.Results,accdamage,a.target,x,a.pos.z+a.Height/2.);
}
static bool BustLinetrace( FLineTraceData d, int accdamage, Actor instigator, Vector3 x, double hitz )
{
LineTracer faketracer = new("LineTracer");
faketracer.Results.HitType = d.HitType;
faketracer.Results.HitSector = d.HitSector;
faketracer.Results.HitLine = d.HitLine;
faketracer.Results.ffloor = d.Hit3DFloor;
faketracer.Results.Side = d.LineSide;
faketracer.Results.Tier = (d.LinePart==Side.Top)?TIER_UPPER:(d.LinePart==Side.Bottom)?TIER_LOWER:TIER_Middle;
return Bust(faketracer.Results,accdamage,instigator,x,hitz);
}
static bool Bust( TraceResults d, int accdamage, Actor instigator, Vector3 x, double hitz )
{
// we can't blow up 3D floors (yet)
@ -586,6 +678,7 @@ Class BusterWall : Thinker
bust.bustdir = x;
mnew = true;
}
bust.delay = max(bust.delay,5+min(20,accdamage>>4));
bust.accdamage += accdamage;
bust.acchits.Push(accdamage);
bust.bustdir = (bust.bustdir+x)*.5;
@ -614,7 +707,7 @@ Class BusterWall : Thinker
// skip if already busted
if ( bust.busted ) return true;
// not enough total damage
if ( bust.accdamage < 1000 ) return false;
if ( bust.accdamage < 100 ) return false;
// estimate sector volume
Vector2 a = (32767,32767), b = (-32768,-32768);
for ( int i=0; i<hs.lines.Size(); i++ )
@ -753,9 +846,9 @@ Class BusterWall : Thinker
// damnums
Vector3 bcenter = (bust.boundsmin+bust.boundsmax)*.5;
if ( CVar.GetCVar('swwm_accdamage',players[consoleplayer]).GetBool() )
SWWMScoreObj.Spawn(bust.accdamage,level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),Font.CR_RED);
SWWMScoreObj.Spawn(-bust.accdamage,level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),Font.CR_RED);
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))),Font.CR_RED);
SWWMScoreObj.Spawn(-bust.acchits[i],level.Vec3Offset(bcenter,(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8))),Font.CR_RED);
return true;
}
}
@ -949,12 +1042,15 @@ Class Wallbuster : SWWMWeapon
A_OverlayFlags(PSP_FLASH-index,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
A_OverlayRenderStyle(PSP_FLASH-index,STYLE_Add);
}
action void ProcessTraceHit( SpreadgunTracer t, Vector3 origin, Vector3 dir, int dmg, double mm, Class<Actor> impact = "SpreadImpact", int bc = 1, bool large = false )
action void ProcessTraceHit( SpreadgunTracer t, Vector3 origin, Vector3 dir, int dmg, double mm, Class<Actor> impact = "SpreadImpact", int bc = 1, bool large = false, bool bust = false )
{
// Wall busting
int bustdmg = dmg;
if ( t is 'SpreadSlugTracer' ) bustdmg = int(SpreadSlugTracer(t).penetration);
BusterWall.Bust(t.Results,bustdmg,self,t.Results.HitVector,t.Results.HitPos.z);
if ( bust )
{
int bustdmg = dmg;
if ( t is 'SpreadSlugTracer' ) bustdmg = int(SpreadSlugTracer(t).penetration);
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);
@ -1164,7 +1260,7 @@ Class Wallbuster : SWWMWeapon
sst.shootthroughlist.Clear();
sst.waterhitlist.Clear();
sst.Trace(origin,level.PointInSector(origin.xy),dir,8000.,TRACE_HitSky);
ProcessTraceHit(sst,origin,dir,0,12000,"SlugImpact",1+howmany/8,true);
ProcessTraceHit(sst,origin,dir,0,12000,"SlugImpact",1+howmany/8,true,(howmany>1));
for ( int i=0; i<(3-howmany/10); i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
@ -1202,7 +1298,7 @@ Class Wallbuster : SWWMWeapon
b.pitch = asin(-x2.z);
b.Stamina += howmany*16;
b.Accuracy += 2+howmany/5;
b.Args[1] = 1; // came from wallbuster
b.Args[1] = (howmany>1)?2:1; // came from wallbuster
}
for ( int i=0; i<(9-howmany/3); i++ )
{
@ -1240,7 +1336,7 @@ Class Wallbuster : SWWMWeapon
p.angle = atan2(dir.y,dir.x);
p.pitch = asin(-dir.z);
p.vel = dir*p.speed*1.25;
p.special1 = 1; // came from wallbuster
p.special1 = (howmany>1)?2:1; // came from wallbuster
for ( int i=0; i<(4-howmany/8); i++ )
{
let s = Spawn("SWWMViewSmoke",origin);
@ -1283,7 +1379,7 @@ Class Wallbuster : SWWMWeapon
st.shootthroughlist.Clear();
st.waterhitlist.Clear();
st.Trace(origin,level.PointInSector(origin.xy),dir,8000.,TRACE_HitSky);
ProcessTraceHit(st,origin,dir,individualdmg,7000,bc:5+howmany/6);
ProcessTraceHit(st,origin,dir,individualdmg,7000,bc:5+howmany/6,false,(howmany>1));
}
for ( int i=0; i<(9-howmany/3); i++ )
{

View file

@ -18,10 +18,117 @@ enum ESWWMGZChannels
// Future planning, will be filled out with AI stuff and whatnot someday
Class SWWMMonster : Actor
{
// integrated fun tags
virtual clearscope String GetFunTag( String defstr = "" )
{
return GetTag(defstr);
}
// the function that should be overriden in subclasses
virtual int HandleLocationalDamage( Actor inflictor, Actor source, int damage, Name mod, Vector3 HitLocation, int flags = 0, double angle = 0 )
{
return damage;
}
// locational damage support, akin to UE1, but hitlocation will be treated as a relative offset, to make things easier
// this one should be called directly by everything in this mod, when possible
int LocationalDamageMobj( Actor inflictor, Actor source, int damage, Name mod, Vector3 HitLocation, int flags = 0, double angle = 0 )
{
damage = HandleLocationalDamage(inflictor,source,damage,mod,HitLocation,flags,angle);
return Super.DamageMobj(inflictor,source,damage,mod,flags,angle);
}
// "estimated" locational damage for the vanilla DamageMobj
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
{
Vector3 guesspos = (0,0,Height/2.);
// use inflictor if available, as it may be a projectile or hitscan puff
// if damage comes from an item, use owner
// all of this could be done better (or implemented as an engine feature), but whatever
Actor whomst = inflictor?inflictor:source;
if ( whomst is 'Inventory' ) whomst = Inventory(whomst).Owner;
if ( whomst )
{
if ( whomst.bMISSILE || (flags&DMG_INFLICTOR_IS_PUFF) )
guesspos = level.Vec3Diff(pos,whomst.pos);
else guesspos = level.Vec3Diff(pos,whomst.Vec3Offset(0,0,whomst.Height/2));
guesspos.x = clamp(guesspos.x,-radius,radius);
guesspos.y = clamp(guesspos.y,-radius,radius);
guesspos.z = clamp(guesspos.z,0,height);
}
return LocationalDamageMobj(inflictor,source,damage,mod,guesspos,flags,angle);
}
}
// Less mean-spirited Keen
Class SWWMHangingKeen : Actor
{
action void A_DropKeen()
{
Spawn("SWWMDroppedKeen",Vec3Offset(0,0,8));
}
Default
{
Tag "$FN_KEEN";
Health 100;
Radius 10;
Height 54;
Mass int.max;
PainChance 256;
+SOLID;
+SPAWNCEILING;
+NOGRAVITY;
+SHOOTABLE;
+NOICEDEATH;
+DONTFALL;
+NOBLOOD;
+DONTTHRUST;
PainSound "keen/pain";
DeathSound "keen/death";
}
States
{
Spawn:
KEE2 A -1;
Stop;
Death:
KEE2 A 6;
KEE2 B 6 A_DropKeen();
KEE2 C 6 A_Scream();
KEE2 DE 6;
KEE2 F 30;
KEE2 F -1 A_KeenDie();
Stop;
Pain:
KEE2 G 4;
KEE2 G 8 A_Pain();
Goto Spawn;
}
}
Class SWWMDroppedKeen : Actor
{
Default
{
Radius 10;
Height 32;
Gravity .5;
+NOBLOCKMAP;
}
States
{
Spawn:
KEE3 A 1 A_JumpIf(pos.z<=floorz,1);
Wait;
KEE3 B 1 { vel.z = 4; }
KEE3 B 1 A_JumpIf(pos.z<=floorz,1);
Wait;
KEE3 B 1 { vel.z = 2; }
KEE3 B 1 A_JumpIf(pos.z<=floorz,1);
Wait;
KEE3 B 12;
TNT1 A 1 { Spawn("TeleportFog",pos,ALLOW_REPLACE); }
Stop;
}
}
// imitates UE1 light type LT_TexturePaletteOnce/LT_TexturePaletteLoop

View file

@ -228,6 +228,7 @@ Class EvisceratorChunk : Actor
Vector3 diff = level.Vec3Diff(BlockingMobj.Vec3Offset(0,0,BlockingMobj.Height/2),pos);
HitNormal = diff.unit();
}
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,GetMissileDamage(0,0),oldvel.unit());
// undo the bounce, we need to hook in our own
vel = oldvel;
// re-do the bounce with our formula
@ -241,6 +242,7 @@ Class EvisceratorChunk : Actor
let l = Spawn("ChunkImpact",pos);
l.angle = atan2(HitNormal.y,HitNormal.x);
l.pitch = asin(-HitNormal.z);
A_StartSound("eviscerator/hith",CHAN_WEAPON,CHANF_OVERLAP);
}
A_Gravity();
gravity = .35;
@ -263,14 +265,14 @@ Class EvisceratorChunk : Actor
if ( !target.bNOBLOOD && !target.bINVULNERABLE )
{
target.SpawnBlood(pos,AngleTo(target),damage);
A_StartSound("eviscerator/hitf",CHAN_WEAPON,CHANF_OVERLAP,.1);
A_StartSound("eviscerator/hitf",CHAN_WEAPON,CHANF_OVERLAP,.2);
}
else
{
let l = Spawn("ChunkImpact",pos);
l.angle = angle+180;
l.pitch = -pitch;
A_StartSound("eviscerator/hit",CHAN_WEAPON,CHANF_OVERLAP,.1);
A_StartSound("eviscerator/hith",CHAN_WEAPON,CHANF_OVERLAP,.2);
}
if ( swwm_extraalert || !Random[Eviscerator](0,3) ) A_AlertMonsters(900);
return -1;
@ -298,14 +300,14 @@ Class EvisceratorChunk : Actor
if ( !victim.bNOBLOOD && !victim.bINVULNERABLE )
{
victim.SpawnBlood(pos,AngleTo(victim),damage);
A_StartSound("eviscerator/hitf",CHAN_WEAPON,CHANF_OVERLAP,.1);
A_StartSound("eviscerator/hitf",CHAN_WEAPON,CHANF_OVERLAP,.2);
}
else
{
let l = Spawn("ChunkImpact",pos);
l.angle = angle+180;
l.pitch = -pitch;
A_StartSound("eviscerator/hit",CHAN_WEAPON,CHANF_OVERLAP,.1);
A_StartSound("eviscerator/hith",CHAN_WEAPON,CHANF_OVERLAP,.2);
}
}
vel *= .65; // reduce velocity as it rips
@ -458,6 +460,7 @@ Class EvisceratorProj : Actor
s.scale *= FRandom[Eviscerator](0.9,1.8);
}
Spawn("EvisceratorRing",pos);
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,150,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
}
action void A_SubExpl()
{

View file

@ -645,6 +645,7 @@ Class YnykronBeam : Actor
b.pitch = asin(-norm.z);
b.A_SprayDecal("YnykronBlast",-172);
if ( YnykronShot(master) ) YnykronShot(master).blastcount++;
if ( swwm_omnibust ) BusterWall.Bust(t.Results,int.max,target,t.Results.HitVector,t.Results.HitPos.z);
// find exit point
int maxdist = (25600-special1);
int i;
@ -893,6 +894,7 @@ Class DelayedWallBeam : Actor
}
else at.Results.HitLine.Activate(tracer,at.Results.Side,SPAC_Impact);
}
if ( swwm_omnibust ) BusterWall.Bust(at.Results,int.max,target,-at.Results.HitVector,at.Results.HitPos.z);
Destroy();
}
}
@ -1549,6 +1551,7 @@ Class YnykronLightningArc : Actor
s.target = target;
s.angle = atan2(normal.y,normal.x);
s.pitch = asin(-normal.z);
if ( swwm_omnibust ) BusterWall.Bust(t.Results,GetMissileDamage(0,0),target,t.Results.HitVector,t.Results.HitPos.z);
}
invoker.nextpos = t.Results.HitPos;
bAMBUSH = true;
@ -2278,15 +2281,7 @@ Class YnykronSingularity : Actor
{
// force use the sandwich, warp to safe spot
let gc = GrilledCheeseSandwich(a.FindInventory("GrilledCheeseSandwich"));
Spawn("SWWMItemFog",a.Vec3Offset(0,0,a.Height/2));
Vector3 safepos;
int safeangle;
if ( deathmatch ) [safepos, safeangle] = level.PickDeathmatchStart();
else [safepos, safeangle] = level.PickPlayerStart(a.PlayerNumber());
a.vel *= 0.;
a.SetOrigin(safepos,false);
a.angle = safeangle;
a.pitch = 0;
gc.SafeTeleport(true);
a.A_StartSound(gc.UseSound,CHAN_ITEMEXTRA);
gc.DoTheThing(true);
gc.Amount--;

View file

@ -31,7 +31,9 @@ Class AirBullet : FastProjectile
if ( target == lasthit ) return 0;
lasthit = target;
}
SWWMUtility.DoKnockback(target,vel.unit(),10000);
Vector3 dirto = level.Vec3Offset(pos,target.Vec3Offset(0,0,target.Height/2.));
dirto /= dirto.length();
SWWMUtility.DoKnockback(target,vel.unit()*.6+dirto*.4,60000);
return damage;
}
override void Effect()
@ -54,7 +56,7 @@ Class AirBullet : FastProjectile
s.alpha *= .2;
}
bAMBUSH = true;
SWWMUtility.DoExplosion(self,GetMissileDamage(0,0),0,50,ignoreme:target);
SWWMUtility.DoExplosion(self,GetMissileDamage(0,0),0,60,ignoreme:target);
bAMBUSH = false;
tcnt++;
if ( tcnt < 2 ) return;
@ -104,6 +106,7 @@ Class AirBullet : FastProjectile
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,100,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
}
States
{
@ -303,14 +306,14 @@ Class DeepImpact : SWWMWeapon
int wallhits = 0;
Vector3 hitnormal;
Vector3 wnorm = (0,0,0);
for ( double i=0; i<1.; i+=.05 )
for ( double i=0; i<.35; i+=.05 )
{
for ( int j=0; j<360; j+=(360/rings) )
{
dir = (x+y*cos(j)*i+z*sin(j)*i).unit();
t.hitlist.Clear();
t.hitdist.Clear();
t.Trace(origin,level.PointInSector(origin.xy),dir,150-i*50,0);
t.Trace(origin,level.PointInSector(origin.xy),dir,150-i*120,0);
for ( int i=0; i<t.hitlist.Size(); i++ )
{
int inl = -1;
@ -352,6 +355,7 @@ Class DeepImpact : SWWMWeapon
else hitnormal = t.Results.HitSector.ceilingplane.Normal;
wnorm += (hitnormal*.2-dir)/max(50.,t.Results.Distance);
}
if ( swwm_omnibust ) BusterWall.Bust(t.Results,int(5*(1.-clamp(t.Results.Distance/150.,0.,1.))),target,t.Results.HitVector,t.Results.HitPos.z);
}
rings += 5;
}
@ -365,10 +369,10 @@ Class DeepImpact : SWWMWeapon
{
Vector3 avgdir = list[i].avgdir/list[i].nhits;
double avgdist = list[i].avgdist/list[i].nhits;
double dmg = 4000.*(1.-clamp(avgdist/300.,0.,1.));
double dmg = 5000.*(1.-clamp(avgdist/200.,0.,1.));
if ( !list[i].a.bDONTTHRUST && (list[i].a.mass < LARGE_MASS) )
list[i].a.vel += ((avgdir+(0,0,.35))*dmg/max(50,list[i].a.mass));
list[i].a.DamageMobj(self,self,int(dmg/500.),'Push',DMG_THRUSTLESS);
list[i].a.vel += ((avgdir+(0,0,.2))*dmg/max(50,list[i].a.mass));
list[i].a.DamageMobj(invoker,self,int(dmg/350.),'Push',DMG_THRUSTLESS);
}
let ti = ThinkerIterator.Create("Actor");
Actor m;
@ -457,7 +461,6 @@ Class DeepImpact : SWWMWeapon
Inventory.Icon "graphics/HUD/Icons/W_DeepImpact.png";
Weapon.UpSound "deepimpact/select";
Weapon.SlotNumber 1;
Weapon.SlotPriority 1.;
Weapon.SelectionOrder 2000;
Stamina 6000;
DeepImpact.ClipCount 100;

View file

@ -1,8 +1,9 @@
// collectable items that may drop sometimes (future feature)
// collectable items that may drop sometimes
Class SWWMCollectable : Inventory abstract
{
int avail;
bool propagated;
Property Availability : avail;
@ -27,19 +28,47 @@ Class SWWMCollectable : Inventory abstract
+FLOATBOB;
FloatBobStrength 0.25;
}
override void PostBeginPlay()
{
Super.PostBeginPlay();
// delet ourselves if wrong iwad
if ( !(gameinfo.gametype&avail) )
Destroy();
}
override bool CanPickup( Actor toucher )
{
// no pickup if wrong iwad
if ( !(gameinfo.gametype&avail) ) return false;
return Super.CanPickup(toucher);
}
override string PickupMessage()
{
return StringTable.Localize(PickupMsg)..String.Format(" \cj(\cg¥\cf%d\cj)\c-",Stamina);
if ( Stamina > 0 )
return StringTable.Localize(PickupMsg)..String.Format(" \cj(\cg¥\cf%d\cj)\c-",Stamina);
return Super.PickupMessage();
}
override void AttachToOwner( Actor other )
{
Super.AttachToOwner(other);
// we're only attaching to the other players
if ( propagated )
return;
// give credit
if ( other.player )
if ( other.player && (Stamina > 0) )
{
SWWMScoreObj.Spawn(Stamina,other.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+other.Height/2),Font.CR_GOLD);
SWWMCredits.Give(other.player,Stamina);
}
// send to all other players
for ( int i=0; i<MAXPLAYERS; i++ )
{
if ( !playeringame[i] || !players[i].mo || (i == other.PlayerNumber()) )
continue;
let c = SWWMCollectable(Spawn(GetClass(),pos));
c.propagated = true;
if ( !c.CallTryPickup(players[i].mo) )
c.Destroy();
}
}
States
{
@ -63,11 +92,13 @@ Class FroggyChair : SWWMCollectable
override void AttachToOwner( Actor other )
{
Super.AttachToOwner(other);
cdown = 150;
if ( !propagated )
cdown = 150;
}
override void DoEffect()
{
Super.DoEffect();
if ( cdown <= 0 ) return;
if ( Owner && Owner.CheckLocalView() )
{
if ( cdown == 140 )
@ -75,7 +106,7 @@ Class FroggyChair : SWWMCollectable
else if ( cdown == 70 )
Console.MidPrint(newsmallfont,"$D_FROGGY2");
}
if ( cdown > 0 ) cdown--;
cdown--;
}
}
@ -109,12 +140,33 @@ Class MMiaSRVol3 : SWWMCollectable
}
Class MothPlushy : SWWMCollectable
{
int cdown;
Default
{
Tag "$T_MOTHPLUSH";
Inventory.PickupMessage "$T_MOTHPLUSH";
Stamina 5000;
}
override void AttachToOwner( Actor other )
{
Super.AttachToOwner(other);
if ( !propagated )
cdown = 175;
}
override void DoEffect()
{
Super.DoEffect();
if ( cdown <= 0 ) return;
if ( Owner && Owner.CheckLocalView() )
{
if ( cdown == 105 )
A_StartSound("mashiro/giggle",CHAN_ITEMEXTRA,CHANF_OVERLAP|CHANF_UI,1.,0.);
if ( cdown == 35 )
Console.MidPrint(newsmallfont,"$D_MASHIRO");
}
cdown--;
}
}
Class BRCDebutLP : SWWMCollectable
{
@ -170,15 +222,6 @@ Class HegeCactus : SWWMCollectable
Stamina 8000;
}
}
Class RaideminFig : SWWMCollectable
{
Default
{
Tag "$T_RAIDEMIN";
Inventory.PickupMessage "$T_RAIDEMIN";
Stamina 9000;
}
}
Class FrispyCorn : SWWMCollectable
{
Default
@ -188,15 +231,6 @@ Class FrispyCorn : SWWMCollectable
Stamina 500;
}
}
Class Hoagie : SWWMCollectable
{
Default
{
Tag "$T_HOAGIE";
Inventory.PickupMessage "$T_HOAGIE";
Stamina 800;
}
}
Class LithiumVol1 : SWWMCollectable
{
Default
@ -206,15 +240,6 @@ Class LithiumVol1 : SWWMCollectable
Stamina 3500;
}
}
Class MeetTheSniper : SWWMCollectable
{
Default
{
Tag "$T_PISS";
Inventory.PickupMessage "$T_PISS";
Stamina 1200;
}
}
// Heretic
Class DemoPlush : SWWMCollectable
{
@ -226,26 +251,6 @@ Class DemoPlush : SWWMCollectable
Stamina 6400;
}
}
Class Firemace : SWWMCollectable
{
Default
{
Tag "$T_FIREMACE";
Inventory.PickupMessage "$T_FIREMACE";
SWWMCollectable.Availability AVAIL_Heretic;
Stamina 10000;
}
}
Class DoomguyFig : SWWMCollectable
{
Default
{
Tag "$T_DOOMFIG";
Inventory.PickupMessage "$T_DOOMFIG";
SWWMCollectable.Availability AVAIL_Heretic;
Stamina 9000;
}
}
Class TomeOfPower : SWWMCollectable
{
Default
@ -268,6 +273,8 @@ Class QuartzFlask : SWWMCollectable
}
Class SayaBean : SWWMCollectable
{
int cdown;
Default
{
Tag "$T_SAYABEAN";
@ -275,18 +282,25 @@ Class SayaBean : SWWMCollectable
SWWMCollectable.Availability AVAIL_Heretic;
Stamina 12000;
}
}
// Hexen
Class PuzzleDesignBook : SWWMCollectable
{
Default
override void AttachToOwner( Actor other )
{
Tag "$T_PUZZLEBOOK";
Inventory.PickupMessage "$T_PUZZLEBOOK";
SWWMCollectable.Availability AVAIL_Hexen;
Stamina 2500;
Super.AttachToOwner(other);
if ( !propagated )
cdown = 70;
}
override void DoEffect()
{
Super.DoEffect();
if ( cdown <= 0 ) return;
if ( Owner && Owner.CheckLocalView() )
{
if ( cdown == 35 )
A_StartSound("saya/giggle",CHAN_ITEMEXTRA,CHANF_OVERLAP|CHANF_UI,1.,0.);
}
cdown--;
}
}
// Hexen
Class KirinCummies : SWWMCollectable
{
Default
@ -309,6 +323,8 @@ Class MilkBreads : SWWMCollectable
}
Class EyeObelisk : SWWMCollectable
{
int cdown;
Default
{
Tag "$T_OBELISK";
@ -316,6 +332,20 @@ Class EyeObelisk : SWWMCollectable
SWWMCollectable.Availability AVAIL_Hexen;
Stamina 24000;
}
override void PostBeginPlay()
{
if ( InStateSequence(CurState,FindState("Spawn")) )
A_StartSound("obelisk/idle",CHAN_BODY,CHANF_LOOPING);
}
override void AttachToOwner( Actor other )
{
Super.AttachToOwner(other);
if ( propagated ) return;
// nauseatingly fast trip through images of the future
if ( other.player != players[consoleplayer] ) return;
let hnd = EyeObeliskFlash(StaticEventHandler.Find("EyeObeliskFlash"));
if ( hnd ) hnd.doit = true;
}
}
Class KirinManga : SWWMCollectable
{
@ -328,6 +358,96 @@ Class KirinManga : SWWMCollectable
}
}
Class EyeObeliskFlash : StaticEventHandler
{
bool doit;
int cd;
ui bool doit_ui;
ui int timeme;
ui TextureID ftex[29];
override void UiTick()
{
if ( !doit && !doit_ui )
{
timeme = 0;
return;
}
if ( timeme == 0 )
{
// do not trigger if the user doesn't want this
if ( CVar.GetCVar('swwm_noscare',players[consoleplayer]).GetBool() )
return;
doit_ui = true;
for ( int i=0; i<29; i++ )
ftex[i] = TexMan.CheckForTexture(String.Format("graphics/obelisk/O_SIGHT%d.png",i),TexMan.Type_Any);
S_StartSound("obelisk/sight",CHAN_VOICE,CHANF_UI|CHANF_NOPAUSE,1.,0.);
}
if ( timeme == 210 ) Console.MidPrint(newsmallfont,"$D_OBELISK");
if ( timeme == 240 ) doit_ui = false;
timeme++;
}
override void WorldTick()
{
if ( !doit ) return;
cd++;
if ( cd < 1 ) return;
cd = 0;
doit = false;
}
override void RenderOverlay( RenderEvent e )
{
if ( !doit_ui ) return;
double secs = (timeme+e.FracTic)/Thinker.TICRATE;
PlayerInfo p = players[consoleplayer];
if ( secs < .25 ) Screen.Dim("White",(secs*4.)**2.,0,0,Screen.GetWidth(),Screen.GetHeight());
else if ( secs < 2.25 )
{
Shader.SetEnabled(p,"ObPass0",true);
Shader.SetEnabled(p,"ObPass1",true);
Shader.SetEnabled(p,"ObPass2",true);
Shader.SetEnabled(p,"ObPass3",true);
Shader.SetUniform1f(p,"ObPass0","Timer",(timeme+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"ObPass1","Timer",(timeme+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"ObPass2","Timer",(timeme+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"ObPass3","Timer",(timeme+e.FracTic)/Thinker.TICRATE);
secs -= .25;
Screen.Dim("Black",1.,0,0,Screen.GetWidth(),Screen.GetHeight());
// flash images:
// 0. mirror man (thievery at the forsaken manor redux)
// 1. viridescent lord (hexmas)
// 2. nightmare lord (spooktober)
// 3. dreadful lady (strife 25th anniversary project)
// 4. shinnovre (unsx 6)
// 5. hiikya (the hiikyan war)
// 6. rebirth of azadeku (project abyss dlc1: forgotten times)
// 7. heart of assimilation (project abyss dlc2: the void lords)
// 8. sage of blue (project abyss dlc3: assault on blue tower)
// 9. ser (project convergence)
int whichimg = clamp(int((10.*secs)/2.),0,9);
if ( !ftex[whichimg] ) return;
double ar = Screen.GetAspectRatio();
Vector2 tsize = TexMan.GetScaledSize(ftex[whichimg]);
double sar = tsize.x/tsize.y;
Vector2 vsize;
if ( sar > ar ) vsize = (tsize.x,tsize.x/ar);
else if ( sar < ar ) vsize = (tsize.y*ar,tsize.y);
else vsize = tsize;
vsize *= .75;
Screen.DrawTexture(ftex[whichimg],false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true);
}
else if ( secs < 6.25 )
{
Shader.SetEnabled(p,"ObPass0",false);
Shader.SetEnabled(p,"ObPass1",false);
Shader.SetEnabled(p,"ObPass2",false);
Shader.SetEnabled(p,"ObPass3",false);
secs -= 2.25;
Screen.Dim("White",(1.-secs*.25)**1.5,0,0,Screen.GetWidth(),Screen.GetHeight());
}
}
}
// yay!
Class FancyConfetti : Actor
{
@ -397,6 +517,97 @@ Class FancyConfetti : Actor
Stop;
}
}
Class PartyTime : Actor
{
bool ignite;
Default
{
Radius .1;
Height 0.;
+NOBLOCKMAP;
+NOTELEPORT;
+DONTSPLASH;
+NOINTERACTION;
}
override void PostBeginPlay()
{
if ( target ) specialf1 = target.Height/2.;
else specialf1 = 16.;
}
override void Tick()
{
if ( ignite )
{
// wait for the sound to stop
if ( !IsActorPlayingSound(CHAN_ITEM) )
Destroy();
return;
}
if ( !target || (target.tics == -1) )
{
// burst into treats!
ignite = true;
A_Confetti();
return;
}
SetOrigin(target.pos,false);
}
action void A_Confetti()
{
A_StartSound("misc/tada",CHAN_ITEM);
double ang, pt;
int numpt = Random[ExploS](100,120);
for ( int i=0; i<numpt; i++ )
{
ang = FRandom[ExploS](0,360);
pt = FRandom[ExploS](-90,30);
let c = Spawn("FancyConfetti",Vec3Offset(0,0,specialf1));
c.vel = (cos(pt)*cos(ang),cos(pt)*sin(ang),-sin(pt))*FRandom[ExploS](2,8);
}
}
}
Class ChanceboxSpawner : Actor
{
override void PostBeginPlay()
{
int numcol = 0;
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let c = (Class<SWWMCollectable>)(AllActorClasses[i]);
if ( !c || (c == 'SWWMCollectable') ) continue;
let def = GetDefaultByType(c);
// check that we can collect it in this IWAD
if ( !(gameinfo.gametype&def.avail) ) continue;
numcol++;
}
let ti = ThinkerIterator.Create("SWWMCollectable");
while ( ti.Next() ) numcol--;
ti = ThinkerIterator.Create("Chancebox");
while ( ti.Next() ) numcol--;
if ( numcol <= 0 )
{
// can't spawn a box, everything's been collected
Destroy();
return;
}
let b = Spawn("Chancebox",pos);
// copy all our stuff
b.spawnangle = spawnangle;
b.angle = angle;
b.pitch = pitch;
b.roll = roll;
b.spawnpoint = spawnpoint;
b.special = special;
for ( int i=0; i<5; i++ )
b.Args[i] = Args[i];
b.spawnflags = spawnflags&~MTF_SECRET;
b.HandleSpawnFlags();
b.bCOUNTSECRET = spawnflags&MTF_SECRET;
b.ChangeTid(tid);
}
}
// Collectable box (recycling of discarded "chance box" item)
Class Chancebox : Actor
@ -424,14 +635,21 @@ Class Chancebox : Actor
SWWMCollectable c;
while ( c = SWWMCollectable(ti.Next()) )
{
int f = candidates.Find(inv.GetClass());
int f = candidates.Find(c.GetClass());
if ( f < candidates.Size() )
candidates.Delete(f);
}
if ( candidates.Size() <= 0 )
{
// TODO no candidates? just spawn some random loot
Console.Printf("\cg// TODO spawn random loot\c-");
// no candidates? just burst into treats
for ( int i=0; i<=30; i++ )
{
let a = Spawn((i==0)?"RefresherItem":(i%3)?"HealthNuggetItem":"ArmorNuggetItem",pos);
a.bDROPPED = true;
a.bNOGRAVITY = false;
a.vel.z = FRandom[Chancebox](2,4);
if ( i > 0 ) a.vel.xy = (cos(i*12),sin(i*12))*FRandom[Chancebox](4,12);
}
}
else
{
@ -469,7 +687,19 @@ Class Chancebox : Actor
override bool Used( Actor user )
{
if ( CurState != FindState("Spawn") ) return false;
SetStateLabel("Active");
if ( bCountSecret )
{
user.GiveSecret();
bCountSecret = false;
}
if ( special )
{
user.A_CallSpecial(special,args[0],args[1],args[2],args[3],args[4]);
special = 0;
}
SWWMLoreLibrary.Add(user.player,"Chancebox");
specialf2 = AngleTo(user);
SetStateLabel("PreActive");
return true;
}
Default
@ -479,6 +709,7 @@ Class Chancebox : Actor
+MOVEWITHSECTOR;
+ROLLSPRITE;
+SOLID;
+INTERPOLATEANGLES;
Species "Chancebox";
}
States
@ -486,14 +717,26 @@ Class Chancebox : Actor
Spawn:
XZW1 A -1;
Stop;
PreActive:
XZW1 A 1
{
double delta = deltaangle(angle,specialf2);
int sign = (delta>=0.)?1:-1;
delta = clamp(abs(delta)*.15,.1,10.)*sign;
angle += delta;
return A_JumpIf(abs(deltaangle(angle,specialf2))<1.,"Active");
}
Wait;
Active:
XZW1 A 1
{
angle = specialf2;
specialf1 = angle;
A_StartSound("misc/drumroll",CHAN_WEAPON);
}
XZW1 A 1
{
bINTERPOLATEANGLES = false;
angle = specialf1+FRandom[Chancebox](-5,5);
pitch = FRandom[Chancebox](-5,5);
roll = FRandom[Chancebox](-5,5);

View file

@ -862,6 +862,11 @@ Class SWWMHandler : EventHandler
let t = new("UglyBoyGetsFuckedUp");
t.ChangeStatNum(Thinker.STAT_USER);
}
if ( swwm_partytime )
{
let pt = Actor.Spawn("PartyTime",e.Thing.pos);
pt.target = e.Thing;
}
}
override void WorldThingDamaged( WorldEvent e )
@ -935,6 +940,9 @@ Class SWWMHandler : EventHandler
}
}
if ( (e.Thing.Health > 0) || e.Thing.bKilled || e.Thing.bCorpse ) return;
// romero hax
if ( (e.Thing is 'BossBrain') && (e.DamageType == 'Telefrag') )
e.DamageSource.DamageMobj(null,null,Actor.TELEFRAG_DAMAGE,'EndLevel');
if ( !e.Thing.player && !e.Thing.bIsMonster && !e.Thing.bCountKill && !(e.Thing is 'ScriptedMarine') ) return;
if ( (e.DamageSource && e.DamageSource.player && (e.DamageSource != e.Thing)) )
{
@ -942,7 +950,7 @@ Class SWWMHandler : EventHandler
if ( s )
{
s.kills++;
s.AddWeaponKill(e.Inflictor,e.Thing);
s.AddWeaponKill(e.Inflictor,e.Thing,e.DamageType);
}
if ( e.DamageSource == players[consoleplayer].mo )
{
@ -1000,7 +1008,7 @@ Class SWWMHandler : EventHandler
{
scr.xscore[ofs] = 0;
scr.xtcolor[ofs] = Font.FindFontColor('BlushPink');
scr.xstr[ofs] = StringTable.Localize("$SWWM_LOVED");
scr.xstr[ofs] = StringTable.Localize((e.Thing is 'WolfensteinSS')?"$SWWM_LOVED_ALT":"$SWWM_LOVED");
scr.xcnt = ++ofs;
}
}
@ -1125,12 +1133,12 @@ Class SWWMHandler : EventHandler
DoKeyTagFix(e.Thing);
SWWMInterest.Spawn(thekey:Key(e.Thing));
}
else if ( e.Thing is 'BossBrain' )
else if ( e.Thing.GetClass() == 'BossBrain' )
{
e.Thing.SetTag("$FN_BOSSBRAIN");
e.Thing.A_SetSize(20,70);
e.Thing.A_SetSize(20,40);
e.Thing.bNOBLOOD = true;
}
else if ( e.Thing is 'CommanderKeen' ) e.Thing.SetTag("$FN_KEEN");
if ( !swwm_notrack && (e.Thing.bSHOOTABLE || e.Thing.bISMONSTER) && !(e.Thing is 'LampMoth') && !(e.Thing is 'CompanionLamp') ) SWWMCombatTracker.Spawn(e.Thing);
}
@ -1238,6 +1246,8 @@ Class SWWMHandler : EventHandler
{
if ( e.Replacement is 'DSparilHax' )
e.Replacee = 'Sorcerer2';
if ( e.Replacement is 'SWWMHangingKeen' )
e.Replacee = 'CommanderKeen';
}
override void CheckReplacement( ReplaceEvent e )
@ -1253,6 +1263,17 @@ Class SWWMHandler : EventHandler
static const Class<Actor> blackpool[] = {"BlackShell","BlackShell2"};
if ( e.Replacee is 'ItemFog' ) e.Replacement = 'SWWMItemFog';
if ( e.Replacee is 'TeleportFog' ) e.Replacement = 'SWWMTeleportFog';
if ( (e.Replacee is 'CommanderKeen') && (!e.Replacement || (e.Replacement == 'CommanderKeen')) )
{
let def = GetDefaultByType(e.Replacee);
bool dehackery = false;
for ( State s=def.SpawnState; s; s=s.NextState )
{
if ( s.bDEHACKED ) dehackery = true;
if ( s.NextState == s ) break;
}
if ( !dehackery ) e.Replacement = 'SWWMHangingKeen';
}
else if ( (e.Replacee is 'Chainsaw') || (e.Replacee is 'Gauntlets') || (e.Replacee is 'FWeapAxe') ) e.Replacement = 'PusherWeapon';
else if ( (e.Replacee is 'Fist') || (e.Replacee is 'Staff') ) e.Replacement = 'DeepImpact';
else if ( (e.Replacee is 'Pistol') || (e.Replacee is 'GoldWand') || (e.Replacee is 'FWeapFist') || (e.Replacee is 'CWeapMace') || (e.Replacee is 'MWeapWand') ) e.Replacement = 'ExplodiumGun';
@ -1433,11 +1454,6 @@ Class SWWMHandler : EventHandler
else if ( !Random[Replacements](0,3) ) e.Replacement = 'CandyGunBullets';
else e.Replacement = Random[Replacements](0,2)?'SilverBullets':'SilverBullets2';
}
else if ( (e.Replacee == 'ArtiTeleport') || (e.Replacee == 'ArtiTeleportOther') )
{
if ( Random[Replacements](0,3) ) e.Replacement = 'SWWMNothing';
else e.Replacement = 'GoldShell';
}
else if ( (e.Replacee == 'CellPack') || (e.Replacee == 'SkullRodHefty') )
{
if ( !Random[Replacements](0,2) )
@ -1452,7 +1468,7 @@ Class SWWMHandler : EventHandler
else if ( e.Replacee == 'Mana2' ) e.Replacement = 'FabricatorTier2';
else if ( e.Replacee == 'Mana3' ) e.Replacement = 'FabricatorTier3';
else if ( e.Replacee == 'ArtiBoostMana' ) e.Replacement = 'FabricatorTier4';
else if ( (e.Replacee == 'Backpack') || (e.Replacee == 'BagOfHolding') || (e.Replacee == 'ArtiPork') || (e.Replacee == 'ArtiBoostArmor') ) e.Replacement = 'HammerspaceEmbiggener';
else if ( (e.Replacee == 'Backpack') || (e.Replacee == 'BagOfHolding') || (e.Replacee == 'ArtiBoostArmor') ) e.Replacement = 'HammerspaceEmbiggener';
else if ( (e.Replacee == 'FWeaponPiece1') || (e.Replacee == 'FWeaponPiece2')
|| (e.Replacee == 'CWeaponPiece1') || (e.Replacee == 'CWeaponPiece3')
|| (e.Replacee == 'MWeaponPiece2') || (e.Replacee == 'MWeaponPiece3') )
@ -1481,11 +1497,12 @@ Class SWWMHandler : EventHandler
else if ( e.Replacee == 'Radsuit' ) e.Replacement = 'EBarrier';
else if ( (e.Replacee == 'ArtiFly') ) e.Replacement = 'GravitySuppressor';
else if ( (e.Replacee == 'InvulnerabilitySphere') || (e.Replacee == 'ArtiInvulnerability') || (e.Replacee == 'ArtiInvulnerability2') ) e.Replacement = 'FuckingInvinciball';
else if ( (e.Replacee == 'Berserk') || (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiDarkServant') || (e.Replacee == 'ArtiSpeedBoots') ) e.Replacement = 'Ragekit';
else if ( (e.Replacee == 'Berserk') || (e.Replacee == 'ArtiTomeOfPower') || (e.Replacee == 'ArtiSpeedBoots') ) e.Replacement = 'Ragekit';
else if ( (e.Replacee == 'AllMap') || (e.Replacee == 'SuperMap') ) e.Replacement = 'Omnisight';
else if ( (e.Replacee == 'Infrared') || (e.Replacee == 'ArtiTorch') ) e.Replacement = 'SWWMLamp';
else if ( (e.Replacee == 'GreenArmor') || (e.Replacee == 'SilverShield') || (e.Replacee == 'MeshArmor') ) e.Replacement = 'BlastSuitItem';
else if ( (e.Replacee == 'BlueArmor') || (e.Replacee == 'FalconShield') || (e.Replacee == 'EnchantedShield') ) e.Replacement = 'WarArmorItem';
else if ( (e.Replacee == 'ArtiPork') || (e.Replacee == 'ArtiDarkServant') || (e.Replacee == 'ArtiTeleport') || (e.Replacee == 'ArtiTeleportOther') ) e.Replacement = 'SWWMNothing'/*'Chancebox'*/;
}
override void NetworkProcess( ConsoleEvent e )
@ -1785,6 +1802,8 @@ Class SWWMHandler : EventHandler
Shader.SetEnabled(p,"WaterWarp",true);
Shader.SetUniform1f(p,"WaterWarp","timer",(gametic+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"WaterWarp","dfact",coat?.25:1.);
Shader.SetUniform3f(p,"WaterWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.));
}
else Shader.SetEnabled(p,"WaterWarp",false);
if ( demo.lastunder == Demolitionist.UNDER_LAVA )
@ -1792,6 +1811,7 @@ Class SWWMHandler : EventHandler
Shader.SetEnabled(p,"LavaWarp",true);
Shader.SetUniform1f(p,"LavaWarp","timer",(gametic+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"LavaWarp","dfact",coat?.25:1.);
Shader.SetUniform3f(p,"LavaWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.));
}
else Shader.SetEnabled(p,"LavaWarp",false);
if ( demo.lastunder == Demolitionist.UNDER_SLIME )
@ -1799,6 +1819,7 @@ Class SWWMHandler : EventHandler
Shader.SetEnabled(p,"SlimeWarp",true);
Shader.SetUniform1f(p,"SlimeWarp","timer",(gametic+e.FracTic)/Thinker.TICRATE);
Shader.SetUniform1f(p,"SlimeWarp","dfact",coat?.25:1.);
Shader.SetUniform3f(p,"SlimeWarp","lightcol",(demo.undercol.r/255.,demo.undercol.g/255.,demo.undercol.b/255.));
}
else Shader.SetEnabled(p,"SlimeWarp",false);
int lastdmg = (demo.Health>0)?demo.lastdamage:Random[Flicker](60,80);

View file

@ -33,19 +33,6 @@ Class RefresherHealth : Health
Inventory.Amount 100;
Inventory.MaxAmount 500;
}
override bool TryPickup( in out Actor other )
{
PrevHealth = other.player?other.player.health:other.health;
if ( other.GiveBody(Amount,MaxAmount) )
{
GoAwayAndDie();
let p = Powerup(other.FindInventory("RefresherRegen"));
if ( p ) p.EffectTics = p.default.EffectTics;
else other.GiveInventory("RefresherRegen",1);
return true;
}
return false;
}
}
Class RefresherRegen : Powerup
{
@ -54,6 +41,7 @@ Class RefresherRegen : Powerup
Inventory.Icon "graphics/HUD/Icons/I_Refresher.png";
Powerup.Duration -50;
Powerup.Strength 10;
+INVENTORY.ADDITIVETIME;
}
override void EndEffect()
@ -172,7 +160,7 @@ Class RefresherItem : SWWMHealth
override void AutoUseExtra()
{
let p = Powerup(Owner.FindInventory("RefresherRegen"));
if ( p ) p.EffectTics = p.default.EffectTics;
if ( p ) p.EffectTics += p.default.EffectTics;
else Owner.GiveInventory("RefresherRegen",1);
SWWMHandler.DoFlash(Owner,Color(80,224,128,255),20);
}

View file

@ -9,7 +9,7 @@ Class MsgLine
Class SWWMStatusBar : BaseStatusBar
{
TextureID StatusTex, WeaponTex, ScoreTex[3], InventoryTex, ChatTex[6],
HealthTex[5], FuelTex, DashTex, EnemyBTex, EnemyHTex[5], GenericAmmoTex[3];
HealthTex[5], FuelTex[2], DashTex, EnemyBTex, EnemyHTex[5], GenericAmmoTex[3];
HUDFont mTewiFont, mMiniwiFont, mMPlusFont, mk6x8Font;
// "Full History" contains all messages since session start, nothing is flushed
@ -28,7 +28,7 @@ Class SWWMStatusBar : BaseStatusBar
SWWMVanillaBossHandler bosshnd;
// client cvars
transient CVar safezone, maxchat[2], maxpick, chatduration, msgduration, pickduration, chatcol, teamcol, obitcol, critcol, pickcol, targetter, healthnums, scorenums, scorebonus, pois, targettag, lang, maxtarg, maxscore, maxdamns, hscale, bscale, nscale, sscale, iscale, dscale, midcol, midbcol, midduration, bigtags;
transient CVar safezone, maxchat[2], maxpick, chatduration, msgduration, pickduration, chatcol, teamcol, obitcol, critcol, pickcol, targetter, healthnums, scorenums, scorebonus, pois, targettag, lang, maxtarg, maxscore, maxdamns, hscale, bscale, nscale, sscale, iscale, dscale, midcol, midbcol, midduration, bigtags, showitems, showmaplabel, showmonsters, showsecrets, showtime, showtotaltime;
// shared stuff
Vector2 ss, hs;
@ -598,7 +598,8 @@ Class SWWMStatusBar : BaseStatusBar
SetSize(0,640,360);
StatusTex = TexMan.CheckForTexture("graphics/HUD/StatusBox.png",TexMan.Type_Any);
DashTex = TexMan.CheckForTexture("graphics/HUD/DashBar.png",TexMan.Type_Any);
FuelTex = TexMan.CheckForTexture("graphics/HUD/FuelBar.png",TexMan.Type_Any);
FuelTex[0] = TexMan.CheckForTexture("graphics/HUD/FuelBar.png",TexMan.Type_Any);
FuelTex[1] = TexMan.CheckForTexture("graphics/HUD/FuelBarS.png",TexMan.Type_Any);
HealthTex[0] = TexMan.CheckForTexture("graphics/HUD/HealthBar0.png",TexMan.Type_Any);
HealthTex[1] = TexMan.CheckForTexture("graphics/HUD/HealthBar1.png",TexMan.Type_Any);
HealthTex[2] = TexMan.CheckForTexture("graphics/HUD/HealthBar2.png",TexMan.Type_Any);
@ -864,40 +865,54 @@ Class SWWMStatusBar : BaseStatusBar
int yy = margin+19;
if ( automapactive )
{
if ( !showitems ) showitems = CVar.GetCVar('am_showitems',players[consoleplayer]);
if ( !showmaplabel ) showmaplabel = CVar.GetCVar('am_showmaplabel',players[consoleplayer]);
if ( !showmonsters ) showmonsters = CVar.GetCVar('am_showmonsters',players[consoleplayer]);
if ( !showsecrets ) showsecrets = CVar.GetCVar('am_showsecrets',players[consoleplayer]);
if ( !showtime ) showtime = CVar.GetCVar('am_showtime',players[consoleplayer]);
if ( !showtotaltime ) showtotaltime = CVar.GetCVar('am_showtotaltime',players[consoleplayer]);
int xx = int(ss.x-(margin+2));
String str;
if ( level.clusterflags&level.CLUSTER_HUB ) str = level.levelname;
int label = showmaplabel.GetInt();
if ( !label || ((level.clusterflags&level.CLUSTER_HUB) && (label == 2)) ) str = level.levelname;
else str = String.Format("%s - %s",level.mapname.MakeUpper(),level.levelname);
let fnt = LangFont(mTewiFont);
Screen.DrawText(fnt,Font.CR_FIRE,xx-fnt.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += fnt.GetHeight();
fnt = LangFont(mMiniwiFont);
if ( level.total_monsters > 0 )
if ( (level.total_monsters > 0) && showmonsters.GetBool() )
{
str = String.Format("\cxK \c-%d\cu/\c-%d",level.killed_monsters,level.total_monsters);
Screen.DrawText(fnt,Font.CR_WHITE,xx-fnt.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt,(level.killed_monsters>=level.total_monsters)?Font.CR_GOLD:Font.CR_WHITE,xx-fnt.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += fnt.GetHeight();
}
if ( level.total_items > 0 )
if ( (level.total_items > 0) && showitems.GetBool() )
{
str = String.Format("\cxI \c-%d\cu/\c-%d",level.found_items,level.total_items);
Screen.DrawText(fnt,Font.CR_WHITE,xx-fnt.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt,(level.found_items>=level.total_items)?Font.CR_GOLD:Font.CR_WHITE,xx-fnt.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += fnt.GetHeight();
}
if ( level.total_secrets > 0 )
if ( (level.total_secrets > 0) && showsecrets.GetBool() )
{
str = String.Format("\cxS \c-%d\cu/\c-%d",level.found_secrets,level.total_secrets);
Screen.DrawText(fnt,(level.found_secrets>=level.total_secrets)?Font.CR_GOLD:Font.CR_WHITE,xx-fnt.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += fnt.GetHeight();
}
int sec;
if ( showtime.GetBool() )
{
sec = Thinker.Tics2Seconds(level.maptime);
str = String.Format("\cxT \c-%02d\cu:\c-%02d\cu:\c-%02d",sec/3600,(sec%3600)/60,sec%60);
Screen.DrawText(fnt,(sec>3600)?Font.CR_RED:(sec<=level.partime)?Font.CR_GOLD:Font.CR_WHITE,xx-fnt.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += fnt.GetHeight();
}
if ( showtotaltime.GetBool() )
{
sec = Thinker.Tics2Seconds(level.totaltime);
str = String.Format("\cxTT \c-%02d\cu:\c-%02d\cu:\c-%02d",sec/3600,(sec%3600)/60,sec%60);
Screen.DrawText(fnt,Font.CR_WHITE,xx-fnt.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += fnt.GetHeight();
}
int sec = Thinker.Tics2Seconds(level.maptime);
str = String.Format("\cxT \c-%02d\cu:\c-%02d\cu:\c-%02d",sec/3600,(sec%3600)/60,sec%60);
Screen.DrawText(fnt,Font.CR_WHITE,xx-fnt.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += fnt.GetHeight();
sec = Thinker.Tics2Seconds(level.totaltime);
str = String.Format("\cxTT \c-%02d\cu:\c-%02d\cu:\c-%02d",sec/3600,(sec%3600)/60,sec%60);
Screen.DrawText(fnt,Font.CR_WHITE,xx-fnt.StringWidth(str),yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += fnt.GetHeight();
yy += 3;
}
// in doom/heretic (draw key icons)
@ -1111,7 +1126,7 @@ Class SWWMStatusBar : BaseStatusBar
if ( !d || (d.dashfuel > 20) || ((gametic%10) < 5) ) alph = 1.;
Screen.DrawTexture(DashTex,false,margin+2,ss.y-(margin+20),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,dw,DTA_Alpha,alph);
double fw = FuelInter.GetValue()/2.;
Screen.DrawTexture(FuelTex,false,margin+2,ss.y-(margin+24),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,fw);
Screen.DrawTexture(FuelTex[swwm_superfuel],false,margin+2,ss.y-(margin+24),DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_WindowRightF,fw);
double ht = clamp(HealthInter.GetValue(),0,10000);
double hw = min(ht,100);
if ( isInvulnerable() || CPlayer.mo.FindInventory("InvinciballPower") )

View file

@ -10,7 +10,7 @@ Class SWWMStatScreen : StatusScreen abstract
transient TextureID bgtex, arttex;
transient Font TewiFont, MPlusFont;
int whichart, whichtip;
transient CVar intertype, lang, origmus;
transient CVar interart, lang, origmus;
double hs, hs2;
Vector2 ss, ss2, origin, origin2;
double bgfade;
@ -30,68 +30,38 @@ Class SWWMStatScreen : StatusScreen abstract
}
private void drawSWWMBg()
{
if ( !intertype ) intertype = CVar.GetCVar('swwm_intertype',players[consoleplayer]);
if ( !interart ) interart = CVar.GetCVar('swwm_interart',players[consoleplayer]);
let pdata = SWWMStatScreenData(ThinkerIterator.Create("SWWMStatScreenData",Thinker.STAT_STATIC).Next());
if ( !pdata )
{
pdata = new("SWWMStatScreenData");
pdata.ChangeStatNum(Thinker.STAT_STATIC);
}
if ( !whichart )
if ( !whichart && interart.GetBool() )
{
int no = 0;
if ( intertype.GetInt() == 1 )
int no = StringTable.Localize("$SWWM_NFANART").ToInt();
Array<Int> ents;
ents.Clear();
for ( int i=1; i<=no; i++ )
ents.Push(i);
if ( pdata.lastart.Size() >= no )
{
no = StringTable.Localize("$SWWM_NFANART").ToInt();
Array<Int> ents;
ents.Clear();
for ( int i=1; i<=no; i++ )
ents.Push(i);
if ( pdata.lastart.Size() >= no )
{
// exclude last one, start over
int excludeme = pdata.lastart[pdata.lastart.Size()-1];
ents.Delete(excludeme-1);
pdata.lastart.Clear();
}
else
{
for ( int i=0; i<pdata.lastart.Size(); i++ )
{
int f = ents.Find(pdata.lastart[i]);
if ( f != ents.Size() )
ents.Delete(f);
}
}
whichart = ents[Random[InterArt](0,ents.Size()-1)];
pdata.lastart.Push(whichart);
// exclude last one, start over
int excludeme = pdata.lastart[pdata.lastart.Size()-1];
ents.Delete(excludeme-1);
pdata.lastart.Clear();
}
else if ( intertype.GetInt() == 2 )
else
{
no = StringTable.Localize("$SWWM_N4KOMA").ToInt();
Array<Int> ents;
ents.Clear();
for ( int i=1; i<=no; i++ )
ents.Push(i);
if ( pdata.last4koma.Size() >= no )
for ( int i=0; i<pdata.lastart.Size(); i++ )
{
// exclude last one, start over
int excludeme = pdata.last4koma[pdata.last4koma.Size()-1];
ents.Delete(excludeme-1);
pdata.last4koma.Clear();
int f = ents.Find(pdata.lastart[i]);
if ( f != ents.Size() )
ents.Delete(f);
}
else
{
for ( int i=0; i<pdata.last4koma.Size(); i++ )
{
int f = ents.Find(pdata.last4koma[i]);
if ( f != ents.Size() )
ents.Delete(f);
}
}
whichart = ents[Random[InterArt](0,ents.Size()-1)];
pdata.last4koma.Push(whichart);
}
whichart = ents[Random[InterArt](0,ents.Size()-1)];
pdata.lastart.Push(whichart);
}
if ( !bgtex ) bgtex = TexMan.CheckForTexture("graphics/InterBG.png",TexMan.Type_MiscPatch);
double ar = Screen.GetAspectRatio();
@ -103,8 +73,8 @@ Class SWWMStatScreen : StatusScreen abstract
else vsize = tsize;
Screen.DrawTexture(bgtex,false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true);
Font fnt = LangFont(TewiFont);
// background pics (fanart, 4komas)
if ( intertype.GetInt() == 1 )
// background pics
if ( whichart )
{
String artstr = StringTable.Localize(String.Format("$SWWM_FANART%d",whichart));
int semic = artstr.IndexOf(";");
@ -121,10 +91,6 @@ Class SWWMStatScreen : StatusScreen abstract
Screen.Dim("Black",.8,Screen.GetWidth()-bw,Screen.GetHeight()-bh,bw,bh);
Screen.DrawText(fnt,Font.CR_GOLD,ss.x-(len+4),ss.y-(fnt.GetHeight()+2),bstr,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
}
/*else if ( intertype.GetInt() == 2 )
{
// TBD when there's art
}*/
// intermission tips at the bottom
if ( !whichtip )
{
@ -250,7 +216,7 @@ Class SWWMStatScreen : StatusScreen abstract
}
if ( bgfade <= 0. ) return;
// redraw bgs on top, hiding the rest of the ui
if ( intertype.GetInt() == 1 )
if ( whichart )
{
double ar = Screen.GetAspectRatio();
Vector2 tsize = TexMan.GetScaledSize(arttex);
@ -261,10 +227,6 @@ Class SWWMStatScreen : StatusScreen abstract
else vsize = tsize;
Screen.DrawTexture(arttex,false,(vsize.x-tsize.x)/2,(vsize.y-tsize.y)/2,DTA_VirtualWidthF,vsize.x,DTA_VirtualHeightF,vsize.y,DTA_KeepRatio,true,DTA_Alpha,bgfade);
}
/*else if ( intertype.GetInt() == 2 )
{
// TBD when there's art
}*/
else
{
double ar = Screen.GetAspectRatio();

View file

@ -740,8 +740,6 @@ Class ParryField : Actor
+DONTSPLASH;
+NOTELEPORT;
+NOINTERACTION;
Radius 20;
Height 40;
}
override void Tick()
@ -753,14 +751,14 @@ Class ParryField : Actor
}
Vector3 x, y, z, origin;
[x, y, z] = swwm_CoordUtil.GetAxes(master.pitch,master.angle,master.roll);
origin = level.Vec3Offset(master.Vec2OffsetZ(0,0,master.player.viewz),x*20-(0,0,20));
SetOrigin(origin,true);
origin = level.Vec3Offset(master.Vec2OffsetZ(0,0,master.player.viewz),x*20);
SetOrigin(origin,false);
// check for projectiles to deflect
let ti = ThinkerIterator.Create("Actor");
Actor a;
while ( a = Actor(ti.Next()) )
{
if ( !((a.bMISSILE && !a.IsZeroDamage() && (a.target != master)) || a.bSKULLFLY) || a.bTHRUACTORS || (level.Vec3Diff(a.pos,Vec3Offset(0,0,20)).length() > 80) ) continue;
if ( !((a.bMISSILE && !a.IsZeroDamage() && (a.target != master)) || a.bSKULLFLY) || a.bTHRUACTORS || (level.Vec3Diff(a.pos,pos).length() > 80) ) continue;
Vector3 vdir = a.vel;
Vector3 dir = level.Vec3Diff(master.Vec2OffsetZ(0,0,pos.z),a.pos).unit();
if ( a.bMISSILE )
@ -1133,8 +1131,6 @@ Class SWWMWeapon : Weapon abstract
}
action void A_Melee( int dmg = 40, String hitsound = "", double rangemul = 1. )
{
// temporarily disable parry field so we can trace through
if ( invoker.pfield ) invoker.pfield.bSHOOTABLE = false;
// check for usables
let ut = new("UseLineTracer");
ut.uses.Clear();
@ -1165,12 +1161,8 @@ Class SWWMWeapon : Weapon abstract
for ( int i=0; i<maxang; i++ )
{
if ( TryMelee(angle+i*(45./16),dmg,hitsound,rangemul) || TryMelee(angle-i*(45./16),dmg,hitsound,rangemul) )
{
if ( invoker.pfield ) invoker.pfield.bSHOOTABLE = true;
return;
}
}
if ( invoker.pfield ) invoker.pfield.bSHOOTABLE = true;
// check for walls instead
FTranslatedLineTarget t;
double slope = AimLineAttack(angle,DEFMELEERANGE*rangemul,t,0.,ALF_CHECK3D);
@ -1208,6 +1200,7 @@ Class SWWMWeapon : Weapon abstract
A_StartSound(raging?"pusher/althit":(hitsound!="")?hitsound:"demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP);
A_AlertMonsters(100);
if ( raging ) raging.DoHitFX();
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,raging?(dmg*8):dmg,self,d.HitDir,d.HitLocation.z);
}
override void PlayUpSound( Actor origin )
{
@ -1287,7 +1280,9 @@ Class SWWMWeapon : Weapon abstract
Weapon.BobRangeX 0.5;
Weapon.BobRangeY 0.2;
Weapon.YAdjust 0;
Weapon.SlotPriority 1.;
Inventory.RestrictedTo "Demolitionist";
+INVENTORY.IGNORESKILL;
+WEAPON.NOALERT;
+WEAPON.NODEATHINPUT;
+FLOATBOB;

View file

@ -275,7 +275,14 @@ Class PusherProjectile : Actor
let p = Spawn("BigPusherImpact",d.HitLocation+HitNormal*4);
p.angle = atan2(HitNormal.y,HitNormal.x);
p.pitch = asin(-HitNormal.z);
bNOGRAVITY = true;
bool busted = false;
if ( swwm_omnibust )
{
if ( BusterWall.BustLinetrace(d,100,target,d.HitDir,d.HitLocation.z) )
busted = true;
}
if ( busted ) pitch = 0.;
else bNOGRAVITY = true;
}
else pitch = 0;
gravity = 1.;
@ -376,7 +383,7 @@ Class PusherWeapon : SWWMWeapon
LineTrace(angle,60,pitch,TRF_ABSPOSITION,origin.z,origin.x,origin.y,d);
if ( d.HitType != TRACE_HitNone )
{
A_QuakeEx(3,3,3,7,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.8);
A_QuakeEx(2,2,2,7,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:.8);
A_AlertMonsters(1200);
int dmg = int(5+invoker.chargelevel*3);
if ( d.HitType == TRACE_HitActor )
@ -384,7 +391,7 @@ Class PusherWeapon : SWWMWeapon
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
self.angle += clamp(diff,-5.,5.);
SWWMUtility.DoKnockback(d.HitActor,d.HitDir,8500);
d.HitActor.A_QuakeEx(4,4,4,10,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.1);
d.HitActor.A_QuakeEx(3,3,3,10,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.1);
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Tenderize',DMG_THRUSTLESS);
if ( d.HitActor.bNOBLOOD || d.HitActor.bINVULNERABLE )
{
@ -398,8 +405,9 @@ Class PusherWeapon : SWWMWeapon
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
d.HitActor.A_StartSound("pusher/meat",CHAN_DAMAGE,CHANF_OVERLAP);
}
// move towards target
bJUSTATTACKED = true;
// move towards target (unless we're standing on it)
if ( !(self is 'Demolitionist') || (Demolitionist(self).oldencroached != d.HitActor) )
bJUSTATTACKED = true;
}
else
{
@ -424,6 +432,7 @@ Class PusherWeapon : SWWMWeapon
p.angle = atan2(HitNormal.y,HitNormal.x);
p.pitch = asin(-HitNormal.z);
}
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,dmg,self,d.HitDir,d.HitLocation.z);
}
int numpt = Random[Pusher](1,3);
for ( int i=0; i<numpt; i++ )
@ -507,6 +516,7 @@ Class PusherWeapon : SWWMWeapon
p.angle = atan2(HitNormal.y,HitNormal.x);
p.pitch = asin(-HitNormal.z);
}
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,dmg,self,d.HitDir,d.HitLocation.z);
}
else if ( !Random[Pusher](0,3-int(invoker.chargelevel*3)) )
{
@ -576,6 +586,7 @@ Class PusherWeapon : SWWMWeapon
Inventory.Icon "graphics/HUD/Icons/W_Pusher.png";
Weapon.UpSound "pusher/select";
Weapon.SlotNumber 1;
Weapon.SlotPriority 2.;
Weapon.SelectionOrder 2100;
Stamina 10000;
+WEAPON.MELEEWEAPON;

View file

@ -957,6 +957,8 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
{
if ( !invlist[i] ) continue;
str = invlist[i].GetTag();
if ( invlist[i] is 'SWWMCollectable' )
str.AppendFormat(" (¥%d)",invlist[i].default.Stamina);
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
@ -1363,6 +1365,8 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
{
if ( !invlist[i] ) continue;
str = invlist[i].GetTag();
if ( invlist[i] is 'SWWMCollectable' )
str.AppendFormat(" (¥%d)",invlist[i].default.Stamina);
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
@ -1528,6 +1532,142 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
else if ( (b is 'Health') || (b is 'HealthPickup') || (b is 'SWWMHealth') ) tb = -2;
else if ( (b is 'Armor') || (b is 'SWWMSpareArmor') ) tb = -1;
else if ( b is 'PuzzleItem' ) tb = 1;
if ( ta == tb )
{
if ( a is 'Weapon' )
{
bool dummy;
int slota, slotb;
[dummy, slota] = players[consoleplayer].weapons.LocateWeapon(Weapon(a).GetClass());
if ( slota == 0 ) slota = 10;
[dummy, slotb] = players[consoleplayer].weapons.LocateWeapon(Weapon(b).GetClass());
if ( slotb == 0 ) slotb = 10;
if ( slota == slotb )
return (Weapon(a).SlotPriority <= Weapon(b).SlotPriority);
return slota > slotb;
}
else if ( a is 'Ammo' )
{
Class<Weapon> usesa, usesb;
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let w = (Class<Weapon>)(AllActorClasses[i]);
if ( !w ) continue;
let def = GetDefaultByType(w);
if ( w is 'SWWMWeapon' )
{
if ( SWWMWeapon(def).UsesAmmo(Ammo(a).GetClass()) )
usesa = w;
if ( SWWMWeapon(def).UsesAmmo(Ammo(b).GetClass()) )
usesb = w;
}
else
{
if ( (def.AmmoType1 == Ammo(a).GetClass()) || (def.AmmoType2 == Ammo(a).GetClass()) )
usesa = w;
if ( (def.AmmoType1 == Ammo(b).GetClass()) || (def.AmmoType2 == Ammo(b).GetClass()) )
usesb = w;
}
if ( usesa && usesb )
break;
}
if ( usesa && usesb )
{
bool dummy;
int slota, slotb;
[dummy, slota] = players[consoleplayer].weapons.LocateWeapon(usesa);
if ( slota == 0 ) slota = 10;
[dummy, slotb] = players[consoleplayer].weapons.LocateWeapon(usesb);
if ( slotb == 0 ) slotb = 10;
if ( slota == slotb )
{
// sort by unit value
// (special, those with zero value go last)
int vala = a.default.Stamina;
if ( vala == 0 ) vala = int.max;
int valb = b.default.Stamina;
if ( valb == 0 ) valb = int.max;
if ( vala == valb )
{
// sort alphabetically
return a.GetTag() > b.GetTag();
}
return vala > valb;
}
return slota > slotb;
}
}
else if ( a is 'MagAmmo' )
{
// oh boy this one gets complicated
Class<Ammo> pamoa, pamob;
pamoa = MagAmmo(a).ParentAmmo;
pamob = MagAmmo(b).ParentAmmo;
Class<Weapon> usesa, usesb;
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let w = (Class<Weapon>)(AllActorClasses[i]);
if ( !w ) continue;
let def = GetDefaultByType(w);
if ( w is 'SWWMWeapon' )
{
if ( SWWMWeapon(def).UsesAmmo(pamoa) )
usesa = w;
if ( SWWMWeapon(def).UsesAmmo(pamob) )
usesb = w;
}
else
{
if ( (def.AmmoType1 == pamoa) || (def.AmmoType2 == pamoa) )
usesa = w;
if ( (def.AmmoType1 == pamob) || (def.AmmoType2 == pamob) )
usesb = w;
}
if ( usesa && usesb )
break;
}
if ( usesa && usesb )
{
bool dummy;
int slota, slotb;
[dummy, slota] = players[consoleplayer].weapons.LocateWeapon(usesa);
if ( slota == 0 ) slota = 10;
[dummy, slotb] = players[consoleplayer].weapons.LocateWeapon(usesb);
if ( slotb == 0 ) slotb = 10;
if ( slota == slotb )
{
// sort by unit value
// (special, those with zero value go last)
int vala = GetDefaultByType(pamoa).Stamina;
if ( vala == 0 ) vala = int.max;
int valb = GetDefaultByType(pamob).Stamina;
if ( valb == 0 ) valb = int.max;
if ( vala == valb )
{
// sort alphabetically
return a.GetTag() > b.GetTag();
}
return vala > valb;
}
return slota > slotb;
}
}
else
{
// sort by unit value
// (special, those with zero value go last)
int vala = a.default.Stamina;
if ( vala == 0 ) vala = int.max;
int valb = b.default.Stamina;
if ( valb == 0 ) valb = int.max;
if ( vala == valb )
{
// sort alphabetically
return a.GetTag() > b.GetTag();
}
return vala > valb;
}
}
return ta > tb;
}
@ -1538,15 +1678,25 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
let db = GetDefaultByType(b);
if ( a is 'Weapon' ) ta = 2;
else if ( a is 'Ammo' ) ta = 1;
else if ( (a is 'PowerupGiver') || (a is 'AmmoFabricator') || da.bBIGPOWERUP ) ta = 3;
else if ( (a is 'PowerupGiver') || da.bBIGPOWERUP ) ta = 3;
else if ( (a is 'Health') || (a is 'HealthPickup') || (a is 'SWWMHealth') ) ta = 5;
else if ( (a is 'Armor') || (a is 'SWWMSpareArmor') ) ta = 4;
if ( b is 'Weapon' ) tb = 2;
else if ( b is 'Ammo' ) tb = 1;
else if ( (b is 'PowerupGiver') || (b is 'AmmoFabricator') || db.bBIGPOWERUP ) tb = 3;
else if ( (b is 'PowerupGiver') || db.bBIGPOWERUP ) tb = 3;
else if ( (b is 'Health') || (b is 'HealthPickup') || (b is 'SWWMHealth') ) tb = 5;
else if ( (b is 'Armor') || (b is 'SWWMSpareArmor') ) tb = 4;
return ta <= tb;
if ( ta == tb )
{
// sort by unit price
if ( da.Stamina == db.Stamina )
{
// sort alphabetically
return da.GetTag() > db.GetTag();
}
return da.Stamina > db.Stamina;
}
return ta < tb;
}
private int partition_inventory( Array<Inventory> a, int l, int h )
@ -1609,13 +1759,37 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
qsort_store(a,b,p+1,h);
}
private bool CmpMonsterKill( MonsterKill a, MonsterKill b )
{
if ( a.kills == b.kills )
{
// sort by name instead
String taga, tagb;
taga = GetDefaultByType(a.m).GetTag();
tagb = GetDefaultByType(b.m).GetTag();
// beautify if there's no tag
if ( taga == FallbackTag )
{
taga = a.m.GetClassName();
SWWMUtility.BeautifyClassName(taga);
}
if ( tagb == FallbackTag )
{
tagb = a.m.GetClassName();
SWWMUtility.BeautifyClassName(tagb);
}
return taga > tagb;
}
return a.kills < b.kills;
}
private int partition_mstats( Array<MonsterKill> a, int l, int h )
{
MonsterKill pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( pv.kills < a[j].kills )
if ( CmpMonsterKill(pv,a[j]) )
{
i++;
MonsterKill tmp = a[j];
@ -1663,6 +1837,44 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
qsort_lore(a,p+1,h);
}
private bool CmpCollectable( Inventory a, Inventory b )
{
if ( a.Stamina == b.Stamina )
{
// alphabetic
return a.GetTag() > b.GetTag();
}
// descending value
return a.default.Stamina < b.default.Stamina;
}
private int partition_collectable( Array<Inventory> a, int l, int h )
{
Inventory pv = a[h];
int i = (l-1);
for ( int j=l; j<=(h-1); j++ )
{
if ( CmpCollectable(pv,a[j]) )
{
i++;
Inventory tmp = a[j];
a[j] = a[i];
a[i] = tmp;
}
}
Inventory tmp = a[h];
a[h] = a[i+1];
a[i+1] = tmp;
return i+1;
}
private void qsort_collectable( Array<Inventory> a, int l, int h )
{
if ( l >= h ) return;
int p = partition_collectable(a,l,h);
qsort_collectable(a,l,p-1);
qsort_collectable(a,p+1,h);
}
override void Ticker()
{
Super.Ticker();
@ -1696,7 +1908,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
// alphabetically sorted inventory
for ( Inventory inv=players[consoleplayer].mo.Inv; inv; inv=inv.Inv )
{
if ( (inv.Amount <= 0) || !inv.SpawnState.ValidateSpriteFrame() || (inv is 'Key') || (inv is 'BasicArmor') || (inv is 'HexenArmor') || (inv is 'Powerup') || (inv is 'SWWMArmor') || (!(inv is 'Ammo') && !(inv is 'Weapon') && !inv.bINVBAR) && !(inv is 'HammerspaceEmbiggener') && !(inv is 'SWWMCollectable') && !(inv is 'MagAmmo') ) continue;
if ( (inv.Amount <= 0) || !inv.SpawnState.ValidateSpriteFrame() || (inv is 'Key') || (inv is 'BasicArmor') || (inv is 'HexenArmor') || (inv is 'Powerup') || (inv is 'SWWMArmor') || (!(inv is 'Ammo') && !(inv is 'Weapon') && !inv.bINVBAR) && !(inv is 'HammerspaceEmbiggener') && !(inv is 'SWWMCollectable') && !(inv is 'MagAmmo') || (inv is 'SWWMCollectable') ) continue;
// no hidden weapons
if ( (inv is 'SWWMWeapon') && SWWMWeapon(inv).bHIDEINMENU ) continue;
String tag = inv.GetTag();
@ -1732,6 +1944,15 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
let k = Key(players[consoleplayer].mo.FindInventory(Key.GetKeyType(i)));
if ( k ) invlist.Push(k);
}
// collectables, sorted by name
Array<Inventory> cols;
for ( Inventory inv=players[consoleplayer].mo.Inv; inv; inv=inv.Inv )
{
if ( inv is 'SWWMCollectable' )
cols.Push(inv);
}
qsort_collectable(cols,0,cols.Size()-1);
invlist.Append(cols);
// crop selection
if ( sel0 >= invlist.Size() ) sel0 = max(0,invlist.Size()-1);
}
@ -1746,8 +1967,6 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
if ( !type ) continue;
// no collectables
if ( type is 'SWWMCollectable' ) continue;
// no fabricators before hexen
if ( !(gameinfo.gametype&(GAME_Hexen|GAME_Strife)) && (type is 'AmmoFabricator') ) continue;
// skip maxed items
let cur = players[consoleplayer].mo.FindInventory(type);
if ( cur && (cur.Amount >= cur.MaxAmount) ) continue;
@ -1962,6 +2181,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
if ( stats.favweapon == -1 ) str = str.."N/A";
else if ( stats.wstats[stats.favweapon].w == 'SWWMWeapon' ) str = str..StringTable.Localize("$SWWM_YOURSELF");
else if ( stats.wstats[stats.favweapon].w == 'SWWMGesture' ) str = str..StringTable.Localize("$SWWM_DOKIDOKI");
else if ( stats.wstats[stats.favweapon].w == 'Weapon' ) str = str..StringTable.Localize("$SWWM_GRAVKILL");
else
{
let def = GetDefaultByType(stats.wstats[stats.favweapon].w);
@ -2362,19 +2582,19 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
int ox = ((cnt>22)?623:631);
int sec = Thinker.Tics2Seconds(stats.lstats[i].time);
str = String.Format("%02d\cu:\c-%02d\cu:\c-%02d",sec/3600,(sec%3600)/60,sec%60);
Screen.DrawText(fnt2,Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,(sec>=3600)?Font.CR_RED:(sec<=stats.lstats[i].par)?Font.CR_GOLD:Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,Font.CR_FIRE,origin.x+ox-maxlen[0],origin.y+yy,"T",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
ox -= maxlen[0]+8;
str = String.Format("%d\cu/\c-%d",stats.lstats[i].scount,stats.lstats[i].stotal);
Screen.DrawText(fnt2,Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,(stats.lstats[i].scount>=stats.lstats[i].stotal)?Font.CR_GOLD:Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,Font.CR_FIRE,origin.x+ox-maxlen[1],origin.y+yy,"S",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
ox -= maxlen[1]+8;
str = String.Format("%d\cu/\c-%d",stats.lstats[i].icount,stats.lstats[i].itotal);
Screen.DrawText(fnt2,Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,(stats.lstats[i].icount>=stats.lstats[i].itotal)?Font.CR_GOLD:Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,Font.CR_FIRE,origin.x+ox-maxlen[2],origin.y+yy,"I",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
ox -= maxlen[2]+8;
str = String.Format("%d\cu/\c-%d",stats.lstats[i].kcount,stats.lstats[i].ktotal);
Screen.DrawText(fnt2,Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,(stats.lstats[i].kcount>=stats.lstats[i].ktotal)?Font.CR_GOLD:Font.CR_WHITE,origin.x+ox-fnt2.StringWidth(str),origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
Screen.DrawText(fnt2,Font.CR_FIRE,origin.x+ox-maxlen[3],origin.y+yy,"K",DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true);
yy += 14;
}
@ -2472,6 +2692,8 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
{
if ( !invlist[i] ) continue;
str = invlist[i].GetTag();
if ( invlist[i] is 'SWWMCollectable' )
str.AppendFormat(" (¥%d)",invlist[i].default.Stamina);
len = fnt.StringWidth(str);
if ( len > longest ) longest = len;
}
@ -2482,7 +2704,9 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
{
if ( !invlist[i] ) continue;
str = invlist[i].GetTag();
Screen.DrawText(fnt,Font.CR_UNTRANSLATED,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i!=sel0)?Color(96,0,0,0):Color(0,0,0,0));
if ( invlist[i] is 'SWWMCollectable' )
str.AppendFormat(" \cj(\cg¥\cf%d\cj)\c-",invlist[i].default.Stamina);
Screen.DrawText(fnt,(invlist[i] is 'SWWMCollectable')?Font.CR_PURPLE:Font.CR_UNTRANSLATED,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i!=sel0)?Color(96,0,0,0):Color(0,0,0,0));
yy += 16;
if ( yy >= 370 )
{
@ -2615,7 +2839,7 @@ Class SWWMKnowledgeBaseMenu : GenericMenu
int clscol = Font.CR_WHITE;
if ( storelist[i] is 'Weapon' ) clscol = Font.CR_GOLD;
else if ( storelist[i] is 'Ammo' ) clscol = Font.CR_BROWN;
else if ( (storelist[i] is 'PowerupGiver') || (storelist[i] is 'AmmoFabricator') || def.bBIGPOWERUP ) clscol = Font.CR_PURPLE;
else if ( (storelist[i] is 'PowerupGiver') || def.bBIGPOWERUP ) clscol = Font.CR_PURPLE;
else if ( (storelist[i] is 'Health') || (storelist[i] is 'HealthPickup') || (storelist[i] is 'SWWMHealth') ) clscol = Font.CR_RED;
else if ( (storelist[i] is 'Armor') || (storelist[i] is 'SWWMSpareArmor') ) clscol = Font.CR_GREEN;
Screen.DrawText(fnt,clscol,origin.x+xx,origin.y+yy,str,DTA_VirtualWidthF,ss.x,DTA_VirtualHeightF,ss.y,DTA_KeepRatio,true,DTA_ColorOverlay,(i!=sel0)?Color(96,0,0,0):Color(0,0,0,0));

View file

@ -43,6 +43,7 @@ Class Demolitionist : PlayerPawn
};
int lastunder;
Color undercol;
int deadtimer;
transient int revivefail;
@ -75,10 +76,9 @@ Class Demolitionist : PlayerPawn
Player.AirCapacity 0;
Player.GruntSpeed 20;
Player.SoundClass "demolitionist";
DamageFactor "Drowning", 0.0;
DamageFactor "Poison", 0.0;
DamageFactor "PoisonCloud", 0.0;
DamageFactor "Falling", 0.0;
DamageFactor "Drowning", 0.;
DamageFactor "Poison", 0.;
DamageFactor "PoisonCloud", 0.;
Demolitionist.DashFuel 240.;
+NOBLOOD;
+DONTGIB;
@ -162,6 +162,12 @@ Class Demolitionist : PlayerPawn
if ( !type || (type == 'SWWMArmor') ) continue;
if ( GetReplacement(type) == type )
{
let owned = FindInventory(type);
if ( owned )
{
owned.Amount = owned.MaxAmount;
continue;
}
let item = Inventory(Spawn(type));
item.ClearCounters(); // don't increase item counts
item.Amount = item.MaxAmount;
@ -175,6 +181,12 @@ Class Demolitionist : PlayerPawn
if ( !type || (type == 'SWWMSpareArmor') ) continue;
if ( GetReplacement(type) == type )
{
let owned = FindInventory(type);
if ( owned )
{
owned.Amount = owned.MaxAmount;
continue;
}
let item = Inventory(Spawn(type));
item.ClearCounters(); // don't increase item counts
item.Amount = item.MaxAmount;
@ -262,6 +274,19 @@ Class Demolitionist : PlayerPawn
}
if ( !giveall ) return;
}
if ( (giveall == ALL_YESYES) || (name ~== "collectables") )
{
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
let type = (class<SWWMCollectable>)(AllActorClasses[i]);
if ( !type || (type == 'SWWMCollectable') ) continue;
let def = GetDefaultByType(type);
// check that we can collect it in this IWAD
if ( !(gameinfo.gametype&def.avail) ) continue;
let item = Inventory(Spawn(AllActorClasses[i]));
if ( !item.CallTryPickup(self) ) item.Destroy();
}
}
if ( giveall ) return;
let type = name;
if ( !type )
@ -319,7 +344,7 @@ Class Demolitionist : PlayerPawn
dashboost *= (player.cmd.buttons&BT_USER2)?.9:.1;
}
mystats.fuelusage += dashfuel-max(0.,dashfuel-dashboost);
dashfuel = max(0.,dashfuel-dashboost);
if ( !swwm_superfuel ) dashfuel = max(0.,dashfuel-dashboost);
dashcooldown = min(40,max(10,int(dashcooldown*1.4)));
fuelcooldown = max(30,fuelcooldown);
if ( (dashfuel <= 0.) || (dashboost <= 0.) )
@ -337,7 +362,7 @@ Class Demolitionist : PlayerPawn
last_boost = level.maptime+1;
}
mystats.fuelusage += dashfuel-max(0.,dashfuel-dashboost);
dashfuel = max(0.,dashfuel-dashboost);
if ( !swwm_superfuel ) dashfuel = max(0.,dashfuel-dashboost);
if ( ((dashfuel <= 0.) || (dashboost <= 0.)) )
{
if ( !initial )
@ -416,6 +441,7 @@ Class Demolitionist : PlayerPawn
curunder = UNDER_WATER;
break;
}
undercol = headregion.ColorMap.LightColor;
}
if ( (curunder != lastunder) || restore )
{
@ -567,7 +593,7 @@ Class Demolitionist : PlayerPawn
if ( vel.length() > mystats.topspeed ) mystats.topspeed = vel.length();
if ( !myvoice ) myvoice = CVar.GetCVar('swwm_voicetype',player);
if ( !mute ) mute = CVar.GetCVar('swwm_mutevoice',player);
if ( player.onground && !bNoGravity && !lastground && (waterlevel < 2) && (health > 0) )
if ( player.onground && !bNoGravity && !lastground && (waterlevel < 2) )
{
// bump down weapon
bumpdown = true;
@ -589,7 +615,7 @@ Class Demolitionist : PlayerPawn
mystats.stompcount++;
}
if ( lastvelz < -10 ) A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP);
if ( (player == players[consoleplayer]) && (lastvelz < -gruntspeed) && (mute.GetInt() < 4) )
if ( (player == players[consoleplayer]) && (lastvelz < -gruntspeed) && (mute.GetInt() < 4) && (health > 0) )
A_StartSound(String.Format("voice/%s/grunt",myvoice.GetString()),CHAN_DEMOVOICE,CHANF_OVERLAP);
if ( lastvelz < -1 )
A_Footstep(0,true,clamp(-lastvelz*0.05,0.0,1.0));
@ -702,6 +728,7 @@ Class Demolitionist : PlayerPawn
if ( a.bACTLIKEBRIDGE && (diff.z <= -a.Height) ) continue; // don't bump bridges if hit by above
// large monsters will stop the player (unless hit from above if we're going at ground pound speed)
A_QuakeEx(4,4,4,10,0,128,"",QF_RELATIVE|QF_SCALEDOWN);
A_AlertMonsters(800);
A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP);
a.A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP);
bumptic = gametic+int(20+spd/4.);
@ -776,11 +803,12 @@ Class Demolitionist : PlayerPawn
if ( dir dot ceilnorm < -.6 )
{
bool busted = false;
if ( raging )
if ( raging || swwm_omnibust )
{
// see if we can bust it
let tempme = new("LineTracer"); // gross hack to pass needed data
int dmg = int(128+spd*20);
int dmg = int(15+spd*2.5);
if ( raging ) dmg *= 8;
bool buttslam = false;
// BUTTSLAM
if ( dir dot viewdir < -.3 )
@ -794,13 +822,16 @@ Class Demolitionist : PlayerPawn
if ( BusterWall.Bust(tempme.Results,dmg,self,dir,pos.z+Height) )
{
// busted through
let ps = Spawn("BigPunchSplash",Vec3Offset(0,0,Height));
ps.damagetype = buttslam?'Buttslam':'Dash';
ps.target = self;
ps.special1 = int(15+spd*2.5);
if ( raging )
{
let ps = Spawn("BigPunchSplash",Vec3Offset(0,0,Height));
ps.damagetype = buttslam?'Buttslam':'Dash';
ps.target = self;
ps.special1 = int(15+spd*2.5);
raging.DoHitFX();
}
A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP);
busted = true;
raging.DoHitFX();
if ( buttslam )
{
A_StartSound("demolitionist/buttslam",CHAN_DAMAGE,CHANF_OVERLAP,1.,.2);
@ -810,13 +841,14 @@ Class Demolitionist : PlayerPawn
}
}
}
if ( !busted )
if ( !busted || !raging )
{
// headbump
bumped = true;
A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP);
bumptic = gametic+int(20+spd/4.);
A_QuakeEx(8,8,8,16,0,128,"",QF_RELATIVE|QF_SCALEDOWN);
A_AlertMonsters(800);
vel *= .2;
vel -= dir*(10+(spd*30/mass));
vel.z += 5+(spd*(10/mass));
@ -853,11 +885,12 @@ Class Demolitionist : PlayerPawn
// don't bump if we're only grazing it
if ( dir dot wallnorm > -.6 )
continue;
if ( raging )
if ( raging || swwm_omnibust )
{
// see if we can bust it
let tempme = new("LineTracer"); // gross hack to pass needed data
int dmg = int(128+spd*20);
int dmg = int(15+spd*2.5);
if ( raging ) dmg *= 8;
bool buttslam = false;
// BUTTSLAM
if ( dir dot viewdir < -.3 )
@ -881,12 +914,15 @@ Class Demolitionist : PlayerPawn
if ( BusterWall.Bust(tempme.Results,dmg,self,dir,pos.z+Height/2) )
{
// busted through
let ps = Spawn("BigPunchSplash",Vec3Offset(dir.x*radius,dir.y*radius,(tempme.Results.Tier==TIER_UPPER)?Height:(tempme.Results.Tier==TIER_LOWER)?0:(Height/2)));
ps.damagetype = buttslam?'Buttslam':'Dash';
ps.target = self;
ps.special1 = int(15+spd*2.5);
if ( raging )
{
let ps = Spawn("BigPunchSplash",Vec3Offset(dir.x*radius,dir.y*radius,(tempme.Results.Tier==TIER_UPPER)?Height:(tempme.Results.Tier==TIER_LOWER)?0:(Height/2)));
ps.damagetype = buttslam?'Buttslam':'Dash';
ps.target = self;
ps.special1 = int(15+spd*2.5);
raging.DoHitFX();
}
A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP);
raging.DoHitFX();
if ( buttslam )
{
A_StartSound("demolitionist/buttslam",CHAN_DAMAGE,CHANF_OVERLAP,1.,.2);
@ -894,7 +930,7 @@ Class Demolitionist : PlayerPawn
A_QuakeEx(8,8,8,8,0,3000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:1.);
mystats.buttslams++;
}
continue;
if ( raging ) continue; // don't stop
}
}
// wallbump
@ -902,6 +938,7 @@ Class Demolitionist : PlayerPawn
A_StartSound("demolitionist/bump",CHAN_DAMAGE,CHANF_OVERLAP);
bumptic = gametic+int(20+spd/4.);
A_QuakeEx(8,8,8,16,0,128,"",QF_RELATIVE|QF_SCALEDOWN);
A_AlertMonsters(800);
vel *= .2;
vel -= dir*(10+(spd*30/mass));
vel += wallnorm*(10+(spd*50/mass));
@ -940,6 +977,9 @@ Class Demolitionist : PlayerPawn
}
override int DamageMobj( Actor inflictor, Actor source, int damage, Name mod, int flags, double angle )
{
// completely nullify non-lethal fall damage
if ( (mod == 'Falling') && (damage < TELEFRAG_DAMAGE) )
damage = 0;
if ( damage <= 0 )
{
lastdamage = 0;
@ -2105,22 +2145,21 @@ Class Demolitionist : PlayerPawn
Loop;
CrouchWave:
#### # 3;
XZWE Z 3;
XZWF ABCDEFGHIJKLMN 3;
XZWF ABCDEFGHIJKLMNO 3;
Goto Crouch+1;
CrouchApprove:
#### # 3;
XZWF OPQRSTUVWXYZ 3;
XZWG ABCDE 3;
XZWF PQRSTUVWXYZ 3;
XZWG ABCDEF 3;
Goto Crouch+1;
CrouchVictory:
#### # 3;
XZWG FGHIJKLMNOPQRSTUVWX 3;
XZWG GHIJKLMNOPQRSTUVWXY 3;
Goto Crouch+1;
CrouchBlowKiss:
#### # 3;
XZWG YZ 3;
XZWH ABCDEFGHIJKLMNOPQ 3;
XZWG Z 3;
XZWH ABCDEFGHIJKLMNOPQR 3;
Goto Crouch+1;
CrouchMissile:
XZW7 M 2;
@ -2168,23 +2207,23 @@ Class Demolitionist : PlayerPawn
Goto Spawn+1;
Float:
#### # 3;
XZWD WXYZ 3;
XZWE ABCDEFGH 3;
XZWD XYZ 3;
XZWE ABCDEFGHI 3;
Goto Float+1;
Swim:
#### # 2;
XZWE IJK 2;
XZWE JKL 2;
Goto SwimLoop;
SwimLoop:
XZWE LMN 2;
XZWE O 0 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.2);
XZWE OPQRST 2;
XZWE U 0 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.2);
XZWE UVW 2;
XZWE MNO 2;
XZWE P 0 A_StartSound("demolitionist/runstart",CHAN_FOOTSTEP,CHANF_OVERLAP,.2);
XZWE PQRSTU 2;
XZWE V 0 A_StartSound("demolitionist/runstop",CHAN_FOOTSTEP,CHANF_OVERLAP,.2);
XZWE VWX 2;
Loop;
SwimEnd:
#### # 2;
XZWE LXY 2;
XZWE MYZ 2;
Goto Float+1;
VoodooSpawn:
XZWZ A -1;
@ -2443,11 +2482,12 @@ Class DemolitionistShockwave : Actor
s.vel = pvel;
}
let raging = RagekitPower(target.FindInventory("RagekitPower"));
if ( raging )
if ( raging || swwm_omnibust )
{
// bust the floor if we can
let tempme = new("LineTracer"); // gross hack to pass needed data
int dmg = (40+min(special1,120))*8;
int dmg = 40+min(special1,120);
if ( raging ) dmg *= 8;
F3DFloor ff;
for ( int i=0; i<FloorSector.Get3DFloorCount(); i++ )
{
@ -2459,11 +2499,14 @@ Class DemolitionistShockwave : Actor
tempme.Results.HitSector = FloorSector;
tempme.Results.HitType = TRACE_HitFloor;
BusterWall.Bust(tempme.Results,dmg,target,(0,0,-1),pos.z);
let ps = Spawn("BigPunchSplash",pos);
ps.damagetype = 'GroundPound';
ps.target = target;
ps.special1 = dmg;
raging.DoHitFX();
if ( raging )
{
let ps = Spawn("BigPunchSplash",pos);
ps.damagetype = 'GroundPound';
ps.target = target;
ps.special1 = dmg;
raging.DoHitFX();
}
}
}
States
@ -2656,6 +2699,24 @@ Class LoveHeartBurstLight : PaletteLight
}
}
Class LoveHeartLight : PointLightAttenuated
{
Default
{
Args 255,176,208,80;
}
override void Tick()
{
Super.Tick();
if ( target || target.InStateSequence(target.CurState,target.FindState("Death")) )
{
Destroy();
return;
}
SetOrigin(target.pos,true);
}
}
Class LoveHeart : Actor
{
Default
@ -2679,6 +2740,17 @@ Class LoveHeart : Actor
override int DoSpecialDamage( Actor target, int damage, Name damagetype )
{
let raging = RagekitPower(self.target.FindInventory("RagekitPower"));
if ( raging )
{
bEXTREMEDEATH = true;
bNOEXTREMEDEATH = false;
}
else
{
bEXTREMEDEATH = false;
bNOEXTREMEDEATH = true;
}
if ( target is 'WolfensteinSS' ) target.bFRIENDLY = false;
if ( target.IsFriend(self.target) || (target is 'MBFHelperDog') )
{
int healamt = clamp(special2,5,15);
@ -2701,6 +2773,8 @@ Class LoveHeart : Actor
}
return 0;
}
Vector3 dirto = level.Vec3Diff(pos,target.Vec3Offset(0,0,target.Height/2)).unit();
SWWMUtility.DoKnockback(target,dirto,1500.*damage);
let bread = target.FindState("Pain");
if ( bread ) target.SetState(bread);
if ( raging )
@ -2708,6 +2782,14 @@ Class LoveHeart : Actor
damage *= 8;
raging.DoHitFX();
}
if ( target is 'WolfensteinSS' )
{
damage = int.max;
bEXTREMEDEATH = true;
bNOEXTREMEDEATH = false;
}
else if ( target is 'SWWMHangingKeen' )
damage = max(target.Health,damage);
return damage;
}
@ -2751,13 +2833,30 @@ Class LoveHeart : Actor
{
Super.PostBeginPlay();
A_StartSound("misc/heart",CHAN_WEAPON);
A_AttachLight('LOVELIGHT',DynamicLight.PointLight,Color(255,176,208),80,80,DYNAMICLIGHT.LF_ATTENUATE);
//A_AttachLight('LOVELIGHT',DynamicLight.PointLight,Color(255,176,208),80,80,DYNAMICLIGHT.LF_ATTENUATE);
// can't use Attach/RemoveLight for now due to heavy performance issues
let l = Spawn("LoveHeartLight",pos);
l.target = self;
special2 = 25;
}
action void A_HeartBurst()
{
// use line
if ( BlockingLine )
{
int s = SWWMUtility.PointOnLineSide(pos.xy,BlockingLine);
BlockingLine.RemoteActivate(target,s,SPAC_Use,pos);
}
if ( swwm_omnibust )
{
int dmg = GetMissileDamage(0,0);
let raging = RagekitPower(self.target.FindInventory("RagekitPower"));
if ( raging ) dmg *= 8;
if ( BusterWall.ProjectileBust(self,dmg,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch))) )
raging.DoHitFX();
}
A_SetRenderStyle(1.,STYLE_Add);
A_RemoveLight('LOVELIGHT');
//A_RemoveLight('LOVELIGHT');
A_QuakeEx(2,2,2,8,0,300,"",QF_RELATIVE|QF_SCALEDOWN);
A_StartSound("bestsound",CHAN_VOICE);
Spawn("LoveHeartBurstLight",pos);
@ -2810,8 +2909,8 @@ Class HHitList
Class SWWMGesture : SWWMWeapon
{
Weapon formerweapon;
int whichgesture;
bool deaded;
int whichgesture, nextgesture;
bool deaded, queued;
int gonect;
// these should prevent autoswitch when out of ammo
@ -2826,7 +2925,20 @@ Class SWWMGesture : SWWMWeapon
static void SetGesture( PlayerPawn mo, int which = 0 )
{
if ( (mo.player.ReadyWeapon is 'SWWMGesture') || (mo.player.PendingWeapon is 'SWWMGesture') ) return; // already gesturing
if ( mo.player.PendingWeapon is 'SWWMGesture' ) return; // already initiating a gesture
if ( (mo.player.ReadyWeapon is 'SWWMGesture') )
{
// already gesturing
// just queue another one
// (ignore grenade, that one uses special refire handling)
if ( which != -1 )
{
let g = SWWMGesture(mo.player.ReadyWeapon);
g.nextgesture = which;
g.queued = true;
}
return;
}
SWWMGesture w = SWWMGesture(mo.FindInventory("SWWMGesture"));
if ( !w )
{
@ -2850,6 +2962,13 @@ Class SWWMGesture : SWWMWeapon
action void A_FinishGesture()
{
if ( invoker.queued )
{
invoker.whichgesture = invoker.nextgesture;
invoker.queued = false;
player.SetPSprite(PSP_WEAPON,ResolveState("Ready"));
return;
}
player.PendingWeapon = invoker.formerweapon;
player.SetPSprite(PSP_WEAPON,ResolveState("Deselect"));
}

View file

@ -18,6 +18,14 @@ Class GrilledCheeseSandwich : Inventory
Mixin SWWMAutoUseFix;
Mixin SWWMOverlapPickupSound;
// for falling off cliffs and others
// last 5 seconds of safe positions
bool lastsafevalid;
Vector3 lastsafepos[5];
double lastsafeangle[5];
int safetic;
int dteleport;
override Inventory CreateCopy( Actor other )
{
// additional lore
@ -75,23 +83,80 @@ Class GrilledCheeseSandwich : Inventory
return; // don't trigger on endlevel damage
if ( damageType == 'Telefrag' )
{
// prevent overlap with voodoo doll
if ( source.player == Owner.player )
return; // we can't avoid voodoo telefrag, it would break things
else
Owner.DamageMobj(Owner,Owner,damage,damageType,flags); // kill what attempted the telefrag
dteleport = 1; // teleport has to be on next tic
else source.DamageMobj(Owner,Owner,damage,damageType,flags); // kill what attempted the telefrag
}
if ( (Owner.Health-damage <= 0) && (Amount > 0) )
{
if ( (damageType == 'Falling') || (damageType == 'InstantDeath') )
SafeTeleport(); // get out of pits
newdamage = 0;
if ( (Owner.player == players[consoleplayer]) || bBigPowerup ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA);
DoTheThing(true);
Amount--;
}
}
void SafeTeleport( bool tostart = false )
{
Spawn("SWWMItemFog",Owner.Vec3Offset(0,0,Owner.Height/2));
Vector3 safepos;
double safeangle;
if ( tostart || !lastsafevalid )
{
if ( deathmatch ) [safepos, safeangle] = level.PickDeathmatchStart();
else [safepos, safeangle] = level.PickPlayerStart(Owner.PlayerNumber());
}
else
{
safepos = lastsafepos[4];
safeangle = lastsafeangle[4];
}
Owner.Teleport(safepos,safeangle,TF_TELEFRAG|TF_FORCED|TF_USESPOTZ|TF_NOFOG);
}
override void DoEffect()
{
Super.DoEffect();
if ( Amount <= 0 ) DepleteOrDestroy();
if ( dteleport > 0 )
{
dteleport--;
if ( dteleport <= 0 ) dteleport = -1;
}
else if ( dteleport == -1 )
{
dteleport = 0;
SafeTeleport();
}
// check safe spot
if ( Owner && !(Owner.CurSector.flags&Sector.SECF_ENDLEVEL) && (Owner.CurSector.DamageAmount <= 0) && (Owner.waterlevel < 2) && (Owner.GetFloorTerrain().DamageAmount <= 0) && (Owner.pos.z <= Owner.floorz) )
{
if ( safetic == 0 )
{
if ( lastsafevalid )
{
for ( int i=4; i>0; i-- )
{
lastsafepos[i] = lastsafepos[i-1];
lastsafeangle[i] = lastsafeangle[i-1];
}
lastsafepos[0] = Owner.pos;
lastsafeangle[0] = Owner.angle;
}
else
{
lastsafevalid = true;
for ( int i=0; i<5; i++ )
{
lastsafepos[i] = Owner.pos;
lastsafeangle[i] = Owner.angle;
}
}
}
safetic = (safetic+1)%35;
}
else safetic = 1;
if ( (Amount <= 0) && (dteleport == 0) ) DepleteOrDestroy();
}
override void AttachToOwner( Actor other )
{
@ -190,6 +255,7 @@ Class GhostPower : PowerInvisibility
Powerup.Strength 100;
Powerup.Mode "Translucent";
Powerup.Color "F0E0FF", 0.1;
+INVENTORY.ADDITIVETIME;
}
override void InitEffect()
@ -291,7 +357,7 @@ Class GhostArtifact : Inventory
if ( pickup && !deathmatch ) return false;
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA);
let g = GhostPower(Owner.FindInventory("GhostPower"));
if ( g ) g.EffectTics = g.default.EffectTics;
if ( g ) g.EffectTics += g.default.EffectTics;
else Owner.GiveInventory("GhostPower",1);
return true;
}
@ -362,6 +428,7 @@ Class GravityPower : Powerup
{
Inventory.Icon "graphics/HUD/Icons/I_Gravity.png";
Powerup.Duration -60;
+INVENTORY.ADDITIVETIME;
}
override void InitEffect()
@ -417,7 +484,7 @@ Class GravitySuppressor : Inventory
if ( pickup && !deathmatch ) return false;
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA);
let g = GravityPower(Owner.FindInventory("GravityPower"));
if ( g ) g.EffectTics = g.default.EffectTics;
if ( g ) g.EffectTics += g.default.EffectTics;
else Owner.GiveInventory("GravityPower",1);
return true;
}
@ -529,6 +596,7 @@ Class InvinciballPower : Powerup
Powerup.Duration -30;
Inventory.Icon "graphics/HUD/Icons/I_Invinciball.png";
Powerup.Color "FF3000", 0.1;
+INVENTORY.ADDITIVETIME;
}
override void InitEffect()
@ -638,7 +706,7 @@ Class FuckingInvinciball : Inventory
let i = InvinciballPower(Owner.FindInventory("InvinciballPower"));
if ( i )
{
i.EffectTics = i.default.EffectTics;
i.EffectTics += i.default.EffectTics;
i.lastpulse = max(i.lastpulse,gametic+35);
SWWMHandler.DoFlash(Owner,Color(96,255,64,0),20);
}
@ -759,6 +827,7 @@ Class RagekitPower : Powerup
Powerup.Duration -30;
Inventory.Icon "graphics/HUD/Icons/I_Ragekit.png";
Powerup.Color "FF0000", 0.2;
+INVENTORY.ADDITIVETIME;
}
override void InitEffect()
@ -793,6 +862,8 @@ Class RagekitPower : Powerup
if ( !(level.maptime%30) )
{
SWWMHandler.DoFlash(Owner,Color(16,255,0,0),5);
if ( Owner.GiveBody(1,100) )
SWWMScoreObj.Spawn(1,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),Font.CR_BLUE);
Owner.A_AlertMonsters(2000);
if ( (Owner.player == players[consoleplayer]) && (gametic > lastrage) && (CVar.GetCVar('swwm_mutevoice',players[consoleplayer]).GetInt() < 2) )
lastrage = SWWMHandler.AddOneliner("ragekit",2,5)+40;
@ -845,6 +916,8 @@ Class RagekitPower : Powerup
void DoHitFX()
{
if ( level.maptime <= lasteffect+5 ) return;
if ( Owner.GiveBody(5,100) )
SWWMScoreObj.Spawn(5,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),Font.CR_BLUE);
Owner.A_AlertMonsters(5000);
SWWMHandler.DoFlash(Owner,Color(64,255,0,0),10);
Owner.A_QuakeEx(8,8,8,Random[Rage](3,8),0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
@ -883,11 +956,12 @@ Class Ragekit : Inventory
{
if ( pickup && !deathmatch ) return false;
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA);
Owner.GiveBody(-100); // full base health
if ( Owner.GiveBody(100,100) )
SWWMScoreObj.Spawn(100,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),Font.CR_BLUE);
let r = RagekitPower(Owner.FindInventory("RagekitPower"));
if ( r )
{
r.EffectTics = r.default.EffectTics;
r.EffectTics += r.default.EffectTics;
SWWMHandler.DoFlash(Owner,Color(64,255,0,0),30);
Owner.A_QuakeEx(8,8,8,20,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:1.);
r.lastpulse = max(r.lastpulse,gametic+35);
@ -1267,6 +1341,8 @@ Class LampMashiro : Actor abstract
// ⢅⢇⢘⢕⢍⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣎⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠿⠛⠛⠛⠉⠁⠀⢀⢜⢕⢅⠕⣡⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠁⠀⠀⠐⡫⡠⠜⣡⣾⣿⣿⡿⠟⠫⠦⠪⠊
// ⢕⢕⢸⣵⣷⣾⣿⣿⣿⣿⣟⢿⣿⣿⣿⣿⣿⣿⣿⣦⣤⡩⣉⣉⡩⣉⣉⠉⠉⠈⠀⠀⠀⠀⠀⠀⠀⢀⣀⡀⠀⠀⣤⢔⢗⢕⠕⣰⣿⣿⣿⣿⣿⣿⣿⣿⡿⢋⢔⢕⠕⣓⠏⣊⠰⠻⠟⠛⣉⠅⢄⠂⡪⡪⡪⠨
//
// ~it actually won't be her, but one can dream~
//
}
Class CompanionLamp : Actor
@ -1695,6 +1771,7 @@ Class BarrierPower : PowerIronFeet
Inventory.Icon "graphics/HUD/Icons/I_Barrier.png";
Powerup.Duration -60;
Powerup.Color "20FF00", 0.1;
+INVENTORY.ADDITIVETIME;
}
override void AbsorbDamage( int damage, Name damageType, out int newdamage )
@ -1741,7 +1818,7 @@ Class EBarrier : Inventory
if ( pickup && !deathmatch ) return false;
if ( pickup && ((Owner.player == players[consoleplayer]) || bBigPowerup) ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA);
let b = BarrierPower(Owner.FindInventory("BarrierPower"));
if ( b ) b.EffectTics = b.default.EffectTics;
if ( b ) b.EffectTics += b.default.EffectTics;
else Owner.GiveInventory("BarrierPower",1);
return true;
}

View file

@ -493,7 +493,7 @@ Class SaltImpact : Actor
}
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( args[0] == 1 ) return StringTable.Localize("$O_WALLBUSTER_BLUE");
if ( args[0] >= 1 ) return StringTable.Localize("$O_WALLBUSTER_BLUE");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
override void PostBeginPlay()
@ -584,7 +584,7 @@ Class SaltBeam : Actor
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( args[1] == 1 ) return StringTable.Localize("$O_WALLBUSTER_BLUE");
if ( args[1] >= 1 ) return StringTable.Localize("$O_WALLBUSTER_BLUE");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
void SpreadOut()
@ -618,7 +618,7 @@ Class SaltBeam : Actor
}
if ( t.Results.HitType != TRACE_HitNone )
{
if ( args[1] ) BusterWall.Bust(t.Results,85+Accuracy*10,target,x,t.Results.HitPos.z);
if ( (args[1] == 2) || swwm_omnibust ) BusterWall.Bust(t.Results,85+Accuracy*10,target,x,t.Results.HitPos.z);
if ( t.Results.HitType == TRACE_HitActor )
{
SWWMUtility.DoKnockback(t.Results.HitActor,x,25000);
@ -1329,7 +1329,7 @@ Class TheBall : Actor
}
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
{
if ( special1 == 1 ) return StringTable.Localize("$O_WALLBUSTER_PURPLE");
if ( special1 >= 1 ) return StringTable.Localize("$O_WALLBUSTER_PURPLE");
return Super.GetObituary(victim,inflictor,mod,playerattack);
}
override int SpecialMissileHit( Actor victim )
@ -1451,7 +1451,6 @@ Class TheBall : Actor
lasthit = null;
Vector3 HitNormal = -vel.unit();
F3DFloor ff;
LineTracer faketracer = new("LineTracer");
if ( BlockingFloor )
{
// find closest 3d floor for its normal
@ -1462,14 +1461,8 @@ Class TheBall : Actor
ff = BlockingFloor.Get3DFloor(i);
break;
}
if ( ff )
{
faketracer.Results.ffloor = ff;
HitNormal = -ff.top.Normal;
}
if ( ff ) HitNormal = -ff.top.Normal;
else HitNormal = BlockingFloor.floorplane.Normal;
faketracer.Results.HitType = TRACE_HitFloor;
faketracer.Results.HitSector = BlockingFloor;
}
else if ( BlockingCeiling )
{
@ -1481,14 +1474,8 @@ Class TheBall : Actor
ff = BlockingCeiling.Get3DFloor(i);
break;
}
if ( ff )
{
faketracer.Results.ffloor = ff;
HitNormal = -ff.bottom.Normal;
}
if ( ff ) HitNormal = -ff.bottom.Normal;
else HitNormal = BlockingCeiling.ceilingplane.Normal;
faketracer.Results.HitType = TRACE_HitCeiling;
faketracer.Results.HitSector = BlockingCeiling;
}
else if ( BlockingLine )
{
@ -1502,30 +1489,17 @@ Class TheBall : Actor
if ( (!locknum || (target && target.CheckKeys(locknum,false,true))) && !SWWMUtility.IsExitLine(BlockingLine) )
BlockingLine.RemoteActivate(target,wside,SPAC_Use,pos);
}
faketracer.Results.HitType = TRACE_HitWall;
faketracer.Results.HitLine = BlockingLine;
faketracer.Results.Side = wside;
faketracer.Results.Tier = TIER_Middle;
// guess the tier hit
if ( BlockingLine.sidedef[1] )
{
double ceil = BlockingLine.sidedef[!wside].sector.ceilingplane.ZAtPoint(pos.xy);
double flor = BlockingLine.sidedef[!wside].sector.floorplane.ZAtPoint(pos.xy);
if ( pos.z >= ceil ) faketracer.Results.Tier = TIER_Upper;
else if ( (pos.z+Height) <= flor ) faketracer.Results.Tier = TIER_Lower;
}
}
else if ( BlockingMobj )
{
Vector3 diff = level.Vec3Diff(BlockingMobj.Vec3Offset(0,0,BlockingMobj.Height/2),pos);
HitNormal = diff.unit();
faketracer.Results.HitType = TRACE_HitActor;
}
// send the needed data for a bust
if ( special1 == 1 )
if ( (special1 == 2) || swwm_omnibust )
{
int dmg = int(oldvel.length()*4.2+heat*80);
BusterWall.Bust(faketracer.Results,dmg,target,oldvel.unit(),pos.z+Height/2.);
BusterWall.ProjectileBust(self,dmg,oldvel.unit());
}
// undo the bounce, we need to hook in our own
vel = oldvel;
@ -2100,6 +2074,13 @@ Class Spreadgun : SWWMWeapon
action void ProcessTraceHit( SpreadgunTracer t, Vector3 origin, Vector3 dir, int dmg, double mm, Class<Actor> impact = "SpreadImpact", int bc = 1, bool large = false )
{
if ( swwm_omnibust )
{
// Wall busting
int bustdmg = dmg;
if ( t is 'SpreadSlugTracer' ) bustdmg = int(SpreadSlugTracer(t).penetration);
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

@ -257,7 +257,7 @@ Class BigBiospark : Actor
if ( !t || !t.bSHOOTABLE ) continue;
if ( SWWMUtility.SphereIntersect(t,pos,40) )
{
t.DamageMobj(self,target,4+special1*4,'Electric');
t.DamageMobj(self,target,4+special1,'Electric');
Health--;
}
if ( SWWMUtility.SphereIntersect(t,pos,120) )
@ -268,7 +268,7 @@ Class BigBiospark : Actor
if ( dist > 4. ) SWWMUtility.DoKnockback(t,-dirto,clamp(120.-dist,0.,120.)*100);
}
}
Health -= 2;
Health -= 3;
if ( Health <= 0 ) ExplodeMissile();
}
@ -276,7 +276,7 @@ Class BigBiospark : Actor
{
A_StopSound(CHAN_VOICE);
A_AlertMonsters(15000);
SWWMUtility.DoExplosion(self,450,200000,300,100);
SWWMUtility.DoExplosion(self,250,200000,250,100);
A_QuakeEx(9,9,9,30,0,1400,"",QF_RELATIVE|QF_SCALEDOWN,falloff:500,rollIntensity:1.5);
A_StartSound("biospark/bighit",CHAN_ITEM,attenuation:.4);
A_StartSound("biospark/bighit",CHAN_WEAPON,attenuation:.3);
@ -330,7 +330,7 @@ Class BigBiospark : Actor
if ( special1 > 40 ) return;
double factor = (40-special1)/40.;
double invfct = 1.-factor;
SWWMUtility.DoExplosion(self,150*factor,0.,250*invfct);
SWWMUtility.DoExplosion(self,80*factor,0.,250*invfct);
SWWMUtility.DoExplosion(self,0,-8000*factor,500*invfct);
int numpt = int(Random[ExploS](16,32)*factor);
for ( int i=0; i<numpt; i++ )
@ -536,7 +536,7 @@ Class BiosparkBall : Actor
{
A_StopSound(CHAN_VOICE);
A_AlertMonsters(5000);
SWWMUtility.DoExplosion(self,40,120000,150,80);
SWWMUtility.DoExplosion(self,30,120000,150,80);
A_QuakeEx(6,6,6,16,0,800,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.8);
A_StartSound("biospark/hit",CHAN_ITEM,attenuation:.8);
A_StartSound("biospark/hit",CHAN_WEAPON,attenuation:.6);
@ -581,6 +581,7 @@ Class BiosparkBall : Actor
s.ReactionTime += Random[Sparkster](0,10);
}
Spawn("BiosparkExplLight",pos);
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,30,(cos(angle)*cos(pitch),sin(angle)*cos(pitch),sin(-pitch)));
}
void A_SparkSubExpl()
{
@ -588,7 +589,7 @@ Class BiosparkBall : Actor
if ( special1 > 30 ) return;
double factor = (30-special1)/30.;
double invfct = 1.-factor;
SWWMUtility.DoExplosion(self,20*factor,0.,150*invfct);
SWWMUtility.DoExplosion(self,10*factor,0.,150*invfct);
SWWMUtility.DoExplosion(self,0,-5000*factor,300*invfct);
int numpt = int(Random[ExploS](16,32)*factor);
for ( int i=0; i<numpt; i++ )
@ -699,7 +700,7 @@ Class BiosparkBeamImpact : Actor
{
Super.PostBeginPlay();
A_AlertMonsters(2000);
SWWMUtility.DoExplosion(self,30,120000,100,40);
SWWMUtility.DoExplosion(self,25,120000,100,40);
A_QuakeEx(3,3,3,12,0,800,"",QF_RELATIVE|QF_SCALEDOWN,falloff:300,rollIntensity:.4);
A_StartSound("biospark/beamhit",CHAN_ITEM,attenuation:1.1);
A_StartSound("biospark/beamhit",CHAN_WEAPON,attenuation:.8);
@ -845,7 +846,7 @@ Class BiosparkComboImpactSub : Actor
{
Super.PostBeginPlay();
if ( !bAMBUSH ) return;
SWWMUtility.DoExplosion(self,100,10000,300,120);
SWWMUtility.DoExplosion(self,30,10000,300,120);
}
override void Tick()
{
@ -901,7 +902,7 @@ Class BiosparkComboImpact : Actor
{
Super.PostBeginPlay();
A_AlertMonsters(6000);
SWWMUtility.DoExplosion(self,bAMBUSH?(700+Args[0]*100):300,bAMBUSH?480000:200000,bAMBUSH?800:400,bAMBUSH?400:200);
SWWMUtility.DoExplosion(self,bAMBUSH?(200+Args[0]*25):150,bAMBUSH?480000:200000,bAMBUSH?500:300,bAMBUSH?350:150);
A_QuakeEx(9,9,9,25,0,2000,"",QF_RELATIVE|QF_SCALEDOWN,falloff:600,rollIntensity:1.5);
A_StartSound("biospark/bighit",CHAN_ITEM,attenuation:.4);
A_StartSound("biospark/bighit",CHAN_WEAPON,attenuation:.3);
@ -969,8 +970,8 @@ Class BiosparkComboImpact : Actor
if ( special1 > mx ) return;
double factor = (mx-special1)/mx;
double invfct = 1.-factor;
SWWMUtility.DoExplosion(self,(bAMBUSH?(400+Args[0]*40):100)*factor,0.,(bAMBUSH?900.:300.)*invfct);
SWWMUtility.DoExplosion(self,0.,-12000*factor,(bAMBUSH?1500:500)*invfct);
SWWMUtility.DoExplosion(self,(bAMBUSH?(80+Args[0]*10):50)*factor,0.,(bAMBUSH?600.:400.)*invfct);
SWWMUtility.DoExplosion(self,0.,-12000*factor,(bAMBUSH?800:600)*invfct);
int numpt = int(Random[ExploS](8,16)*factor);
for ( int i=0; i<numpt; i++ )
{
@ -1023,7 +1024,7 @@ Class BiosparkComboImpact : Actor
if ( (special2 > mx) || !(special2%2) ) return;
int numpt = Random[Sparkster](0,mx-special2);
double ang, pt;
double mul = bAMBUSH?8:3;
double mul = bAMBUSH?9:5;
for ( int i=0; i<numpt; i++ )
{
ang = FRandom[Sparkster](0,360);
@ -1160,6 +1161,7 @@ Class BiosparkBeam : Actor
s.angle = atan2(normal.y,normal.x);
s.pitch = asin(-normal.z);
bAMBUSH = true;
if ( swwm_omnibust ) BusterWall.Bust(t.Results,GetMissileDamage(0,0),target,t.Results.HitVector,t.Results.HitPos.z);
return;
}
double closest = double.infinity;
@ -1583,7 +1585,7 @@ Class BiosparkArc : Actor
{
Obituary "$O_SPARKSTER";
RenderStyle "Add";
DamageFunction 5;
DamageFunction 3;
ReactionTime 12;
Speed 16;
Radius .1;
@ -1631,7 +1633,7 @@ Class BiosparkArcBig : BiosparkArc
Default
{
ReactionTime 10;
DamageFunction 100;
DamageFunction 10;
Speed 64;
}
States

View file

@ -184,6 +184,7 @@ Class ExplodiumMagProj : Actor
s.target = target;
}
Spawn("ExploLight2",pos);
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,20+15*special1,cvel);
}
States
{
@ -414,6 +415,7 @@ Class ExplodiumGun : SWWMWeapon
b.angle = atan2(hitnormal.y,hitnormal.x);
b.pitch = asin(-hitnormal.z);
b.target = self;
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,50,self,d.HitDir,d.HitLocation.z);
}
for ( int i=0; i<6; i++ )
{
@ -523,7 +525,6 @@ Class ExplodiumGun : SWWMWeapon
Weapon.UpSound "explodium/select";
Weapon.SlotNumber 2;
Weapon.SelectionOrder 1000;
Weapon.SlotPriority 1.;
Inventory.MaxAmount 2;
Weapon.SisterWeapon "DualExplodiumGun";
Stamina 8000;
@ -805,6 +806,7 @@ Class DualExplodiumGun : SWWMWeapon
b.angle = atan2(hitnormal.y,hitnormal.x);
b.pitch = asin(-hitnormal.z);
b.target = self;
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,50,self,d.HitDir,d.HitLocation.z);
}
for ( int i=0; i<6; i++ )
{

View file

@ -453,8 +453,8 @@ Class CandyMagArmBig : CandyMagArm
override void PostBeginPlay()
{
Super.PostBeginPlay();
reactiontime = Random[ExploS](10,16);
vel *= FRandom[ExploS](.8,1.5);
reactiontime = Random[ExploS](10,20);
vel *= FRandom[ExploS](.8,2.);
}
States
{
@ -540,9 +540,9 @@ Class CandyGunProj : Actor
pitch = asin(-cvel.z);
bNOGRAVITY = true;
A_SetRenderStyle(1.,STYLE_Add);
Scale *= 6.+.2*special1;
Scale *= 7.+.2*special1;
A_AlertMonsters(40000);
SWWMUtility.DoExplosion(self,4000+900*special1,80000+15000*special1,300+30*special1,200,DE_EXTRAZTHRUST);
SWWMUtility.DoExplosion(self,5000+900*special1,80000+15000*special1,500+30*special1,300,DE_EXTRAZTHRUST);
A_QuakeEx(9,9,9,70,0,1500+100*special1,"",QF_RELATIVE|QF_SCALEDOWN,falloff:1200,rollintensity:2.);
A_StartSound("candygun/gunhit",CHAN_VOICE,attenuation:.24);
A_StartSound("candygun/gunhit",CHAN_WEAPON,attenuation:.12);
@ -575,7 +575,7 @@ Class CandyGunProj : Actor
let s = Spawn("SWWMChip",pos);
s.vel = pvel;
}
numpt = int(Random[ExploS](2,4)*(1.+.4*special1));
numpt = int(Random[ExploS](2,6)*(1.+.4*special1));
for ( int i=0; i<numpt; i++ )
{
let s = Spawn("CandyMagArmBig",pos);
@ -583,6 +583,7 @@ Class CandyGunProj : Actor
}
Spawn("CandyLight3",pos);
Spawn("CandyRing3",pos);
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,5000+900*special1,cvel);
}
States
{
@ -603,7 +604,7 @@ Class CandyGunProj : Actor
{
double ang = FRandom[ExploS](0,360);
double pt = FRandom[ExploS](-90,90);
let s = Spawn((frame>10)?"CandyBeam":"TinyCandyBeam",pos);
let s = Spawn((frame<10)?"CandyBeam":"TinyCandyBeam",pos);
s.target = target;
s.angle = ang;
s.pitch = pt;
@ -698,6 +699,7 @@ Class CandyMagProj : Actor
}
Spawn("CandyLight2",pos);
Spawn("CandyRing2",pos);
if ( swwm_omnibust ) BusterWall.ProjectileBust(self,1000+900*special1,cvel);
}
States
{
@ -1024,6 +1026,7 @@ Class CandyGun : SWWMWeapon
b.angle = atan2(hitnormal.y,hitnormal.x);
b.pitch = asin(-hitnormal.z);
b.target = self;
if ( swwm_omnibust ) BusterWall.BustLinetrace(d,1700,self,d.HitDir,d.HitLocation.z);
}
for ( int i=0; i<6; i++ )
{

View file

@ -161,9 +161,11 @@ Class SilverImpact : Actor
Class WallPenetrate
{
Vector3 hitpos, hitdir, hitnormal;
Vector3 hitpos, hitdir, hitnormal, bustdir;
int hitside, hittier, hittype, penetration;
Line hitline;
int hitside;
Sector hitsector;
F3DFloor hitffloor;
}
Class AuxiliarySilverBulletTracer : LineTracer
@ -237,25 +239,38 @@ Class SilverBulletTracer : SpreadSlugTracer
Vector3 ofs = level.Vec3Offset(Results.HitPos,Results.HitVector*i);
if ( level.IsPointInLevel(ofs) )
{
penetration = max(0,penetration-i*4);
let wp = new("WallPenetrate");
wp.hittype = Results.HitType;
wp.hitline = Results.HitLine;
wp.hitside = Results.Side;
wp.hittier = Results.Tier;
wp.hitsector = Results.HitSector;
wp.hitffloor = Results.ffloor;
wp.hitpos = Results.HitPos;
wp.hitdir = Results.HitVector;
wp.bustdir = Results.HitVector;
wp.penetration = int(penetration);
wp.hitnormal = (-Results.HitLine.delta.y,Results.HitLine.delta.x,0).unit();
if ( !Results.Side ) wp.hitnormal *= -1;
WallPenetrateList.Push(wp);
penetration = max(0,penetration-i*4);
// trace backwards to find exit surface
let at = new("AuxiliarySilverBulletTracer");
at.Trace(ofs,level.PointInSector(ofs.xy),-Results.HitVector,2.,TRACE_NoSky);
if ( at.Results.HitType != TRACE_HitNone )
{
let wp2 = new("WallPenetrate");
wp2.hittype = at.Results.HitType;
wp2.hitline = at.Results.HitLine;
wp2.hitside = at.Results.Side;
wp2.hittier = at.Results.Tier;
wp2.hitsector = at.Results.HitSector;
wp2.hitffloor = at.Results.ffloor;
wp2.hitside = at.Results.Side;
wp2.hitpos = at.Results.HitPos;
wp2.hitdir = at.Results.HitVector;
wp2.bustdir = -at.Results.HitVector;
wp2.penetration = int(penetration);
if ( at.Results.HitType == TRACE_HitWall )
{
wp2.hitnormal = (-at.Results.HitLine.delta.y,at.Results.HitLine.delta.x,0).unit();
@ -284,12 +299,17 @@ Class SilverBulletTracer : SpreadSlugTracer
Vector3 ofs = level.Vec3Offset(Results.HitPos,Results.HitVector*i);
if ( level.IsPointInLevel(ofs) )
{
penetration = max(0,penetration-i*4);
let wp = new("WallPenetrate");
wp.hittype = Results.HitType;
wp.hitline = Results.HitLine;
wp.hitside = Results.Side;
wp.hittier = Results.Tier;
wp.hitsector = Results.HitSector;
wp.hitffloor = Results.ffloor;
wp.hitpos = Results.HitPos;
wp.hitdir = Results.HitVector;
wp.bustdir = Results.HitVector;
wp.penetration = int(penetration);
if ( Results.HitType == TRACE_HitWall )
{
wp.hitnormal = (-Results.HitLine.delta.y,Results.HitLine.delta.x,0).unit();
@ -301,16 +321,24 @@ Class SilverBulletTracer : SpreadSlugTracer
else if ( Results.HitType == TRACE_HitFloor )
wp.hitnormal = Results.HitSector.floorplane.Normal;
WallPenetrateList.Push(wp);
penetration = max(0,penetration-i*4);
// trace backwards to find exit surface
let at = new("AuxiliarySilverBulletTracer");
at.Trace(ofs,level.PointInSector(ofs.xy),-Results.HitVector,2.,TRACE_NoSky);
if ( at.Results.HitType != TRACE_HitNone )
{
let wp2 = new("WallPenetrate");
wp2.hittype = at.Results.HitType;
wp2.hitline = at.Results.HitLine;
wp2.hitside = at.Results.Side;
wp2.hittier = at.Results.Tier;
wp2.hitsector = at.Results.HitSector;
wp2.hitffloor = at.Results.ffloor;
wp2.hitside = at.Results.Side;
wp2.hitpos = at.Results.HitPos;
wp2.hitdir = at.Results.HitVector;
wp2.bustdir = -at.Results.HitVector;
wp2.penetration = int(penetration);
if ( at.Results.HitType == TRACE_HitWall )
{
wp2.hitnormal = (-at.Results.HitLine.delta.y,at.Results.HitLine.delta.x,0).unit();
@ -776,6 +804,7 @@ Class SilverBullet : SWWMWeapon
p.target = self;
}
}
LineTracer faketracer = new("LineTracer");
for ( int i=0; i<t.WallPenetrateList.Size(); i++ )
{
Vector3 hitpos = t.WallPenetrateList[i].hitpos;
@ -784,8 +813,18 @@ Class SilverBullet : SWWMWeapon
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.target = self;
if ( t.WallPenetrateList[i].hitline )
if ( t.WallPenetrateList[i].hittype == TRACE_HitWall )
t.WallPenetrateList[i].hitline.Activate(self,t.WallPenetrateList[i].hitside,SPAC_Impact);
if ( swwm_omnibust )
{
faketracer.Results.HitType = t.WallPenetrateList[i].HitType;
faketracer.Results.HitSector = t.WallPenetrateList[i].HitSector;
faketracer.Results.HitLine = t.WallPenetrateList[i].HitLine;
faketracer.Results.ffloor = t.WallPenetrateList[i].HitFFloor;
faketracer.Results.Side = t.WallPenetrateList[i].HitSide;
faketracer.Results.Tier = t.WallPenetrateList[i].HitTier;
BusterWall.Bust(faketracer.Results,t.WallPenetrateList[i].penetration,self,t.WallPenetrateList[i].BustDir,t.WallPenetrateList[i].HitPos.z);
}
}
if ( (t.Results.HitType != TRACE_HitNone) && (t.Results.HitType != TRACE_HasHitSky) && (t.Results.HitType != TRACE_HitActor) )
{
@ -810,6 +849,7 @@ Class SilverBullet : SWWMWeapon
p.pitch = asin(-hitnormal.z);
p.target = self;
if ( t.Results.HitType == TRACE_HitWall ) t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos);
if ( swwm_omnibust ) BusterWall.Bust(t.Results,int(t.penetration),self,t.Results.HitVector,t.Results.HitPos.z);
}
for ( int i=0; i<t.Results.Distance; i+=16 )
{
@ -872,16 +912,14 @@ Class SilverBullet : SWWMWeapon
}
}
else if ( t.Results.HitType == TRACE_HitWall )
{
t.Results.HitLine.RemoteActivate(self,t.Results.Side,SPAC_Impact,t.Results.HitPos);
BusterWall.Bust(t.Results,invoker.proneme?2500:2000,self,t.Results.HitVector,t.Results.HitPos.z);
}
let p = Spawn("FatChodeImpact",t.Results.HitPos+hitnormal*4);
p.angle = atan2(hitnormal.y,hitnormal.x);
p.pitch = asin(-hitnormal.z);
p.target = self;
FatChodeImpact(p).realangle = atan2(t.Results.HitVector.y,t.Results.HitVector.x);
FatChodeImpact(p).realpitch = asin(-t.Results.HitVector.z);
BusterWall.Bust(t.Results,invoker.proneme?2500:2000,self,t.Results.HitVector,t.Results.HitPos.z);
}
for ( int i=0; i<t.Results.Distance; i+=16 )
{

View file

@ -97,7 +97,7 @@ Class SWWMStats : Thinker
lstats.Push(ls);
}
void AddWeaponKill( Actor inflictor, Actor victim )
void AddWeaponKill( Actor inflictor, Actor victim, Name damagetype )
{
if ( victim )
{
@ -132,6 +132,17 @@ Class SWWMStats : Thinker
else if ( (inflictor is 'CandyBeam') || (inflictor is 'CandyPop') || (inflictor is 'CandyMagArm') || (inflictor is 'CandyGunProj') || (inflictor is 'CandyMagProj') || (inflictor is 'CandyBulletImpact') ) which = 'CandyGun';
else if ( (inflictor is 'YnykronBeam') || (inflictor is 'YnykronImpact') || (inflictor is 'YnykronSingularity') || (inflictor is 'YnykronCloud') || (inflictor is 'YnykronVoidBeam') || (inflictor is 'YnykronLightningArc') || (inflictor is 'YnykronLightningImpact') ) which = 'Ynykron';
else if ( (inflictor is 'Demolitionist') || (inflictor is 'DemolitionistShockwave') || (inflictor is 'DemolitionistRadiusShockwave') ) which = 'SWWMWeapon'; // hack to assume Demolitionist as weapon
else if ( inflictor is 'BigPunchSplash' )
{
// guess from damagetype
if ( (damagetype == 'Jump') || (damagetype == 'Dash') || (damagetype == 'Buttslam') || (damagetype == 'GroundPound') )
which = 'SWWMWeapon';
else if ( damagetype == 'Love' )
which = 'SWWMGesture';
// others are just weapon melee, so keep the readyweapon
}
if ( damagetype == 'Falling' )
which = 'Weapon'; // the gross hacks continue
if ( !which ) return;
for ( int i=0; i<wstats.Size(); i++ )
{
@ -341,6 +352,15 @@ Class SWWMLoreLibrary : Thinker
if ( ref ~== "TheOrder" ) return true;
if ( ref ~== "TheFront" ) return true;
}
// restrict dlc weaponset until that's done
if ( ref ~== "ItamexHammer" ) return true;
if ( ref ~== "PuntzerBeta" ) return true;
if ( ref ~== "PuntzerGamma" ) return true;
if ( ref ~== "HeavyMahSheenGun" ) return true;
if ( ref ~== "Quadravol" ) return true;
if ( ref ~== "BlackfireIgniter" ) return true;
if ( ref ~== "EMPCarbine" ) return true;
if ( ref ~== "RayKhom" ) return true;
// check if entry is for a collectable
for ( int i=0; i<AllActorClasses.Size(); i++ )
{
@ -369,7 +389,7 @@ Class SWWMLoreLibrary : Thinker
else if ( text ~== "SWWM_LORETXT_HELL" )
text = "SWWM_LORETXT_HELL3"; // met father nostros during the wedding
else if ( text ~== "SWWM_LORETXT_GHOULHUNT" )
text = "SWWM_LORETXT_GHOULHUNT2"; // met anthon anderken during the
text = "SWWM_LORETXT_GHOULHUNT2"; // met anthon anderken during the wedding
else if ( text ~== "SWWM_LORETXT_RAGEKIT" )
text = "SWWM_LORETXT_RAGEKIT2"; // kirin's reactions to demo using this item
else if ( text ~== "SWWM_LORETXT_SANKAIDERIHA" )
@ -382,6 +402,8 @@ Class SWWMLoreLibrary : Thinker
text = "SWWM_LORETXT_ZANAVETH22"; // met at wedding
else if ( text ~== "SWWM_LORETXT_YNYKRON" )
text = "SWWM_LORETXT_YNYKRON2"; // confirmed to harm (but not kill) gods
else if ( text ~== "SWWM_LORETXT_AKARIPROJECT" )
text = "SWWM_LORETXT_AKARIPROJECT3"; // mentions kirin
}
if ( gameinfo.gametype&(GAME_Raven|GAME_Strife) )
{
@ -403,6 +425,8 @@ Class SWWMLoreLibrary : Thinker
text = "SWWM_LORETXT_ZANAVETH32"; // iagb happened
else if ( text ~== "SWWM_LORETXT_BIGSHOT" )
text = "SWWM_LORETXT_BIGSHOT2"; // predictions about crimes_m
else if ( text ~== "SWWM_LORETXT_AKARIPROJECT" )
text = "SWWM_LORETXT_AKARIPROJECT2"; // fiction becomes reality
}
// check that it's valid
if ( StringTable.Localize(tag,false) == tag ) return false;
@ -755,12 +779,12 @@ Class SWWMCombatTracker : Thinker
bool legged;
int tcnt;
double height;
transient CVar funtags;
transient CVar funtags, maxdist;
void UpdateTag()
{
if ( !funtags ) funtags = CVar.GetCVar('swwm_funtags',players[consoleplayer]);
if ( mytarget && (mytarget.player || mytarget.bISMONSTER || (mytarget is 'BossBrain')) )
if ( mytarget && (mytarget.player || mytarget.bISMONSTER || (mytarget is 'BossBrain') || (mytarget is 'SWWMHangingKeen')) )
{
String realtag = funtags.GetBool()?SWWMUtility.GetFunTag(mytarget,FallbackTag):mytarget.GetTag(FallbackTag);
if ( realtag == FallbackTag )
@ -879,22 +903,25 @@ Class SWWMCombatTracker : Thinker
}
lasthealth = mytarget.Health;
intp.Update(lasthealth);
if ( (mytarget.bISMONSTER || mytarget.player) && !mytarget.bINVISIBLE )
if ( !maxdist ) maxdist = CVar.GetCVar('swwm_maxtargetdist',players[consoleplayer]);
if ( (mytarget.bISMONSTER || mytarget.player) && !mytarget.bINVISIBLE && !mytarget.bCORPSE )
{
bool straifu = false;
if ( (gameinfo.gametype&GAME_Strife) && (!mytarget.bINCOMBAT && !mytarget.bJUSTATTACKED) || (mytarget is 'Beggar') || (mytarget is 'Peasant') )
straifu = true;
// enemies within 2000mu that have us as target
if ( !straifu && mytarget.target && (mytarget.target.Health > 0) && (mytarget.target.player == players[consoleplayer]) && (mytarget.Vec3To(mytarget.target).length() < 2000) && mytarget.CheckSight(mytarget.target,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime+70;
int mxdist = maxdist.GetInt();
// enemies within a set distance that have us as target
if ( !straifu && mytarget.target && (mytarget.target.Health > 0) && (mytarget.target.player == players[consoleplayer]) && ((mxdist <= 0) || (mytarget.Vec3To(mytarget.target).length() < mxdist)) && mytarget.CheckSight(mytarget.target,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime+70;
// players (but not voodoo dolls), always visible in sp/coop
if ( !deathmatch && mytarget.player && (mytarget.player.mo == mytarget) ) updated = level.maptime+35;
// any visible enemies within 600mu
if ( (mytarget.Vec3To(players[consoleplayer].mo).length() < 600) && players[consoleplayer].mo.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime;
// any visible enemies within one quarter of the set distance
if ( ((mxdist <= 0) || (mytarget.Vec3To(players[consoleplayer].mo).length() < (mxdist/4))) && players[consoleplayer].mo.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime;
}
else if ( mytarget is 'BossBrain' )
else if ( (mytarget is 'BossBrain') || (mytarget is 'SWWMHangingKeen') )
{
// teh romero, only if visible
if ( (mytarget.Vec3To(players[consoleplayer].mo).length() < 600) && players[consoleplayer].mo.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime;
int mxdist = maxdist.GetInt();
// special stuff, only if visible
if ( ((mxdist <= 0) || (mytarget.Vec3To(players[consoleplayer].mo).length() < (mxdist/4))) && players[consoleplayer].mo.CheckSight(mytarget,SF_IGNOREVISIBILITY|SF_IGNOREWATERBOUNDARY) ) updated = level.maptime;
}
}
}

View file

@ -474,6 +474,7 @@ Class SWWMUtility
basetag = "WOLFSS";
break;
case 'CommanderKeen':
case 'SWWMHangingKeen':
basetag = "KEEN";
break;
case 'MBFHelperDog':