- Fuck it, Quadravol will be lever action. - Tiny cleanup. - Rewrite the weapon replacement system (less of a mess now maybe?). - Some menu fixes. - Minimap zoom increments like in the Common Library. - Add missing sound definition for Safety Tether. (This mostly went unnoticed because it's VERY rare to have it play) - Shift Sparkster x3 (DLC2) to slot 7. This way you can have both it and the Quadravol simultaneously. It would be unfair to not let you hold both "iconic" UnSX weapons at once. - Small lore tweak on Quadravol stance swap. - Fix off-by-one bug in looping palette lights. - Re-do logo shader. Use separate layer textures. - Fix Elemental Coating breaking "End Level" damage sectors. (This will be the last batch of changes before I continue working on menus)
444 lines
14 KiB
Text
444 lines
14 KiB
Text
// Base class for all SWWM Weapons
|
|
Class SWWMWeapon : Weapon abstract
|
|
{
|
|
Mixin SWWMOverlapPickupSound;
|
|
Mixin SWWMRespawn;
|
|
|
|
bool wasused;
|
|
bool bUsePickup;
|
|
private int SWeaponFlags;
|
|
String tooltip;
|
|
bool tooltipsent;
|
|
Class<Ammo> dropammotype;
|
|
int dropamount;
|
|
|
|
Property Tooltip : tooltip;
|
|
Property DropAmmoType : dropammotype;
|
|
|
|
FlagDef NoFirstGive : SWeaponFlags, 0; // don't give ammo on first pickup (for weapons with a clip count)
|
|
FlagDef HideInMenu : SWeaponFlags, 1; // don't show in inventory menu (usually for sister weapons)
|
|
FlagDef NoSwapWeapon : SWeaponFlags, 2; // weapon is not affected by slot swapping
|
|
|
|
bool IsSwapWeapon( Inventory i ) const
|
|
{
|
|
if ( bNoSwapWeapon || (i.GetClass() == GetClass()) ) return false;
|
|
let w = SWWMWeapon(i);
|
|
if ( w && !w.bNoSwapWeapon && (SlotNumber != -1) && (w.SlotNumber == SlotNumber) )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
SWWMWeapon HasSwapWeapon( Actor other ) const
|
|
{
|
|
if ( bNoSwapWeapon ) return null;
|
|
for ( Inventory i=other.inv; i; i=i.inv )
|
|
{
|
|
if ( IsSwapWeapon(i) )
|
|
return SWWMWeapon(i);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
override void Touch( Actor toucher )
|
|
{
|
|
// show prompt to swap weapon, and prevent normal pickup
|
|
SWWMWeapon sw;
|
|
if ( bSPECIAL && swwm_swapweapons && (sw = HasSwapWeapon(toucher)) )
|
|
{
|
|
if ( toucher.CheckLocalView() )
|
|
{
|
|
// use sisterweapon tag for dual wield (slot 2 weapons)
|
|
if ( sw.SisterWeapon && (sw.Amount > 1) )
|
|
Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.SisterWeapon.GetTag(),GetTag()));
|
|
else Console.MidPrint(SmallFont,String.Format(StringTable.Localize("$SWWM_SWAPWEAPON"),sw.GetTag(),GetTag()));
|
|
}
|
|
return;
|
|
}
|
|
// explicit use-to pickup, function must be called from Used() virtual
|
|
if ( toucher.player && swwm_usetopickup && !bUsePickup )
|
|
return;
|
|
Super.Touch(toucher);
|
|
}
|
|
// allow pickup by use + swap weapon support
|
|
override bool Used( Actor user )
|
|
{
|
|
// can't pick up
|
|
if ( !bSPECIAL ) return false;
|
|
// no use through melee
|
|
if ( (user.player.ReadyWeapon is 'SWWMWeapon') && SWWMWeapon(user.player.ReadyWeapon).wallponch && !swwm_meleepickup )
|
|
return false;
|
|
Vector3 itempos = Vec3Offset(0,0,Height/2),
|
|
userpos = user.Vec2OffsetZ(0,0,user.player.viewz);
|
|
// test vertical range
|
|
Vector3 diff = level.Vec3Diff(user.Vec3Offset(0,0,user.Height/2),Vec3Offset(0,0,Height/2));
|
|
double rang = user.player?PlayerPawn(user.player.mo).UseRange:(user.Height/2);
|
|
if ( abs(diff.z) > rang ) return false;
|
|
// if the toucher owns our SwapWeapon, drop it before picking us up
|
|
bool swapto = false;
|
|
SWWMWeapon sw;
|
|
if ( swwm_swapweapons && (sw = HasSwapWeapon(user)) )
|
|
{
|
|
if ( (sw == user.player.ReadyWeapon) || (sw.SisterWeapon && (sw.SisterWeapon == user.player.ReadyWeapon)) )
|
|
swapto = true;
|
|
int ngun = sw.Amount;
|
|
double ang = -15*(ngun-1);
|
|
for ( int i=0; i<ngun; i++ )
|
|
{
|
|
let d = user.DropInventory(sw);
|
|
if ( !d || (ngun <= 1) ) continue;
|
|
// adjust angle for multi-drops
|
|
d.angle = user.angle+ang;
|
|
d.vel.xy = RotateVector((5,0),d.angle);
|
|
d.vel.z = 1;
|
|
d.vel += user.vel;
|
|
ang += 30;
|
|
}
|
|
// don't autoswitch just yet (hacky)
|
|
if ( swapto )
|
|
{
|
|
user.player.ReadyWeapon = null;
|
|
user.player.PendingWeapon = WP_NOCHANGE;
|
|
}
|
|
}
|
|
bUsePickup = true;
|
|
Touch(user);
|
|
bUsePickup = false;
|
|
// we got picked up
|
|
if ( bDestroyed || Owner || !bSPECIAL )
|
|
{
|
|
// autoswitch to us if we got swapped
|
|
if ( swapto ) user.A_SelectWeapon(GetClass());
|
|
Vector3 tracedir = level.Vec3Diff(userpos,itempos);
|
|
double dist = tracedir.length();
|
|
tracedir /= dist;
|
|
let cf = new("CrossLineFinder");
|
|
cf.Trace(userpos,level.PointInSector(userpos.xy),tracedir,dist,0);
|
|
// trigger all player cross lines found between user and item
|
|
for ( int i=0; i<cf.clines.Size(); i++ )
|
|
cf.clines[i].Activate(user,cf.csides[i],SPAC_Cross);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
override int, int CheckAddToSlots()
|
|
{
|
|
if ( (GetReplacement(GetClass()) == GetClass()) && !bPowered_Up )
|
|
{
|
|
if ( swwm_singlefirst && SisterWeaponType )
|
|
{
|
|
let sw = GetDefaultByType(SisterWeaponType);
|
|
return sw.SlotNumber, int(sw.SlotPriority*65536);
|
|
}
|
|
return SlotNumber, int(SlotPriority*65536);
|
|
}
|
|
return -1, 0;
|
|
}
|
|
|
|
action void A_BumpFOV( double factor )
|
|
{
|
|
if ( !(self is 'Demolitionist') ) return;
|
|
Demolitionist(self).lastbump *= factor;
|
|
}
|
|
|
|
// subtracts given ammo from price, drops excess
|
|
virtual bool PickupForAmmoSWWM( SWWMWeapon ownedWeapon )
|
|
{
|
|
// save time, always return false if we don't use ammo
|
|
if ( !ownedWeapon.Ammo1 && !ownedWeapon.Ammo2 ) return false;
|
|
bool gotstuff = false;
|
|
int oldamount1 = 0, oldamount2 = 0;
|
|
if ( ownedWeapon.Ammo1 ) oldamount1 = ownedWeapon.Ammo1.Amount;
|
|
if ( ownedWeapon.Ammo2 ) oldamount2 = ownedWeapon.Ammo2.Amount;
|
|
if ( AmmoGive1 > 0 ) gotstuff = AddExistingAmmo(ownedWeapon.Ammo1,AmmoGive1);
|
|
if ( AmmoGive2 > 0 ) gotstuff |= AddExistingAmmo(ownedWeapon.Ammo2,AmmoGive2);
|
|
let Owner = ownedWeapon.Owner;
|
|
if ( gotstuff && Owner && Owner.player )
|
|
{
|
|
if ( ownedWeapon.Ammo1 && (oldamount1 == 0) )
|
|
PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo1.GetClass());
|
|
else if ( ownedWeapon.Ammo2 && (oldamount2 == 0) )
|
|
PlayerPawn(Owner).CheckWeaponSwitch(ownedWeapon.Ammo2.GetClass());
|
|
}
|
|
if ( ownedWeapon.Ammo1 )
|
|
{
|
|
// subtract price of ammo we don't give
|
|
int ammonotgiven = default.AmmoGive1-AmmoGive1;
|
|
if ( ammonotgiven > 0 ) Stamina -= int(ownedWeapon.Ammo1.Stamina*(1.+.75*(ammonotgiven-1)));
|
|
// subtract price of given ammo
|
|
int ammogiven = ownedWeapon.Ammo1.Amount-oldamount1;
|
|
if ( ammogiven > 0 ) Stamina -= int(ownedWeapon.Ammo1.Stamina*(1.+.75*(ammogiven-1)));
|
|
// drop excess
|
|
int dropme = AmmoGive1-ammogiven;
|
|
if ( dropme > 0 )
|
|
{
|
|
// hacky, but it works
|
|
ownedWeapon.Ammo1.CreateTossable(dropme);
|
|
ownedWeapon.Ammo1.Amount += dropme;
|
|
}
|
|
}
|
|
if ( ownedWeapon.Ammo2 )
|
|
{
|
|
// subtract price of ammo we don't give
|
|
int ammonotgiven = default.AmmoGive2-AmmoGive2;
|
|
if ( ammonotgiven > 0 ) Stamina -= int(ownedWeapon.Ammo2.Stamina*(1.+.75*(ammonotgiven-1)));
|
|
// subtract price of given ammo
|
|
int ammogiven = ownedWeapon.Ammo2.Amount-oldamount2;
|
|
if ( ammogiven > 0 ) Stamina -= int(ownedWeapon.Ammo2.Stamina*(1.+.75*(ammogiven-1)));
|
|
// drop excess
|
|
int dropme = AmmoGive2-ammogiven;
|
|
if ( dropme > 0 )
|
|
{
|
|
// hacky, but it works
|
|
ownedWeapon.Ammo2.CreateTossable(dropme);
|
|
ownedWeapon.Ammo2.Amount += dropme;
|
|
}
|
|
}
|
|
return gotstuff;
|
|
}
|
|
override bool HandlePickup( Inventory item )
|
|
{
|
|
// can't hold both weapons at once
|
|
if ( swwm_swapweapons && IsSwapWeapon(item) )
|
|
return true;
|
|
if ( (GetClass() == item.GetClass()) && !item.ShouldStay() )
|
|
{
|
|
if ( SWWMWeapon(item).PickupForAmmoSWWM(self) )
|
|
item.bPickupGood = true;
|
|
if ( !deathmatch && (Amount+item.Amount > MaxAmount) && (item.Stamina > 0) )
|
|
{
|
|
// sell excess
|
|
int sellprice = item.Stamina/2;
|
|
SWWMScoreObj.Spawn(sellprice,Owner.Vec3Offset(FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8),FRandom[ScoreBits](-8,8)+Owner.Height/2));
|
|
SWWMCredits.Give(Owner.player,sellprice);
|
|
if ( Owner.player )
|
|
Console.Printf(StringTable.Localize(SWWMUtility.SellFemaleItem(item)?"$SWWM_SELLEXTRA_FEM":"$SWWM_SELLEXTRA"),Owner.player.GetUserName(),GetTag(),sellprice);
|
|
item.bPickupGood = true;
|
|
}
|
|
// reset the price in case it has to respawn
|
|
item.Stamina = item.default.Stamina;
|
|
return true;
|
|
}
|
|
return Super.HandlePickup(item);
|
|
}
|
|
override bool ShouldStay()
|
|
{
|
|
// SWWM weapons never stay unless explicitly stated
|
|
if ( !bDROPPED && (deathmatch || alwaysapplydmflags) && sv_weaponstay ) return true;
|
|
return false;
|
|
}
|
|
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 && ReportHUDAmmo() ) // hey, as long as it works
|
|
Owner.player.PendingWeapon = self;
|
|
if ( Owner.player.mo == players[consoleplayer].camera )
|
|
StatusBar.ReceivedWeapon(self);
|
|
}
|
|
GivenAsMorphWeapon = false;
|
|
}
|
|
override void OwnerDied()
|
|
{
|
|
if ( Owner.player && (Owner.player.ReadyWeapon == self) )
|
|
{
|
|
Owner.A_StopSound(CHAN_WEAPONEXTRA);
|
|
Owner.A_StopSound(CHAN_WEAPONEXTRA2);
|
|
Owner.A_StopSound(CHAN_WEAPONEXTRA3);
|
|
}
|
|
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 )
|
|
{
|
|
}
|
|
// animations
|
|
action void A_PlayerFire()
|
|
{
|
|
let demo = Demolitionist(player.mo);
|
|
if ( demo && (demo.Health > 0) ) demo.PlayFire();
|
|
}
|
|
action void A_PlayerMelee( bool bFast = false )
|
|
{
|
|
let demo = Demolitionist(player.mo);
|
|
if ( demo && (demo.Health > 0) )
|
|
{
|
|
if ( bFast ) demo.PlayFastMelee();
|
|
else demo.PlayMelee();
|
|
}
|
|
}
|
|
action void A_PlayerReload()
|
|
{
|
|
let demo = Demolitionist(player.mo);
|
|
if ( demo && (demo.Health > 0) ) demo.PlayReload();
|
|
}
|
|
action void A_PlayerCheckGun()
|
|
{
|
|
let demo = Demolitionist(player.mo);
|
|
if ( demo && (demo.Health > 0) ) demo.PlayCheckGun();
|
|
}
|
|
// 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);
|
|
if ( !psp ) return;
|
|
ResetPSprite(psp);
|
|
psp.y = WEAPONTOP;
|
|
// do not jump to ready state here, the weapon should do that
|
|
// directly once it finishes playing its select animation
|
|
}
|
|
action void A_FullLower()
|
|
{
|
|
if ( !player ) return;
|
|
if ( !player.ReadyWeapon )
|
|
{
|
|
player.mo.BringUpWeapon();
|
|
return;
|
|
}
|
|
let psp = player.GetPSprite(PSP_WEAPON);
|
|
if ( !psp ) return;
|
|
psp.y = WEAPONBOTTOM;
|
|
ResetPSprite(psp);
|
|
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();
|
|
}
|
|
override void PlayUpSound( Actor origin )
|
|
{
|
|
if ( UpSound ) origin.A_StartSound(UpSound,CHAN_WEAPON,CHANF_OVERLAP);
|
|
}
|
|
override void Tick()
|
|
{
|
|
Super.Tick();
|
|
if ( !Owner ) return;
|
|
if ( !Owner.player || (Owner.player.ReadyWeapon != self) || !(Owner.player.WeaponState&WF_WEAPONSWITCHOK) || (Owner.player.WeaponState&WF_DISABLESWITCH) )
|
|
{
|
|
tooltipsent = false;
|
|
return;
|
|
}
|
|
if ( tooltipsent ) return;
|
|
tooltipsent = true;
|
|
if ( Owner.player == players[consoleplayer] )
|
|
SWWMUtility.SendTooltip(GetClass());
|
|
}
|
|
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 )
|
|
{
|
|
self.dropamount = dropamount;
|
|
Super.ModifyDropAmount(dropamount);
|
|
if ( (AmmoGive1 <= 0) && (default.AmmoGive1 > 0) )
|
|
AmmoGive1 = 1;
|
|
if ( (AmmoGive2 <= 0) && (default.AmmoGive2 > 0) )
|
|
AmmoGive2 = 1;
|
|
}
|
|
override bool SpecialDropAction( Actor dropper )
|
|
{
|
|
if ( swwm_enemydrops > 0 ) return false;
|
|
else if ( swwm_enemydrops == 0 )
|
|
{
|
|
// drop our corresponding ammo
|
|
if ( !DropAmmoType ) return true;
|
|
let a = Spawn(DropAmmoType,pos,ALLOW_REPLACE);
|
|
if ( !a ) return true;
|
|
a.bDROPPED = true;
|
|
a.bNOGRAVITY = false;
|
|
if ( !(level.compatflags&COMPATF_NOTOSSDROPS) )
|
|
a.TossItem();
|
|
if ( a is 'Inventory' )
|
|
{
|
|
let i = Inventory(a);
|
|
i.ModifyDropAmount(dropamount);
|
|
i.bTOSSED = true;
|
|
if ( i.SpecialDropAction(dropper) )
|
|
i.Destroy();
|
|
}
|
|
return true;
|
|
}
|
|
// no weapon drops from enemies
|
|
return true;
|
|
}
|
|
override Inventory CreateTossable( int amt )
|
|
{
|
|
// disallow dropping if weapon isn't ready for switching
|
|
if ( (Owner.player.ReadyWeapon == self) && (!(Owner.player.WeaponState&WF_WEAPONSWITCHOK) || (Owner.player.WeaponState&WF_DISABLESWITCH)) )
|
|
return null;
|
|
let ret = Super.CreateTossable(amt);
|
|
// reattach our glow if we became a pickup
|
|
if ( (ret == self) && (PickupFlash is 'SWWMPickupFlash') && swwm_itemglows )
|
|
{
|
|
let p = Spawn(PickupFlash,Vec3Offset(0,0,16));
|
|
p.target = self;
|
|
p.SetStateLabel("Pickup");
|
|
}
|
|
return ret;
|
|
}
|
|
Default
|
|
{
|
|
Weapon.BobStyle "Alpha";
|
|
Weapon.BobSpeed 3.0;
|
|
Weapon.BobRangeX 0.5;
|
|
Weapon.BobRangeY 0.2;
|
|
Weapon.YAdjust 0;
|
|
Weapon.SlotPriority 1.;
|
|
Inventory.RestrictedTo "Demolitionist";
|
|
Inventory.PickupFlash "SWWMRedPickupFlash";
|
|
+INVENTORY.IGNORESKILL;
|
|
+WEAPON.NOALERT;
|
|
+WEAPON.NODEATHINPUT;
|
|
+FLOATBOB;
|
|
FloatBobStrength 0.25;
|
|
}
|
|
}
|