//************************************************************************** //** //** ## ## ## ## ## #### #### ### ### //** ## ## ## ## ## ## ## ## ## ## #### #### //** ## ## ## ## ## ## ## ## ## ## ## ## ## ## //** ## ## ######## ## ## ## ## ## ## ## ### ## //** ### ## ## ### ## ## ## ## ## ## //** # ## ## # #### #### ## ## //** //** $Id: Weapon.vc 2659 2007-08-18 14:16:15Z dj_jl $ //** //** Copyright (C) 1999-2006 Jānis Legzdiņš //** //** This program is free software; you can redistribute it and/or //** modify it under the terms of the GNU General Public License //** as published by the Free Software Foundation; either version 2 //** of the License, or (at your option) any later version. //** //** This program is distributed in the hope that it will be useful, //** but WITHOUT ANY WARRANTY; without even the implied warranty of //** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //** GNU General Public License for more details. //** //************************************************************************** class Weapon : Inventory abstract; const float LOWERSPEED = 6.0; const float RAISESPEED = 6.0; const float WEAPONBOTTOM = 128.0; const float WEAPONTOP = 32.0; // Type of ammo weapon needs to operate. class AmmoType1; class AmmoType2; // How much ammo to give when picked up. int AmmoGive1; int AmmoGive2; // How much ammo used in each shot. int AmmoUse1; int AmmoUse2; // Weapon given together with current one. class SisterWeaponType; name UpSound; name ReadySound; // Weapon slot this weapon is placed in. int Slot; // Weapons with lower values have greater priority int SelectionOrder; // Kickback from this weapon float Kickback; // Y-adjustment value for full screen float PSpriteSY; // For displaying weapon with player model. int PlayerModelVersion; float BotCombatDist; // Switch away if got ammo for more powerfull weapon. bool bWimpyWeapon; // Don't auto fire bool bNoAutoFire; // This is a powered up version of another weapon bool bPoweredUp; // Does Heretic staff level 2 style kickback. bool bStaff2Kickback; // Always use extreme death bool bExtremeDeath; // Ammo is optional bool bAmmoOptional; // Half a chance to play the reay sound. bool bReadySndHalf; // Don't bob weapon. bool bDontBob; // Doesn't allert monsters when fired. bool bNoAlert; // It's a melee weapon bool bBotMelee; // It's a projectile weapon bool bBotProjectile; // It's a BFG bool bBotBfg; // Not considered a weapon by gimme cheat bool bCheatNotWeapon; // Runtime in-inventory references. Ammo Ammo1; Ammo Ammo2; Weapon SisterWeapon; replication { reliable if (Role == ROLE_Authority && bNetOwner) Ammo1, Ammo2; } //========================================================================== // // HandlePickup // //========================================================================== bool HandlePickup(Inventory Item) { if (Item.Class == Class) { if (Weapon(Item).PickupForAmmo(self)) { Item.bPickupGood = true; } return true; } if (Inventory) { return Inventory.HandlePickup(Item); } return false; } //========================================================================== // // CreateCopy // //========================================================================== Inventory CreateCopy(EntityEx Toucher) { Weapon Copy = Weapon(::CreateCopy(Toucher)); if (Copy) { Copy.AmmoGive1 = AmmoGive1; Copy.AmmoGive2 = AmmoGive2; } return Copy; } //========================================================================== // // CreateTossable // //========================================================================== Inventory CreateTossable() { // Drop the wapon that is meant to be placed in a level, i.e. that // normaly gives you ammo. if (SisterWeapon && !default.AmmoGive1 && !default.AmmoGive2 && (SisterWeapon.default.AmmoGive1 > 0 || SisterWeapon.default.AmmoGive2 > 0)) { return SisterWeapon.CreateTossable(); } Weapon Copy = Weapon(::CreateTossable()); if (Copy) { // If weapon has a sister weapon, remove it too. if (SisterWeapon) { SisterWeapon.Destroy(); } // Dropped weapons don't have ammo to prevent cheating. Copy.AmmoGive1 = 0; Copy.AmmoGive2 = 0; } return Copy; } //========================================================================== // // AttachToOwner // //========================================================================== void AttachToOwner(EntityEx NewOwner) { ::AttachToOwner(NewOwner); // Set up references to ammo items since they are used a lots. Ammo1 = AddAmmo(NewOwner, AmmoType1, AmmoGive1); Ammo2 = AddAmmo(NewOwner, AmmoType2, AmmoGive2); SisterWeapon = AddWeapon(SisterWeaponType); if (Owner.Player) { // Doom and Strife always switch to a new weapon, Heretic and Hexen // only switch to more powerful weapons. if (LineSpecialGameInfo(Level.Game).bAlwaysSwitchNewWeapon || !PlayerEx(Owner.Player).ReadyWeapon || Slot > PlayerEx(Owner.Player).ReadyWeapon.Slot || (Slot == PlayerEx(Owner.Player).ReadyWeapon.Slot && SelectionOrder < PlayerEx(Owner.Player).ReadyWeapon.SelectionOrder)) { PlayerEx(Owner.Player).PendingWeapon = self; } } } //========================================================================== // // AddAmmo // // Returns false if the ammo can't be picked up at all // //========================================================================== final Ammo AddAmmo(EntityEx Toucher, class AmmoType, int count) { if (!AmmoType) { return none; } if (!bIgnoreSkill && (Level.Game.gameskill == sk_baby || (Level.Game.gameskill == sk_nightmare && LineSpecialGameInfo(Level.Game).bNightmareExtraAmmo))) { // extra ammo in baby mode and nightmare mode if (LineSpecialGameInfo(Level.Game).bBabyNightmareDoubleAmmo) { count <<= 1; } else { count += count >> 1; } } Ammo AmmoItem = Ammo(Toucher.FindInventory(AmmoType)); if (!AmmoItem) { AmmoItem = Spawn(AmmoType); AmmoItem.AttachToOwner(Toucher); AmmoItem.Amount = count; } else if (AmmoItem.Amount < AmmoItem.MaxAmount) { AmmoItem.Amount += count; } if (AmmoItem.Amount > AmmoItem.MaxAmount) { AmmoItem.Amount = AmmoItem.MaxAmount; } return AmmoItem; } //========================================================================== // // AddExistingAmmo // //========================================================================== final bool AddExistingAmmo(Ammo AmmoItem, int count) { int oldammo; if (!AmmoItem || count <= 0) { return false; } if (AmmoItem.Amount == AmmoItem.MaxAmount) { return false; } if (!bIgnoreSkill && (Level.Game.gameskill == sk_baby || (Level.Game.gameskill == sk_nightmare && LineSpecialGameInfo(Level.Game).bNightmareExtraAmmo))) { // extra ammo in baby mode and nightmare mode if (LineSpecialGameInfo(Level.Game).bBabyNightmareDoubleAmmo) { count <<= 1; } else { count += count >> 1; } } oldammo = AmmoItem.Amount; AmmoItem.Amount += count; if (AmmoItem.Amount > AmmoItem.MaxAmount) { AmmoItem.Amount = AmmoItem.MaxAmount; } if (oldammo <= 0 && AmmoItem.Owner.Player) { PlayerEx(AmmoItem.Owner.Player).GotAmmo(AmmoItem); } return true; } //========================================================================== // // AttachToOwner // //========================================================================== final Weapon AddWeapon(class WeaponType) { if (!WeaponType) { return none; } Weapon Wpn = Weapon(EntityEx(Owner).FindInventory(WeaponType)); if (!Wpn) { Wpn = Spawn(WeaponType); Wpn.AttachToOwner(EntityEx(Owner)); } return Wpn; } //========================================================================== // // PickupForAmmo // //========================================================================== bool PickupForAmmo(Weapon ExistingWeapon) { // leave placed weapons forever on net games if (ShouldStay() && !bDropped) { return false; } bool gaveammo = false; if (AddExistingAmmo(ExistingWeapon.Ammo1, AmmoGive1)) { gaveammo = true; } if (AddExistingAmmo(ExistingWeapon.Ammo2, AmmoGive2)) { gaveammo = true; } return gaveammo; } //========================================================================== // // ShouldStay // //========================================================================== bool ShouldStay() { return LineSpecialGameInfo(Level.Game).bWeaponsStay; } //========================================================================== // // Use // // Using a weapon makes it active. // //========================================================================== bool Use(bool Pickup) { // Can't directly use powered up weapon. if (bPoweredUp) { return false; } // See which weapon to use. Weapon UseWpn = self; if (SisterWeapon && SisterWeapon.bPoweredUp && EntityEx(Owner).FindInventory(PowerWeaponLevel2)) { UseWpn = SisterWeapon; } if (Owner.bIsPlayer && PlayerEx(Owner.Player).ReadyWeapon != UseWpn) { PlayerEx(Owner.Player).PendingWeapon = UseWpn; } return false; } //========================================================================== // // GetUpState // //========================================================================== state GetUpState() { return FindState('Select'); } //========================================================================== // // GetDownState // //========================================================================== state GetDownState() { return FindState('Deselect'); } //========================================================================== // // GetReadyState // //========================================================================== state GetReadyState() { return FindState('Ready'); } //========================================================================== // // GetAttackState // //========================================================================== state GetAttackState(bool Hold) { state S = none; if (Hold) { S = FindState('Hold'); } if (!S) { S = FindState('Fire'); } return S; } //========================================================================== // // CheckAmmo // // Returns true if there is enough mana to shoot. If not, selects the // next weapon to use. // //========================================================================== bool CheckAmmo(bool AutoSwitch, optional bool RequireAmmo) { if (!RequireAmmo && bAmmoOptional) { return true; } bool Good = true; if (Ammo1 && Ammo1.Amount < AmmoUse1) { Good = false; } if (Ammo2 && Ammo2.Amount < AmmoUse2) { Good = false; } if (Good) { return true; } if (!AutoSwitch) { return false; } // out of ammo, pick a weapon to change to Weapon Best = PlayerEx(Owner.Player).BestWeapon(); PlayerEx(Owner.Player).PendingWeapon = Best; Owner.Player.SetViewState(ps_weapon, GetDownState()); return false; } //========================================================================== // // DepleteAmmo // //========================================================================== final bool DepleteAmmo(optional bool CheckEnough, optional int DehackedUse) { // Default parameter values if (!specified_CheckEnough) { CheckEnough = true; } if (!specified_DehackedUse) { DehackedUse = AmmoUse1; } if (CheckEnough && !CheckAmmo(false)) { return false; } if (Ammo1) { if (LineSpecialGameInfo(Level.Game).bDehacked) { Ammo1.Amount -= DehackedUse; } else { Ammo1.Amount -= AmmoUse1; } } if (Ammo2) { Ammo2.Amount -= AmmoUse2; } return true; } //=========================================================================== // // EndPowerup // //=========================================================================== final void EndPowerup() { if (SisterWeapon) { PlayerEx(Owner.Player).SetWeapon(SisterWeapon); if (SisterWeapon.GetReadyState() != GetReadyState()) { PlayerEx(Owner.Player).SetViewState(ps_weapon, SisterWeapon.GetReadyState()); } } } //========================================================================== // // A_WeaponReady // // The player can fire the weapon or change to another weapon at this time. // Follows after getting weapon up, or after previous attack/fire sequence. // //========================================================================== final void A_WeaponReady() { float angle; // get out of attack state if (StateIsInSequence(Owner.State, EntityEx(Owner).MissileState) || StateIsInSequence(Owner.State, EntityEx(Owner).MeleeState)) { Owner.SetState(EntityEx(Owner).IdleState); } if (Owner.Player.ViewStates[ps_weapon].State == GetReadyState() && ReadySound && (!bReadySndHalf || Random() < 0.5)) { Owner.PlaySound(ReadySound, CHAN_WEAPON); } // Put the weapon away if the player has a pending weapon or has died. if (PlayerEx(Owner.Player).PendingWeapon || !Owner.Player.Health) { Owner.Player.SetViewState(ps_weapon, GetDownState()); return; } // Check for fire. if (Owner.Player.Buttons & BT_ATTACK) { if (!Owner.Player.bAttackDown || !PlayerEx(Owner.Player).ReadyWeapon.bNoAutoFire) { Owner.Player.bAttackDown = true; PlayerEx(Owner.Player).FireWeapon(); return; } } else { Owner.Player.bAttackDown = false; } if (!bDontBob) { // bob the weapon based on movement speed angle = AngleMod360(180.0 * XLevel.Time); Owner.Player.ViewStates[ps_weapon].SX = 1.0 + PlayerEx(Owner.Player).Bob * cos(angle); if (angle >= 180.0) { angle -= 180.0; } Owner.Player.ViewStates[ps_weapon].SY = WEAPONTOP + PlayerEx(Owner.Player).Bob * sin(angle); } } //========================================================================== // // A_Lower // // Lowers current weapon, and changes weapon at bottom. // //========================================================================== final void A_Lower() { if (EntityEx(Owner).PlayerIsMorphed()) { Owner.Player.ViewStates[ps_weapon].SY = WEAPONBOTTOM; } else { // FIXME!! if (Level.Game.frametime < 1.0 / 35.0) { Owner.Player.ViewStates[ps_weapon].SY += LOWERSPEED; } else { Owner.Player.ViewStates[ps_weapon].SY += LOWERSPEED * 35.0 * Level.Game.frametime; } } if (Owner.Player.ViewStates[ps_weapon].SY < WEAPONBOTTOM) { // Not lowered all the way yet return; } if (Owner.Player.PlayerState == PST_DEAD) { // Player is dead, so don't bring up a pending weapon Owner.Player.ViewStates[ps_weapon].SY = WEAPONBOTTOM; return; } if (!Owner.Player.Health) { // Player is dead, so keep the weapon off screen. Owner.Player.SetViewState(ps_weapon, none); return; } // Clear flash state, needed for Strife. Owner.Player.SetViewState(ps_flash, none); PlayerEx(Owner.Player).SetWeapon(PlayerEx(Owner.Player).PendingWeapon); PlayerEx(Owner.Player).BringUpWeapon(); } //========================================================================== // // A_Raise // //========================================================================== final void A_Raise() { // FIXME!! if (Level.Game.frametime < 1.0 / 35.0) Owner.Player.ViewStates[ps_weapon].SY -= RAISESPEED; else Owner.Player.ViewStates[ps_weapon].SY -= RAISESPEED * 35.0 * Level.Game.frametime; if (Owner.Player.ViewStates[ps_weapon].SY > WEAPONTOP) { // Not raised all the way yet return; } Owner.Player.ViewStates[ps_weapon].SY = WEAPONTOP; Owner.Player.SetViewState(ps_weapon, GetReadyState()); } //=========================================================================== // // A_ReFire // // The player can re-fire the weapon without lowering it entirely. // //=========================================================================== final void A_ReFire() { if ((Owner.Player.Buttons & BT_ATTACK) && !PlayerEx(Owner.Player).PendingWeapon && Owner.Player.Health) { PlayerEx(Owner.Player).Refire++; PlayerEx(Owner.Player).FireWeapon(); } else { PlayerEx(Owner.Player).Refire = 0; CheckAmmo(true); } } //=========================================================================== // // A_CheckReload // //=========================================================================== final void A_CheckReload() { CheckAmmo(true); } //=========================================================================== // // A_GunFlash // //=========================================================================== final void A_GunFlash() { Owner.SetState(EntityEx(Owner).MeleeState); Owner.Player.SetViewState(ps_flash, FindState('Flash')); } //=========================================================================== // // A_Light0 // //=========================================================================== final void A_Light0() { Owner.Player.ExtraLight = 0; EntityEx(Owner).bMuzzleFlash = false; } //=========================================================================== // // A_Light1 // //=========================================================================== final void A_Light1() { Owner.Player.ExtraLight = 1; } //=========================================================================== // // A_Light2 // //=========================================================================== final void A_Light2() { Owner.Player.ExtraLight = 2; } defaultproperties { PickupSound = 'misc/w_pkup'; }