swwmgz_m/zscript/weapons/swwm_baseweapon.zsc
Marisa Kirisame 4d7019ae86 Several changes from devel once more:
- 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)
2021-12-08 18:17:41 +01:00

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;
}
}