Partial implementation of Fuck Your Shit rounds, currently in progress. Added various shader effects to some powerups, and to player damage. Added custom view effects to player death, disabled "face attacker" because it looks weird with model-based players. Added "untouchable" spree tracking to the Stats tab. Implemented "emergency reboot system" for people who want a less shameful form of the Resurrect cheat. Cooldown for consecutive reboots can be configured. Rebalanced armors. Small language string corrections. Adjusted pickup model sizes of some weapons. Fixed missing punch sound (damn typos). Fix targetter always displaying voodoo dolls. Fix uptime breaking when loading saves, now based on total playtime rather than gametic. Readjusted Spreadgun ammo availability. Flush HUD interpolators alongside messages, fixes things such as the score VERY slowly counting up when loading a save. Spare armors now only get auto-used on pickup if there is NO armor available of that type. Added some extra visual effects to punching walls and non-bleeding actors. Slightly altered the melee range so it's not as awkward. Fixed punching not using flesh sounds for bleeding actors. Pusher primary now drags the player towards their target, like the Chainsaw. Fixed the player having no pain sounds whatsoever. Fixed stair step anchoring not working. The damage dealt when walljumping on a monster now also gets boosted by the Ragekit.
670 lines
No EOL
18 KiB
Text
670 lines
No EOL
18 KiB
Text
// Inventory stuff
|
|
Mixin Class SWWMAutoUseFix
|
|
{
|
|
override bool HandlePickup( Inventory item )
|
|
{
|
|
bool res = Super.HandlePickup(item);
|
|
if ( res && (item.GetClass() == item.GetClass()) )
|
|
{
|
|
if ( Use(true) ) Amount--;
|
|
if ( Amount <= 0 ) DepleteOrDestroy();
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// Base class for all SWWM Armors
|
|
Class SWWMArmor : Armor abstract
|
|
{
|
|
int priority;
|
|
String drainmsg;
|
|
Class<SWWMSpareArmor> parent;
|
|
|
|
Property ArmorPriority : priority;
|
|
Property DrainMessage : drainmsg;
|
|
Property GiverArmor : parent;
|
|
|
|
Default
|
|
{
|
|
+INVENTORY.AUTOACTIVATE;
|
|
+INVENTORY.UNTOSSABLE;
|
|
+INVENTORY.UNDROPPABLE;
|
|
+INVENTORY.KEEPDEPLETED;
|
|
+INVENTORY.ALWAYSPICKUP;
|
|
}
|
|
override void AttachToOwner( Actor other )
|
|
{
|
|
Super.AttachToOwner(other);
|
|
// find last armor that's better than us
|
|
Inventory found = null;
|
|
for ( Inventory i=other.Inv; i; i=i.Inv )
|
|
{
|
|
if ( !(i is 'SWWMArmor') || (i == self) || (SWWMArmor(i).priority < priority) ) continue;
|
|
found = i;
|
|
}
|
|
if ( !found ) return;
|
|
// place ourselves right after it
|
|
Inventory saved = found.Inv;
|
|
found.Inv = self;
|
|
other.Inv = Inv;
|
|
Inv = saved;
|
|
}
|
|
// for subclasses
|
|
virtual int HandleDamage( int damage, Name damageType, int flags )
|
|
{
|
|
return damage;
|
|
}
|
|
override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags )
|
|
{
|
|
if ( !passive ) return;
|
|
int saved;
|
|
if ( (amount > 0) && !DamageTypeDefinition.IgnoreArmor(damageType) && (damage > 0) )
|
|
{
|
|
SWWMHandler.DoFlash(Owner,Color(int(clamp(damage*.15,1,16)),255,224,192),3);
|
|
Owner.A_StartSound("armor/hit",CHAN_BODY,CHANF_DEFAULT,clamp(damage*.03,0.,1.),2.5);
|
|
saved = HandleDamage(damage,damageType,flags);
|
|
if ( amount <= saved ) saved = amount;
|
|
newdamage -= saved;
|
|
if ( newdamage < 0 ) Owner.GiveBody(abs(newdamage));
|
|
amount -= saved;
|
|
damage = newdamage;
|
|
if ( amount <= 0 )
|
|
{
|
|
if ( damage > 0 ) newdamage = ApplyDamageFactors(GetClass(),damageType,damage,damage);
|
|
if ( Owner.CountInv(parent) > 0 )
|
|
{
|
|
Amount = default.Amount;
|
|
if ( GetDefaultByType(parent).UseSound ) Owner.A_StartSound(GetDefaultByType(parent).UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6);
|
|
Owner.TakeInventory(parent,1);
|
|
}
|
|
else
|
|
{
|
|
if ( Owner.CheckLocalView() && (drainmsg != "") ) Console.Printf(StringTable.Localize(drainmsg));
|
|
DepleteOrDestroy();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if ( damage > 0 ) newdamage = ApplyDamageFactors(GetClass(),damageType,damage,damage);
|
|
}
|
|
}
|
|
|
|
// gives armor when used
|
|
Class SWWMSpareArmor : Inventory abstract
|
|
{
|
|
Mixin SWWMAutoUseFix;
|
|
|
|
Class<SWWMArmor> giveme;
|
|
|
|
Property GiveArmor : giveme;
|
|
|
|
override bool Use( bool pickup )
|
|
{
|
|
bool shouldautouse = false;
|
|
if ( swwm_enforceautousearmor == 1 ) shouldautouse = true;
|
|
else if ( swwm_enforceautousearmor == -1 ) shouldautouse = false;
|
|
else shouldautouse = CVar.GetCVar('swwm_autousearmor',Owner.player).GetBool();
|
|
if ( pickup && !shouldautouse ) return false;
|
|
let cur = Owner.FindInventory(giveme);
|
|
if ( !cur || (cur.Amount <= 0) )
|
|
{
|
|
Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount);
|
|
if ( UseSound ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6);
|
|
SWWMHandler.DoFlash(Owner,Color(48,96,255,64),5);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Default
|
|
{
|
|
+INVENTORY.INVBAR;
|
|
+INVENTORY.ISARMOR;
|
|
+INVENTORY.AUTOACTIVATE;
|
|
Inventory.MaxAmount 5;
|
|
Inventory.InterHubAmount 5;
|
|
+FLOATBOB;
|
|
FloatBobStrength 0.25;
|
|
}
|
|
}
|
|
|
|
Class SWWMHealth : Inventory abstract
|
|
{
|
|
Mixin SWWMAutoUseFix;
|
|
|
|
// can't use the Health class for whatever reason
|
|
// nice parser you got there I guess?
|
|
Class<Inventory> giveme;
|
|
|
|
Property GiveHealth : giveme;
|
|
|
|
override bool Use( bool pickup )
|
|
{
|
|
bool shouldautouse = false;
|
|
if ( swwm_enforceautousehealth == 1 ) shouldautouse = true;
|
|
else if ( swwm_enforceautousehealth == -1 ) shouldautouse = false;
|
|
else shouldautouse = CVar.GetCVar('swwm_autousehealth',Owner.player).GetBool();
|
|
if ( pickup && !shouldautouse ) return false;
|
|
if ( Owner.Health >= GetDefaultByType(giveme).MaxAmount ) return false;
|
|
if ( UseSound ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6);
|
|
SWWMHandler.DoFlash(Owner,Color(48,64,128,255),5);
|
|
Owner.GiveInventory(giveme,GetDefaultByType(giveme).Amount);
|
|
SWWMScoreObj.Spawn(GetDefaultByType(giveme).Amount,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2),Font.CR_GREEN);
|
|
return true;
|
|
}
|
|
|
|
virtual void AutoUseExtra()
|
|
{
|
|
}
|
|
|
|
override void DoEffect()
|
|
{
|
|
Super.DoEffect();
|
|
if ( Amount <= 0 ) DepleteOrDestroy();
|
|
}
|
|
|
|
override void ModifyDamage( int damage, Name damageType, out int newdamage, bool passive, Actor inflictor, Actor source, int flags )
|
|
{
|
|
if ( passive && (Owner.Health-damage <= 0) )
|
|
{
|
|
if ( UseSound ) Owner.A_StartSound(UseSound,CHAN_ITEMEXTRA,CHANF_DEFAULT,.6);
|
|
while ( (Amount > 0) && (newdamage > 0) )
|
|
{
|
|
newdamage = damage-GetDefaultByType(giveme).Amount;
|
|
if ( newdamage < 0 ) Owner.GiveBody(-newdamage,GetDefaultByType(giveme).MaxAmount);
|
|
newdamage = max(0,newdamage);
|
|
AutoUseExtra();
|
|
Amount--;
|
|
}
|
|
}
|
|
else newdamage = damage;
|
|
}
|
|
|
|
Default
|
|
{
|
|
+INVENTORY.INVBAR;
|
|
+INVENTORY.ISHEALTH;
|
|
+INVENTORY.AUTOACTIVATE;
|
|
Inventory.MaxAmount 5;
|
|
Inventory.InterHubAmount 5;
|
|
Inventory.UseSound "misc/health_pkup";
|
|
+FLOATBOB;
|
|
FloatBobStrength 0.25;
|
|
}
|
|
}
|
|
|
|
// Base casing classes
|
|
Class SWWMCasing : Actor abstract
|
|
{
|
|
int deadtimer, numbounces;
|
|
double pitchvel, anglevel;
|
|
double heat;
|
|
|
|
Default
|
|
{
|
|
Radius 2;
|
|
Height 2;
|
|
+NOBLOCKMAP;
|
|
+MISSILE;
|
|
+MOVEWITHSECTOR;
|
|
+THRUACTORS;
|
|
+USEBOUNCESTATE;
|
|
+INTERPOLATEANGLES;
|
|
+NOTELEPORT;
|
|
+ROLLSPRITE;
|
|
+ROLLCENTER;
|
|
Mass 1;
|
|
Gravity 0.35;
|
|
BounceType "Hexen";
|
|
WallBounceFactor 0.65;
|
|
BounceFactor 0.65;
|
|
BounceSound "explodium/casing";
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
deadtimer = 0;
|
|
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
heat = 1.0;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( isFrozen() ) return;
|
|
if ( InStateSequence(CurState,ResolveState("Death")) )
|
|
{
|
|
deadtimer++;
|
|
if ( deadtimer > 300 ) A_FadeOut(0.05);
|
|
return;
|
|
}
|
|
heat -= 0.05;
|
|
if ( heat <= 0 ) return;
|
|
let s = Spawn("SWWMSmallSmoke",pos);
|
|
s.alpha *= heat;
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
XZW1 A 1
|
|
{
|
|
angle += anglevel;
|
|
pitch += pitchvel;
|
|
}
|
|
Loop;
|
|
Bounce:
|
|
#### # 0
|
|
{
|
|
pitchvel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
anglevel = FRandom[Junk](10,30)*RandomPick[Junk](-1,1);
|
|
vel = (vel.unit()+(FRandom[Junk](-.2,.2),FRandom[Junk](-.2,.2),FRandom[Junk](-.2,.2))).unit()*vel.length();
|
|
if ( numbounces && ((numbounces > 3) || (Random[Junk](1,20) < 17) || (vel.z > -1.4)) )
|
|
{
|
|
ClearBounce();
|
|
ExplodeMissile();
|
|
}
|
|
numbounces++;
|
|
}
|
|
Goto Spawn;
|
|
Death:
|
|
#### # -1
|
|
{
|
|
pitch = roll = 0;
|
|
angle = FRandom[Junk](0,360);
|
|
}
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMBulletImpact : Actor
|
|
{
|
|
Default
|
|
{
|
|
RenderStyle "Add";
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOGRAVITY;
|
|
+NOBLOCKMAP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
Scale 0.25;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_SprayDecal("Pock",-20);
|
|
int numpt = int(Random[Junk](5,10)*scale.x*4);
|
|
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (x+(FRandom[Junk](-.8,.8),FRandom[Junk](-.8,.8),FRandom[Junk](-.8,.8))).unit()*FRandom[Junk](0.1,1.2);
|
|
let s = Spawn("SWWMSmoke",pos+x*2);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(1,1,1)*Random[Junk](128,192));
|
|
}
|
|
numpt = int(Random[Junk](3,8)*scale.x*4);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Junk](-1,1),FRandom[Junk](-1,1),FRandom[Junk](-1,1)).unit()*FRandom[Junk](2,8);
|
|
let s = Spawn("SWWMSpark",pos+x*2);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = int(Random[Junk](2,5)*scale.x*4);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Junk](-1,1),FRandom[Junk](-1,1),FRandom[Junk](-1,1)).unit()*FRandom[Junk](2,8);
|
|
let s = Spawn("SWWMChip",pos+x*2);
|
|
s.vel = pvel;
|
|
}
|
|
if ( !Random[Junk](0,3) ) A_StartSound("bullet/ricochet",CHAN_VOICE,attenuation:2.5);
|
|
else A_StartSound("bullet/hit",CHAN_VOICE,attenuation:3.0);
|
|
Spawn("InvisibleSplasher",pos);
|
|
}
|
|
States
|
|
{
|
|
Spawn:
|
|
TNT1 A 1;
|
|
Stop;
|
|
}
|
|
}
|
|
|
|
Class SWWMWeaponLight : DynamicLight
|
|
{
|
|
int cnt;
|
|
Default
|
|
{
|
|
DynamicLight.Type "Point";
|
|
args 255,224,64,150;
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !target )
|
|
{
|
|
Destroy();
|
|
return;
|
|
}
|
|
if ( target.player )
|
|
{
|
|
Vector3 x, y, z, origin;
|
|
[x, y, z] = swwm_CoordUtil.GetAxes(target.pitch,target.angle,target.roll);
|
|
origin = level.Vec3Offset(target.Vec2OffsetZ(0,0,target.player.viewz),x*12);
|
|
SetOrigin(origin,true);
|
|
}
|
|
else SetOrigin(target.pos,true);
|
|
if ( cnt++ > 2 ) Destroy();
|
|
}
|
|
}
|
|
|
|
Class PunchImpact : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOGRAVITY;
|
|
+NOCLIP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_QuakeEx(2,2,2,12,0,200,"",QF_RELATIVE|QF_SCALEDOWN,falloff:100,rollIntensity:.3);
|
|
A_StartSound("demolitionist/punch",CHAN_VOICE);
|
|
A_SprayDecal("WallCrack",-20);
|
|
int numpt = Random[Ponch](5,10);
|
|
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (x+(FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8))).unit()*FRandom[Ponch](.1,1.2);
|
|
let s = Spawn("SWWMSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(1,1,1)*Random[Ponch](128,192));
|
|
}
|
|
numpt = Random[Ponch](4,12);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
|
let s = Spawn("SWWMSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Random[Ponch](4,8);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
|
let s = Spawn("SWWMChip",pos);
|
|
s.vel = pvel;
|
|
}
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
Class BigPunchImpact : Actor
|
|
{
|
|
Default
|
|
{
|
|
Radius 0.1;
|
|
Height 0;
|
|
+NOGRAVITY;
|
|
+NOCLIP;
|
|
+DONTSPLASH;
|
|
+NOTELEPORT;
|
|
}
|
|
override void PostBeginPlay()
|
|
{
|
|
Super.PostBeginPlay();
|
|
A_QuakeEx(8,8,8,18,0,600,"",QF_RELATIVE|QF_SCALEDOWN,falloff:200,rollIntensity:.9);
|
|
A_StartSound("pusher/althit",CHAN_VOICE);
|
|
A_SprayDecal("BigWallCrack",-20);
|
|
int numpt = Random[Ponch](9,16);
|
|
Vector3 x = (cos(angle)*cos(pitch),sin(angle)*cos(pitch),-sin(pitch));
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (x+(FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8),FRandom[Ponch](-.8,.8))).unit()*FRandom[Ponch](.1,1.2);
|
|
let s = Spawn("SWWMSmoke",pos);
|
|
s.vel = pvel;
|
|
s.SetShade(Color(1,1,1)*Random[Ponch](128,192));
|
|
}
|
|
numpt = Random[Ponch](9,15);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
|
let s = Spawn("SWWMSpark",pos);
|
|
s.vel = pvel;
|
|
}
|
|
numpt = Random[Ponch](9,16);
|
|
for ( int i=0; i<numpt; i++ )
|
|
{
|
|
Vector3 pvel = (FRandom[Ponch](-1,1),FRandom[Ponch](-1,1),FRandom[Ponch](-1,1)).unit()*FRandom[Ponch](2,8);
|
|
let s = Spawn("SWWMChip",pos);
|
|
s.vel = pvel;
|
|
}
|
|
Destroy();
|
|
}
|
|
}
|
|
|
|
// Base class for all SWWM Weapons
|
|
Class SWWMWeapon : Weapon abstract
|
|
{
|
|
private int SWeaponFlags;
|
|
|
|
FlagDef NoFirstGive : SWeaponFlags, 0; // don't give ammo on first pickup (for weapons with a clip count)
|
|
|
|
override void AttachToOwner( Actor other )
|
|
{
|
|
Inventory.AttachToOwner(other);
|
|
Ammo1 = AddAmmo(Owner,AmmoType1,bNoFirstGive?0:AmmoGive1);
|
|
Ammo2 = AddAmmo(Owner,AmmoType2,bNoFirstGive?0:AmmoGive2);
|
|
SisterWeapon = AddWeapon(SisterWeaponType);
|
|
if ( Owner.player )
|
|
{
|
|
if ( !Owner.player.GetNeverSwitch() && !bNo_Auto_Switch )
|
|
Owner.player.PendingWeapon = self;
|
|
if ( Owner.player.mo == players[consoleplayer].camera )
|
|
StatusBar.ReceivedWeapon(self);
|
|
}
|
|
GivenAsMorphWeapon = false;
|
|
}
|
|
override void DetachFromOwner()
|
|
{
|
|
Owner.A_StopSound(CHAN_WEAPON);
|
|
Owner.A_StopSound(CHAN_WEAPONEXTRA);
|
|
Super.DetachFromOwner();
|
|
}
|
|
override void OwnerDied()
|
|
{
|
|
if ( Owner.player && (Owner.player.ReadyWeapon == self) )
|
|
{
|
|
Owner.A_StopSound(CHAN_WEAPON);
|
|
Owner.A_StopSound(CHAN_WEAPONEXTRA);
|
|
}
|
|
A_ClearRefire();
|
|
Super.OwnerDied();
|
|
}
|
|
override String GetObituary( Actor victim, Actor inflictor, Name mod, bool playerattack )
|
|
{
|
|
if ( mod == 'Melee' ) return StringTable.Localize("$O_MELEE");
|
|
return Super.GetObituary(victim,inflictor,mod,playerattack);
|
|
}
|
|
// draw ammo on hud above weapon box
|
|
virtual ui void DrawWeapon( double TicFrac, double bx, double by, Vector2 hs, Vector2 ss )
|
|
{
|
|
}
|
|
// HUD-side ticking
|
|
virtual ui void HudTick()
|
|
{
|
|
}
|
|
// instant raise/lower
|
|
action void A_FullRaise()
|
|
{
|
|
if ( !player ) return;
|
|
if ( player.PendingWeapon != WP_NOCHANGE )
|
|
{
|
|
player.mo.DropWeapon();
|
|
return;
|
|
}
|
|
if ( !player.ReadyWeapon ) return;
|
|
let psp = player.GetPSprite(PSP_WEAPON);
|
|
psp.y = WEAPONTOP;
|
|
}
|
|
action void A_FullLower()
|
|
{
|
|
if ( !player ) return;
|
|
if ( !player.ReadyWeapon )
|
|
{
|
|
player.mo.BringUpWeapon();
|
|
return;
|
|
}
|
|
let psp = player.GetPSprite(PSP_WEAPON);
|
|
psp.y = WEAPONBOTTOM;
|
|
if ( player.playerstate == PST_DEAD )
|
|
{
|
|
// Player is dead, so don't bring up a pending weapon
|
|
// Player is dead, so keep the weapon off screen
|
|
player.SetPSprite(PSP_FLASH,null);
|
|
psp.SetState(player.ReadyWeapon.FindState('DeadLowered'));
|
|
return;
|
|
}
|
|
// [RH] Clear the flash state. Only needed for Strife.
|
|
player.SetPSprite(PSP_FLASH,null);
|
|
player.mo.BringUpWeapon();
|
|
}
|
|
private action bool TryMelee( double angle, int dmg )
|
|
{
|
|
FTranslatedLineTarget t;
|
|
double slope = AimLineAttack(angle,1.5*DEFMELEERANGE,t,0.,ALF_CHECK3D);
|
|
FLineTraceData d;
|
|
LineTrace(angle,1.5*DEFMELEERANGE,slope,0,player.viewheight,data:d);
|
|
bool raging = CountInv("RagekitPower");
|
|
if ( d.HitType == TRACE_HitActor )
|
|
{
|
|
bool bloodless = true;
|
|
double diff = deltaangle(self.angle,AngleTo(d.HitActor));
|
|
self.angle += clamp(diff,-5.,5.);
|
|
SWWMHandler.DoKnockback(d.HitActor,d.HitDir+(0,0,.2),dmg*2000);
|
|
if ( raging )
|
|
{
|
|
invoker.bEXTREMEDEATH = true;
|
|
invoker.bNOEXTREMEDEATH = false;
|
|
}
|
|
else
|
|
{
|
|
invoker.bEXTREMEDEATH = false;
|
|
invoker.bNOEXTREMEDEATH = true;
|
|
}
|
|
d.HitActor.DaggerAlert(self);
|
|
dmg = d.HitActor.DamageMobj(invoker,self,dmg,'Melee',DMG_USEANGLE|DMG_THRUSTLESS,atan2(d.HitDir.y,d.HitDir.x));
|
|
invoker.bEXTREMEDEATH = invoker.default.bEXTREMEDEATH;
|
|
invoker.bNOEXTREMEDEATH = invoker.default.bEXTREMEDEATH;
|
|
int quakin = raging?8:2;
|
|
if ( d.HitActor.player ) d.HitActor.A_QuakeEx(quakin,quakin,quakin,6,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.125*quakin);
|
|
if ( !d.HitActor.bNOBLOOD && !d.HitActor.bINVULNERABLE )
|
|
{
|
|
d.HitActor.TraceBleed(dmg,invoker);
|
|
d.HitActor.SpawnBlood(d.HitLocation,atan2(d.HitDir.y,d.HitDir.x)+180,dmg);
|
|
bloodless = false;
|
|
}
|
|
else
|
|
{
|
|
let p = Spawn(raging?"BigPunchImpact":"PunchImpact",d.HitLocation);
|
|
p.angle = atan2(-d.HitDir.y,-d.HitDir.x);
|
|
}
|
|
A_QuakeEx(quakin/2,quakin/2,quakin/2,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.06*quakin);
|
|
if ( raging ) A_StartSound(bloodless?"pusher/althit":"pusher/altmeat",CHAN_WEAPON,CHANF_OVERLAP);
|
|
else A_StartSound(bloodless?"demolitionist/punch":"demolitionist/punchf",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_AlertMonsters(300);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
action void A_Melee( int dmg = 40 )
|
|
{
|
|
bool raging = CountInv("RagekitPower");
|
|
int maxang = raging?18:12;
|
|
for ( int i=0; i<maxang; i++ ) if ( TryMelee(angle+i*(45./16),dmg) || TryMelee(angle-i*(45./16),dmg) ) return;
|
|
// check for walls instead
|
|
FTranslatedLineTarget t;
|
|
double slope = AimLineAttack(angle,DEFMELEERANGE,t,0.,ALF_CHECK3D);
|
|
FLineTraceData d;
|
|
LineTrace(angle,DEFMELEERANGE,slope,TRF_THRUACTORS,player.viewheight,data:d);
|
|
if ( d.HitType == TRACE_HitNone ) return;
|
|
Vector3 HitNormal = -d.HitDir;
|
|
if ( d.HitType == TRACE_HitFloor )
|
|
{
|
|
if ( d.Hit3DFloor ) HitNormal = -d.Hit3DFloor.top.Normal;
|
|
else HitNormal = d.HitSector.floorplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitCeiling )
|
|
{
|
|
if ( d.Hit3DFloor ) HitNormal = -d.Hit3DFloor.bottom.Normal;
|
|
else HitNormal = d.HitSector.ceilingplane.Normal;
|
|
}
|
|
else if ( d.HitType == TRACE_HitWall )
|
|
{
|
|
HitNormal = (-d.HitLine.delta.y,d.HitLine.delta.x,0).unit();
|
|
if ( !d.LineSide ) HitNormal *= -1;
|
|
d.HitLine.RemoteActivate(self,d.LineSide,SPAC_Impact,d.HitLocation+HitNormal*4);
|
|
}
|
|
let p = Spawn(raging?"BigPunchImpact":"PunchImpact",d.HitLocation+HitNormal*4);
|
|
p.angle = atan2(HitNormal.y,HitNormal.x);
|
|
p.pitch = asin(-HitNormal.z);
|
|
int quakin = raging?4:1;
|
|
A_QuakeEx(quakin,quakin,quakin,3,0,1,"",QF_RELATIVE|QF_SCALEDOWN,rollIntensity:0.12*quakin);
|
|
A_StartSound(raging?"pusher/althit":"demolitionist/punch",CHAN_WEAPON,CHANF_OVERLAP);
|
|
A_AlertMonsters(100);
|
|
}
|
|
override void PlayUpSound( Actor origin )
|
|
{
|
|
if ( UpSound ) origin.A_StartSound(UpSound,CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
action void A_SWWMFlash( StateLabel flashlabel = null )
|
|
{
|
|
if ( !player || !player.ReadyWeapon )
|
|
return;
|
|
Weapon weap = player.ReadyWeapon;
|
|
State flashstate = null;
|
|
if ( !flashlabel )
|
|
{
|
|
if ( weap.bAltFire )
|
|
flashstate = weap.FindState('AltFlash');
|
|
if ( !flashstate )
|
|
flashstate = weap.FindState('Flash');
|
|
}
|
|
else flashstate = weap.FindState(flashlabel);
|
|
player.SetPSprite(PSP_FLASH,flashstate);
|
|
A_OverlayFlags(PSP_FLASH,PSPF_RENDERSTYLE|PSPF_FORCESTYLE,true);
|
|
A_OverlayRenderStyle(PSP_FLASH,STYLE_Add);
|
|
}
|
|
// tells the SWWM HUD that this weapon has ammo available
|
|
virtual clearscope bool ReportHUDAmmo()
|
|
{
|
|
return (!Ammo1||(Ammo1.Amount>0)||(Ammo2&&(Ammo2.Amount>0)));
|
|
}
|
|
// tells the Embiggener that this weapon uses the specified ammo type
|
|
// even if it is not its primary one
|
|
virtual clearscope bool UsesAmmo( Class<Ammo> kind )
|
|
{
|
|
return (AmmoType1&&(kind is AmmoType1))||(AmmoType2&&(kind is AmmoType2));
|
|
}
|
|
override void ModifyDropAmount( int dropamount )
|
|
{
|
|
Super.ModifyDropAmount(dropamount);
|
|
if ( (AmmoGive1 <= 0) && (default.AmmoGive1 > 0) )
|
|
AmmoGive1 = 1;
|
|
if ( (AmmoGive2 <= 0) && (default.AmmoGive2 > 0) )
|
|
AmmoGive2 = 1;
|
|
}
|
|
Default
|
|
{
|
|
Weapon.BobStyle "Alpha";
|
|
Weapon.BobSpeed 3.0;
|
|
Weapon.BobRangeX 0.5;
|
|
Weapon.BobRangeY 0.2;
|
|
Weapon.YAdjust 0;
|
|
+WEAPON.NOALERT;
|
|
+WEAPON.NODEATHINPUT;
|
|
+FLOATBOB;
|
|
FloatBobStrength 0.25;
|
|
}
|
|
} |