rott/kf_sources/Engine/Classes/Weapon.uc
2026-07-14 20:27:09 +07:00

2139 lines
56 KiB
Ucode

class Weapon extends Inventory
abstract
native
nativereplication
HideDropDown
CacheExempt;
#exec Texture Import File=Textures\Weapon.tga Name=S_Weapon Mips=Off Alpha=1
replication
{
// Things the server should send to the client.
reliable if( Role==ROLE_Authority )
Ammo, AmmoCharge;
//if _RO_
// Things the server should send to the client.
reliable if( bNetDirty && Role==ROLE_Authority )
bBayonetMounted, bUsingSights;
// end _RO_
// Functions called by server on client
reliable if( Role==ROLE_Authority )
ClientWeaponSet, ClientWeaponThrown, ClientForceAmmoUpdate, ClientWriteStats, ClientWriteFire;
// functions called by client on server
reliable if( Role<ROLE_Authority )
ServerStartFire, ServerStopFire;
}
const NUM_FIRE_MODES = 2;
var() class<WeaponFire> FireModeClass[NUM_FIRE_MODES];
var() protected WeaponFire FireMode[NUM_FIRE_MODES];
var() protected Ammunition Ammo[NUM_FIRE_MODES];
// animation //
var() Name IdleAnim;
var() Name RestAnim;
var() Name AimAnim;
var() Name RunAnim;
var() Name SelectAnim;
var() Name PutDownAnim;
var() float IdleAnimRate;
var() float RestAnimRate;
var() float AimAnimRate;
var() float RunAnimRate;
var() float SelectAnimRate;
var() float PutDownAnimRate;
var float PutDownTime;
var float BringUpTime;
var() Sound SelectSound;
var() String SelectForce; // jdf
// AI //
var() int BotMode; // the fire Mode currently being used for bots
var() float AIRating;
var float CurrentRating; // rating result from most recent RateSelf()
var() bool bMeleeWeapon;
var() bool bSniping;
// other useful stuff //
var bool bShowChargingBar;
var bool bMatchWeapons; // OBSOLETE - see WeaponAttachment
var() bool bCanThrow;
var() bool bForceSwitch; // if true, this weapon will prevent any other weapon from delaying the switch to it (bomb launcher)
var() deprecated bool bNotInPriorityList; // Should be displayed in a GUI weapon list - refer to 'Description' documentation
var bool bNotInDemo;
var bool bNoVoluntarySwitch;
var bool bSpectated;
var bool bDebugging;
var bool bNoInstagibReplace;
var bool bInitOldMesh;
var config bool bUseOldWeaponMesh;
var bool bEndOfRound; // don't allow firing
var bool bNoAmmoInstances; // if true, replicated ammocount using the Weapons AmmoCharge property - true by default, included to allow mod authors to fallback to old style
var bool bBerserk;
// properties needed if no instantiated ammunition
var int AmmoCharge[2];
var class<Ammunition> AmmoClass[2];
var Mesh OldMesh;
var string OldPickup;
var(OldFirstPerson) float OldDrawScale, OldCenteredOffsetY;
var(OldFirstPerson) vector OldPlayerViewOffset, OldSmallViewOffset;
var(OldFirstPerson) rotator OldPlayerViewPivot;
var(OldFirstPerson) int OldCenteredRoll, OldCenteredYaw;
/*
A note about weapons & the caching system:
You must now perform two commands on your mod's final package file:
'ucc dumpint <PackageFileName.u>' - this exports the localized text to a localization file, which is used by the caching system to load the ItemName and Description
'ucc exportcache <PackageName.u>' - this exports the information that will be used to load the weapon into the caching system. Type 'ucc help exportcache' at the command-line for more info.
Ex: (ACoolWeaponMod.u)
ucc dumpint ACoolWeaponMod.u
- creates 'ACoolWeaponMod.int' file, (file extension will vary if a different language is specified in the UT2004.ini file)
ucc exportcache ACoolWeaponMod.u
- creates an entry in the 'CacheRecords.ucl' file, containing caching information for your package
Weapons must have values for both ItemName & Description in order to appear in the game. The caching system will not recognize inherited values for these properties.
If creating a custom crosshair for your weapon, it will FIXME
-- rjp
*/
var() localized cache string Description;
var class<Weapon> DemoReplacement;
var transient bool bPendingSwitch;
var(FirstPerson) vector EffectOffset; // where muzzle flashes and smoke appear. replace by bone reference eventually
var() Localized string MessageNoAmmo;
var(FirstPerson) float DisplayFOV;
var() enum EWeaponClientState
{
WS_None,
WS_Hidden,
WS_BringUp,
WS_PutDown,
WS_ReadyToFire
} ClientState; // this will always be None on the server
var() config byte ExchangeFireModes;
var() config byte Priority;
var() deprecated byte DefaultPriority;
var float Hand;
var float RenderedHand;
var Color HudColor;
var Weapon OldWeapon;
var(FirstPerson) vector SmallViewOffset; // Offset from view center with small weapons option.
var(FirstPerson) vector SmallEffectOffset;
var(FirstPerson) float CenteredOffsetY;
var(FirstPerson) int CenteredRoll, CenteredYaw;
var config int CustomCrosshair;
var config color CustomCrossHairColor;
var config float CustomCrossHairScale;
var config string CustomCrossHairTextureName;
var texture CustomCrossHairTexture;
var float DownDelay, MinReloadPct; // Used to delay putting down weapons which have jsut been fired
// ROVariables - put here to avoid casting
// if _RO_
var bool bBayonetMounted; // If true this weapon has a bayonet mounted
var bool bCanAttachOnBack;// True if this weapon can be placed on the pawns back
var bool bCanRestDeploy;// True if this weapon can be deployed by resting it on something
var bool bCanBipodDeploy;// True if this weapon can be deployed using a bipod
var bool bBipodDeployed; // True if the weapon is deployed on its bipod
var float LastStartCrawlingTime; // Used in smoothing out transitions to/from crawling forward/back
var bool bUsingSights; // True if the weapon is zoomed in using the sights
var bool bUsesFreeAim; // Whether or not this weapon uses free-aim
var bool bCanSway; // This weapon can sway in ironsights (false for binocs)
// end _RO_
native final function InitWeaponFires();
simulated function float ChargeBar();
// ROFunctions - put here to avoid casting
// if _RO_
simulated function bool WeaponCanSwitch() {return true;}
simulated function bool WeaponCanBusySwitch() {return false;}
simulated function bool WeaponAllowSprint() {return true;}
simulated function bool WeaponAllowProneChange() {return true;}
simulated function bool WeaponAllowCrouchChange() {return true;}
simulated function bool IsMounted() {return false;}
simulated function bool IsCrawling() {return false;}
simulated function ROIronSights();
simulated function int GetHudAmmoCount();
simulated function NotifyOwnerJumped();
function SetServerOrientation(rotator NewRotation);
function coords GetMuzzleCoords();
simulated function bool ShouldUseFreeAim() {return false;}
simulated function PostFire();
// end _RO_
// Called by physics when the player is crawling
simulated event NotifyCrawlMoving();
simulated event NotifyStopCrawlMoving();
//=========================================================================
// Ammunition Interface (to remove the need for instantiated ammunition)
simulated function class<Ammunition> GetAmmoClass(int mode)
{
return AmmoClass[mode];
}
simulated function MaxOutAmmo()
{
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] != None )
AmmoCharge[0] = MaxAmmo(0);
if ( (AmmoClass[1] != None) && (AmmoClass[0] != AmmoClass[1]) )
AmmoCharge[1] = MaxAmmo(1);
return;
}
if ( Ammo[0] != None )
Ammo[0].AmmoAmount = Ammo[0].MaxAmmo;
if ( Ammo[1] != None )
Ammo[1].AmmoAmount = Ammo[1].MaxAmmo;
}
simulated function SuperMaxOutAmmo()
{
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] != None )
AmmoCharge[0] = 999;
if ( (AmmoClass[1] != None) && (AmmoClass[0] != AmmoClass[1]) )
AmmoCharge[1] = 999;
return;
}
if ( Ammo[0] != None )
Ammo[0].AmmoAmount = 999;
if ( Ammo[1] != None )
Ammo[1].AmmoAmount = 999;
}
simulated function int MaxAmmo(int mode)
{
if ( AmmoClass[mode] != None )
return AmmoClass[mode].Default.MaxAmmo;
return 0;
}
simulated function FillToInitialAmmo()
{
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] != None )
AmmoCharge[0] = Max(AmmoCharge[0], AmmoClass[0].Default.InitialAmount);
if ( (AmmoClass[1] != None) && (AmmoClass[0] != AmmoClass[1]) )
AmmoCharge[1] = Max(AmmoCharge[1], AmmoClass[1].Default.InitialAmount);
return;
}
if ( Ammo[0] != None )
Ammo[0].AmmoAmount = Max(Ammo[0].AmmoAmount,Ammo[0].InitialAmount);
if ( Ammo[1] != None )
Ammo[1].AmmoAmount = Max(Ammo[1].AmmoAmount,Ammo[1].InitialAmount);
}
simulated function int AmmoAmount(int mode)
{
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] == AmmoClass[mode] )
return AmmoCharge[0];
return AmmoCharge[mode];
}
if ( Ammo[mode] != None )
return Ammo[mode].AmmoAmount;
return 0;
}
simulated function class<Pickup> AmmoPickupClass(int mode)
{
if ( AmmoClass[mode] != None )
return FireMode[mode].AmmoClass.Default.PickupClass;
return None;
}
simulated function bool AmmoMaxed(int mode)
{
if ( AmmoClass[mode] == None )
return false;
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] == AmmoClass[mode] )
mode = 0;
return AmmoCharge[mode] >= MaxAmmo(mode);
}
if ( Ammo[mode] == None )
return false;
return (Ammo[mode].AmmoAmount >= MaxAmmo(mode));
}
simulated function GetAmmoCount(out float MaxAmmoPrimary, out float CurAmmoPrimary)
{
if ( AmmoClass[0] == None )
return;
if ( bNoAmmoInstances )
{
MaxAmmoPrimary = MaxAmmo(0);
CurAmmoPrimary = AmmoCharge[0];
return;
}
if ( Ammo[0] == None )
return;
MaxAmmoPrimary = Ammo[0].MaxAmmo;
CurAmmoPrimary = Ammo[0].AmmoAmount;
}
simulated function float AmmoStatus(optional int Mode) // returns float value for ammo amount
{
if ( bNoAmmoInstances )
{
if ( AmmoClass[Mode] == None )
return 0;
if ( AmmoClass[0] == AmmoClass[mode] )
mode = 0;
return float(AmmoCharge[Mode])/float(MaxAmmo(Mode));
}
if (Ammo[Mode] == None)
return 0.0;
else
return float(Ammo[Mode].AmmoAmount) / float(Ammo[Mode].MaxAmmo);
}
simulated function bool ConsumeAmmo(int Mode, float load, optional bool bAmountNeededIsMax)
{
local int AmountNeeded;
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] == AmmoClass[mode] )
mode = 0;
AmountNeeded = int(load);
if (bAmountNeededIsMax && AmmoCharge[mode] < AmountNeeded)
AmountNeeded = AmmoCharge[mode];
if (AmmoCharge[mode] < AmountNeeded)
{
CheckOutOfAmmo();
return false; // Can't do it
}
AmmoCharge[mode] -= AmountNeeded;
NetUpdateTime = Level.TimeSeconds - 1;
if (Level.NetMode == NM_StandAlone || Level.NetMode == NM_ListenServer)
CheckOutOfAmmo();
return true;
}
if (Ammo[Mode] != None)
return Ammo[Mode].UseAmmo(int(load), bAmountNeededIsMax);
return true;
}
function bool AddAmmo(int AmmoToAdd, int Mode)
{
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] == AmmoClass[mode] )
mode = 0;
if ( Level.GRI.WeaponBerserk > 1.0 )
AmmoCharge[mode] = MaxAmmo(Mode);
else if ( AmmoCharge[mode] < MaxAmmo(mode) )
AmmoCharge[mode] = Min(MaxAmmo(mode), AmmoCharge[mode]+AmmoToAdd);
NetUpdateTime = Level.TimeSeconds - 1;
return true;
}
if (Ammo[Mode] != None)
return Ammo[Mode].AddAmmo(AmmoToAdd);
}
simulated function bool HasAmmo()
{
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] == AmmoClass[1] )
return ( (AmmoClass[0] != None && FireMode[0] != None && AmmoCharge[0] >= FireMode[0].AmmoPerFire) );
return ( (AmmoClass[0] != None && FireMode[0] != None && AmmoCharge[0] >= FireMode[0].AmmoPerFire)
|| (AmmoClass[1] != None && FireMode[1] != None && AmmoCharge[1] >= FireMode[1].AmmoPerFire) );
}
return ( (Ammo[0] != None && FireMode[0] != None && Ammo[0].AmmoAmount >= FireMode[0].AmmoPerFire)
|| (Ammo[1] != None && FireMode[1] != None && Ammo[1].AmmoAmount >= FireMode[1].AmmoPerFire) );
}
// for AI
simulated function bool NeedAmmo(int mode)
{
if ( bNoAmmoInstances )
{
if ( AmmoClass[0] == AmmoClass[mode] )
mode = 0;
if ( AmmoClass[mode] == None )
return false;
return ( AmmoCharge[Mode] < AmmoClass[mode].default.InitialAmount );
}
if ( Ammo[mode] != None )
return (Ammo[mode].AmmoAmount < Ammo[mode].InitialAmount);
return false;
}
simulated function float DesireAmmo(class<Inventory> NewAmmoClass, bool bDetour)
{
local int i;
local float curr, max;
for ( i=0; i<2; i++ )
if ( NewAmmoClass == AmmoClass[i] )
{
if ( AmmoMaxed(i) )
return -100;
curr = AmmoAmount(i);
if ( curr == 0 )
return 1;
max = MaxAmmo(i);
return ( FMin(0.5*(max-curr),AmmoClass[i].Default.AmmoAmount)/max );
}
return 0;
}
simulated function CheckOutOfAmmo()
{
if (Instigator != None && Instigator.Weapon == self)
{
if ( bNoAmmoInstances )
{
if ( (AmmoCharge[0] <= 0) && (AmmoCharge[1] <= 0) )
OutOfAmmo();
return;
}
if ( Ammo[0] != None )
Ammo[0].CheckOutOfAmmo();
if ( Ammo[1] != None )
Ammo[1].CheckOutOfAmmo();
}
}
simulated function PostNetReceive()
{
CheckOutOfAmmo();
}
//=========================================================================
simulated function DrawWeaponInfo(Canvas C);
simulated function NewDrawWeaponInfo(Canvas C, float YPos);
function StartDebugging()
{
}
simulated function ClientWriteStats(byte Mode, bool bMatch, bool bAllowFire, bool bDelay, bool bAlt, float wait)
{
log(self$" Same weapon "$bMatch$" Mode "$Mode$" Allow fire "$bAllowFire$" delay start fire "$bDelay$" alt firing "$bAlt$" next fire wait "$wait);
}
function class<DamageType> GetDamageType();
function HackPlayFireSound()
{
if ( (FireMode[0] != None) && (FireMode[0].FireSound != None) )
PlaySound(FireMode[0].FireSound, SLOT_None, 1.0);
}
//=================================================================
// AI functions
function float RangedAttackTime()
{
return 0;
}
function bool RecommendRangedAttack()
{
return false;
}
function bool RecommendLongRangedAttack()
{
return false;
}
function bool FocusOnLeader(bool bLeaderFiring)
{
return false;
}
function FireHack(byte Mode);
// return true if weapon effect has splash damage (if significant)
// use by bot to avoid hurting self
// should be based on current firing Mode if active
function bool SplashDamage()
{
return FireMode[BotMode].bSplashDamage;
}
// return true if weapon should be fired to take advantage of splash damage
// For example, rockets should be fired at enemy feet
function bool RecommendSplashDamage()
{
return FireMode[BotMode].bRecommendSplashDamage;
}
function float GetDamageRadius()
{
if (FireMode[BotMode].ProjectileClass == None)
return 0;
else
return FireMode[BotMode].ProjectileClass.default.DamageRadius;
}
// Repeater weapons like minigun should be 0.99, other weapons based on likelihood
// of firing again right away
function float RefireRate()
{
return FireMode[BotMode].BotRefireRate;
}
// tells AI that it needs to release the fire button for this weapon to do anything
function bool FireOnRelease()
{
return FireMode[BotMode].bFireOnRelease;
}
simulated function Loaded();
simulated function DisplayDebug(Canvas Canvas, out float YL, out float YPos)
{
local int i;
local string T;
local name Anim;
local float frame,rate;
Canvas.SetDrawColor(0,255,0);
if ( (Pawn(Owner) != None) && (Pawn(Owner).PlayerReplicationInfo != None) )
Canvas.DrawText("WEAPON "$GetItemName(string(self))$" Owner "$Pawn(Owner).PlayerReplicationInfo.PlayerName);
else
Canvas.DrawText("WEAPON "$GetItemName(string(self))$" Owner "$Owner);
YPos += YL;
Canvas.SetPos(4,YPos);
T = " STATE: "$GetStateName()$" Timer: "$TimerCounter$" Client State ";
Switch( ClientState )
{
case WS_None: T=T$"None"; break;
case WS_Hidden: T=T$"Hidden"; break;
case WS_BringUp: T=T$"BringUp"; break;
case WS_PutDown: T=T$"PutDown"; break;
case WS_ReadyToFire: T=T$"ReadyToFire"; break;
}
Canvas.DrawText(T, false);
YPos += YL;
Canvas.SetPos(4,YPos);
if ( DrawType == DT_StaticMesh )
Canvas.DrawText(" StaticMesh "$GetItemName(string(StaticMesh))$" AmbientSound "$AmbientSound, false);
else
Canvas.DrawText(" Mesh "$GetItemName(string(Mesh))$" AmbientSound "$AmbientSound, false);
YPos += YL;
Canvas.SetPos(4,YPos);
if ( Mesh != None )
{
// mesh animation
GetAnimParams(0,Anim,frame,rate);
T = " AnimSequence "$Anim$" Frame "$frame$" Rate "$rate;
if ( bAnimByOwner )
T= T$" Anim by Owner";
Canvas.DrawText(T, false);
YPos += YL;
Canvas.SetPos(4,YPos);
T = "Eyeheight "$Instigator.EyeHeight$" base "$Instigator.BaseEyeheight$" landbob "$Instigator.Landbob$" just landed "$Instigator.bJustLanded$" land recover "$Instigator.bLandRecovery;
Canvas.DrawText(T, false);
YPos += YL;
Canvas.SetPos(4,YPos);
}
for ( i=0; i<NUM_FIRE_MODES; i++ )
{
if ( FireMode[i] == None )
{
Canvas.DrawText("NO FIREMODE "$i);
YPos += YL;
Canvas.SetPos(4,YPos);
}
else
FireMode[i].DisplayDebug(Canvas,YL,YPos);
Canvas.DrawText("Ammunition "$i$" amount "$AmmoAmount(i));
YPos += YL;
Canvas.SetPos(4,YPos);
}
}
simulated function Weapon RecommendWeapon( out float rating )
{
local Weapon Recommended;
local float oldRating;
if ( (Instigator == None) || (Instigator.Controller == None) )
rating = -2;
else
rating = RateSelf() + Instigator.Controller.WeaponPreference(self);
if ( inventory != None )
{
Recommended = inventory.RecommendWeapon(oldRating);
if ( (Recommended != None) && (oldRating > rating) )
{
rating = oldRating;
return Recommended;
}
}
return self;
}
function SetAITarget(Actor T);
/* BestMode()
choose between regular or alt-fire
*/
function byte BestMode()
{
if ( Instigator.Controller.bFire != 0 )
return 0;
else if ( Instigator.Controller.bAltFire != 0 )
return 1;
if ( FRand() < 0.5 )
return 1;
return 0;
}
/* BotFire()
called by NPC firing weapon. Weapon chooses appropriate firing Mode to use (typically no change)
bFinished should only be true if called from the Finished() function
FiringMode can be passed in to specify a firing Mode (used by scripted sequences)
*/
function bool BotFire(bool bFinished, optional name FiringMode)
{
local int newmode;
local Controller C;
C = Instigator.Controller;
newMode = BestMode();
if ( newMode == 0 )
{
C.bFire = 1;
C.bAltFire = 0;
}
else
{
C.bFire = 0;
C.bAltFire = 1;
}
if ( bFinished )
return true;
if ( FireMode[BotMode].IsFiring() )
{
if (BotMode == newMode)
return true;
else
StopFire(BotMode);
}
if ( !ReadyToFire(newMode) || ClientState != WS_ReadyToFire )
return false;
BotMode = NewMode;
StartFire(NewMode);
return true;
}
// this returns the actual projectile spawn location or trace start
simulated function vector GetFireStart(vector X, vector Y, vector Z)
{
return FireMode[BotMode].GetFireStart(X,Y,Z);
}
// need to figure out modified rating based on enemy/tactical situation
simulated function float RateSelf()
{
if ( !HasAmmo() )
CurrentRating = -2;
else if ( Instigator.Controller == None )
return 0;
else
CurrentRating = Instigator.Controller.RateWeapon(self);
return CurrentRating;
}
function float GetAIRating()
{
return AIRating;
}
// tells bot whether to charge or back off while using this weapon
function float SuggestAttackStyle()
{
return 0.0;
}
// tells bot whether to charge or back off while defending against this weapon
function float SuggestDefenseStyle()
{
return 0.0;
}
// return true if recommend jumping while firing to improve splash damage (by shooting at feet)
// true for R.L., for example
function bool SplashJump()
{
return FireMode[BotMode].bSplashJump;
}
// return false if out of range, can't see target, etc.
function bool CanAttack(Actor Other)
{
local float Dist, CheckDist;
local vector HitLocation, HitNormal,X,Y,Z, projStart;
local actor HitActor;
local int m;
local bool bInstantHit;
local bool bShouldAttack;
// if _RO_
// Extra friendly fire checking
local pawn Victims;
local vector dir, lookdir;
local float DiffAngle, FriendlyDist, MinAngle;
// end _RO_
if ( (Instigator == None) || (Instigator.Controller == None) )
return false;
// check that target is within range
Dist = VSize(Instigator.Location - Other.Location);
if ( (Dist > FireMode[0].MaxRange()) && (Dist > FireMode[1].MaxRange()) )
return false;
// check that can see target
if ( !Instigator.Controller.LineOfSightTo(Other) )
return false;
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if ( FireMode[m].bInstantHit )
bInstantHit = true;
else
{
CheckDist = FMax(CheckDist, 0.5 * FireMode[m].ProjectileClass.Default.Speed);
CheckDist = FMax(CheckDist, 300);
CheckDist = FMin(CheckDist, VSize(Other.Location - Location));
}
}
// check that would hit target, and not a friendly
GetAxes(Instigator.Controller.Rotation, X,Y,Z);
projStart = GetFireStart(X,Y,Z);
if ( bInstantHit )
HitActor = Instigator.Trace(HitLocation, HitNormal, Other.Location + Other.CollisionHeight * vect(0,0,0.8), projStart, true);
else
{
// for non-instant hit, only check partial path (since others may move out of the way)
HitActor = Instigator.Trace(HitLocation, HitNormal,
projStart + CheckDist * Normal(Other.Location + Other.CollisionHeight * vect(0,0,0.8) - Location),
projStart, true);
}
// if( (HitActor != None) && HitActor.IsA('ROCollisionAttachment'))
// {
// log(self$"'s trace hit "$HitActor.Base$" Collision attachment");
// }
// if _RO_
// Extra friendly fire checking
if (!Instigator.IsHumanControlled() )
{
foreach VisibleCollidingActors( class 'Pawn', Victims, 3000 ) //, RadiusHitLocation
{
if( Victims != Instigator && Instigator.Controller.SameTeamAs(Victims.Controller))
{
FriendlyDist = VSizeSquared(Instigator.Location - Victims.Location);
if( FriendlyDist < 22500 ) //2.5 meters
{
MinAngle = 0.85;
}
else if( FriendlyDist < 90000 ) //5.0 meters
{
MinAngle = 0.95;
}
else if( FriendlyDist < 360000 ) //10.0 meters
{
MinAngle = 0.98;
}
else
{
MinAngle = 0.99;
}
lookdir = Normal((Other.Location + Other.CollisionHeight * vect(0,0,0.8))-projStart);
dir = Normal(Victims.Location - Instigator.Location);
DiffAngle = lookdir dot dir;
if( DiffAngle > MinAngle )
{
//log("Not firing because we might hit a friendly with a DiffAngle of "$DiffAngle);
return false;
}
}
}
}
if ( (HitActor == None) || (HitActor == Other) )
bShouldAttack = true;//return true;
else if ( Pawn(HitActor) == None )
bShouldAttack = !HitActor.BlocksShotAt(Other);//return !HitActor.BlocksShotAt(Other);
else if ( (Pawn(HitActor).Controller == None) || !Instigator.Controller.SameTeamAs(Pawn(HitActor).Controller) )
bShouldAttack = true;//return true;
// if( Pawn(HitActor) != none && Instigator.Controller.SameTeamAs(Pawn(HitActor).Controller))
// {
// log("Weapon "$Instigator$"'s shot would hit "$Other$" who is on the same team and should attack is "$bShouldAttack);
// }
return bShouldAttack;//false;
}
//=================================================================
simulated function PostBeginPlay()
{
local int m;
Super.PostBeginPlay();
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if (FireModeClass[m] != None)
{
FireMode[m] = new(self) FireModeClass[m];
if ( FireMode[m] != None )
AmmoClass[m] = FireMode[m].AmmoClass;
}
}
InitWeaponFires();
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if (FireMode[m] != None)
{
FireMode[m].ThisModeNum = m;
FireMode[m].Weapon = self;
FireMode[m].Instigator = Instigator;
FireMode[m].Level = Level;
FireMode[m].Owner = self;
FireMode[m].PreBeginPlay();
FireMode[m].BeginPlay();
FireMode[m].PostBeginPlay();
FireMode[m].SetInitialState();
FireMode[m].PostNetBeginPlay();
}
}
if ( Level.bDropDetail || (Level.DetailMode == DM_Low) )
MaxLights = Min(4,MaxLights);
if ( SmallViewOffset == vect(0,0,0) )
SmallViewOffset = Default.PlayerviewOffset;
if ( SmallEffectOffset == vect(0,0,0) )
SmallEffectOffset = EffectOffset + Default.PlayerViewOffset - SmallViewOffset;
if ( bUseOldWeaponMesh && (OldMesh != None) )
{
bInitOldMesh = true;
LinkMesh(OldMesh);
}
if ( Level.GRI != None )
CheckSuperBerserk();
}
simulated function SetGRI(GameReplicationInfo G)
{
CheckSuperBerserk();
}
simulated function Destroyed()
{
local int m;
AmbientSound = None;
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if ( FireMode[m] != None )
FireMode[m].DestroyEffects();
if (Ammo[m] != None)
{
Ammo[m].Destroy();
Ammo[m] = None;
}
}
Super.Destroyed();
}
simulated function Reselect()
{
}
simulated function bool WeaponCentered()
{
return ( bSpectated || (Hand > 1) );
}
simulated event RenderOverlays( Canvas Canvas )
{
local int m;
local vector NewScale3D;
local rotator CenteredRotation;
local name AnimSeq;
local float frame,rate;
if (Instigator == None)
return;
if ( Instigator.Controller != None )
Hand = Instigator.Controller.Handedness;
if ((Hand < -1.0) || (Hand > 1.0))
return;
// draw muzzleflashes/smoke for all fire modes so idle state won't
// cause emitters to just disappear
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if (FireMode[m] != None)
{
FireMode[m].DrawMuzzleFlash(Canvas);
}
}
if ( (OldMesh != None) && (bUseOldWeaponMesh != (OldMesh == Mesh)) )
{
GetAnimParams(0,AnimSeq,frame,rate);
bInitOldMesh = true;
if ( bUseOldWeaponMesh )
LinkMesh(OldMesh);
else
LinkMesh(Default.Mesh);
PlayAnim(AnimSeq,rate,0.0);
}
if ( (Hand != RenderedHand) || bInitOldMesh )
{
newScale3D = Default.DrawScale3D;
if ( Hand != 0 )
newScale3D.Y *= Hand;
SetDrawScale3D(newScale3D);
SetDrawScale(Default.DrawScale);
CenteredRoll = Default.CenteredRoll;
CenteredYaw = Default.CenteredYaw;
CenteredOffsetY = Default.CenteredOffsetY;
PlayerViewPivot = Default.PlayerViewPivot;
SmallViewOffset = Default.SmallViewOffset;
if ( SmallViewOffset == vect(0,0,0) )
SmallViewOffset = Default.PlayerviewOffset;
bInitOldMesh = false;
if ( Default.SmallEffectOffset == vect(0,0,0) )
SmallEffectOffset = EffectOffset + Default.PlayerViewOffset - SmallViewOffset;
else
SmallEffectOffset = Default.SmallEffectOffset;
if ( Mesh == OldMesh )
{
SmallEffectOffset = EffectOffset + OldPlayerViewOffset - OldSmallViewOffset;
PlayerViewPivot = OldPlayerViewPivot;
SmallViewOffset = OldSmallViewOffset;
if ( Hand != 0 )
{
PlayerViewPivot.Roll *= Hand;
PlayerViewPivot.Yaw *= Hand;
}
CenteredRoll = OldCenteredRoll;
CenteredYaw = OldCenteredYaw;
CenteredOffsetY = OldCenteredOffsetY;
SetDrawScale(OldDrawScale);
}
else if ( Hand == 0 )
{
PlayerViewPivot.Roll = Default.PlayerViewPivot.Roll;
PlayerViewPivot.Yaw = Default.PlayerViewPivot.Yaw;
}
else
{
PlayerViewPivot.Roll = Default.PlayerViewPivot.Roll * Hand;
PlayerViewPivot.Yaw = Default.PlayerViewPivot.Yaw * Hand;
}
RenderedHand = Hand;
}
if ( class'PlayerController'.Default.bSmallWeapons )
PlayerViewOffset = SmallViewOffset;
else if ( Mesh == OldMesh )
PlayerViewOffset = OldPlayerViewOffset;
else
PlayerViewOffset = Default.PlayerViewOffset;
if ( Hand == 0 )
PlayerViewOffset.Y = CenteredOffsetY;
else
PlayerViewOffset.Y *= Hand;
SetLocation( Instigator.Location + Instigator.CalcDrawOffset(self) );
if ( Hand == 0 )
{
CenteredRotation = Instigator.GetViewRotation();
CenteredRotation.Yaw += CenteredYaw;
CenteredRotation.Roll = CenteredRoll;
SetRotation(CenteredRotation);
}
else
SetRotation( Instigator.GetViewRotation() );
PreDrawFPWeapon(); // Laurent -- Hook to override things before render (like rotation if using a staticmesh)
bDrawingFirstPerson = true;
Canvas.DrawActor(self, false, false, DisplayFOV);
bDrawingFirstPerson = false;
if ( Hand == 0 )
PlayerViewOffset.Y = 0;
}
simulated function PreDrawFPWeapon();
simulated function SetHand(float InHand)
{
Hand = InHand;
}
simulated function GetViewAxes( out vector xaxis, out vector yaxis, out vector zaxis )
{
if ( Instigator.Controller == None )
GetAxes( Instigator.Rotation, xaxis, yaxis, zaxis );
else
GetAxes( Instigator.Controller.Rotation, xaxis, yaxis, zaxis );
}
simulated function vector CenteredEffectStart()
{
return Instigator.Location;
}
simulated function vector GetEffectStart()
{
local Vector X,Y,Z;
// jjs - this function should actually never be called in third person views
// any effect that needs a 3rdp weapon offset should figure it out itself
// 1st person
if (Instigator.IsFirstPerson())
{
if ( WeaponCentered() )
return CenteredEffectStart();
GetViewAxes(X, Y, Z);
if ( class'PlayerController'.Default.bSmallWeapons )
return (Instigator.Location +
Instigator.CalcDrawOffset(self) +
SmallEffectOffset.X * X +
SmallEffectOffset.Y * Y * Hand +
SmallEffectOffset.Z * Z);
else
return (Instigator.Location +
Instigator.CalcDrawOffset(self) +
EffectOffset.X * X +
EffectOffset.Y * Y * Hand +
EffectOffset.Z * Z);
}
// 3rd person
else
{
return (Instigator.Location +
Instigator.EyeHeight*Vect(0,0,0.5) +
Vector(Instigator.Rotation) * 40.0);
}
}
simulated function IncrementFlashCount(int Mode)
{
if ( WeaponAttachment(ThirdPersonActor) != None )
{
if (Mode == 0)
WeaponAttachment(ThirdPersonActor).FiringMode = 0;
else
WeaponAttachment(ThirdPersonActor).FiringMode = 1;
ThirdPersonActor.NetUpdateTime = Level.TimeSeconds - 1;
WeaponAttachment(ThirdPersonActor).FlashCount++;
WeaponAttachment(ThirdPersonActor).ThirdPersonEffects();
}
}
simulated function ZeroFlashCount(int Mode)
{
if ( WeaponAttachment(ThirdPersonActor) != None )
{
if (Mode == 0)
WeaponAttachment(ThirdPersonActor).FiringMode = 0;
else
WeaponAttachment(ThirdPersonActor).FiringMode = 1;
ThirdPersonActor.NetUpdateTime = Level.TimeSeconds - 1;
WeaponAttachment(ThirdPersonActor).FlashCount = 0;
WeaponAttachment(ThirdPersonActor).ThirdPersonEffects();
}
}
simulated function Weapon WeaponChange( byte F, bool bSilent )
{
local Weapon newWeapon;
if ( InventoryGroup == F )
{
if ( !HasAmmo() )
{
if ( Inventory == None )
newWeapon = None;
else
newWeapon = Inventory.WeaponChange(F,bSilent);
if ( !bSilent && (newWeapon == None) && Instigator.IsHumanControlled() )
Instigator.ClientMessage( ItemName$MessageNoAmmo );
return newWeapon;
}
else
return self;
}
else if ( Inventory == None )
return None;
else
return Inventory.WeaponChange(F,bSilent);
}
simulated function Weapon PrevWeapon(Weapon CurrentChoice, Weapon CurrentWeapon)
{
if ( HasAmmo() )
{
if ( (CurrentChoice == None) )
{
if ( CurrentWeapon != self )
CurrentChoice = self;
}
else if ( InventoryGroup == CurrentWeapon.InventoryGroup )
{
if ( (GroupOffset < CurrentWeapon.GroupOffset)
&& ((CurrentChoice.InventoryGroup != InventoryGroup) || (GroupOffset > CurrentChoice.GroupOffset)) )
CurrentChoice = self;
}
else if ( InventoryGroup == CurrentChoice.InventoryGroup )
{
if ( GroupOffset > CurrentChoice.GroupOffset )
CurrentChoice = self;
}
else if ( InventoryGroup > CurrentChoice.InventoryGroup )
{
if ( (InventoryGroup < CurrentWeapon.InventoryGroup)
|| (CurrentChoice.InventoryGroup > CurrentWeapon.InventoryGroup) )
CurrentChoice = self;
}
else if ( (CurrentChoice.InventoryGroup > CurrentWeapon.InventoryGroup)
&& (InventoryGroup < CurrentWeapon.InventoryGroup) )
CurrentChoice = self;
}
if ( Inventory == None )
return CurrentChoice;
else
return Inventory.PrevWeapon(CurrentChoice,CurrentWeapon);
}
simulated function Weapon NextWeapon(Weapon CurrentChoice, Weapon CurrentWeapon)
{
if ( HasAmmo() )
{
if ( (CurrentChoice == None) )
{
if ( CurrentWeapon != self )
CurrentChoice = self;
}
else if ( InventoryGroup == CurrentWeapon.InventoryGroup )
{
if ( (GroupOffset > CurrentWeapon.GroupOffset)
&& ((CurrentChoice.InventoryGroup != InventoryGroup) || (GroupOffset < CurrentChoice.GroupOffset)) )
CurrentChoice = self;
}
else if ( InventoryGroup == CurrentChoice.InventoryGroup )
{
if ( GroupOffset < CurrentChoice.GroupOffset )
CurrentChoice = self;
}
else if ( InventoryGroup < CurrentChoice.InventoryGroup )
{
if ( (InventoryGroup > CurrentWeapon.InventoryGroup)
|| (CurrentChoice.InventoryGroup < CurrentWeapon.InventoryGroup) )
CurrentChoice = self;
}
else if ( (CurrentChoice.InventoryGroup < CurrentWeapon.InventoryGroup)
&& (InventoryGroup > CurrentWeapon.InventoryGroup) )
CurrentChoice = self;
}
if ( Inventory == None )
return CurrentChoice;
else
return Inventory.NextWeapon(CurrentChoice,CurrentWeapon);
}
function HolderDied()
{
local int m;
for (m = 0; m < NUM_FIRE_MODES; m++)
{
// if _RO_
if( FireMode[m] == none )
continue;
// End _RO_
if (FireMode[m].bIsFiring)
{
StopFire(m);
if (FireMode[m].bFireOnRelease)
FireMode[m].ModeDoFire();
}
}
}
simulated function bool CanThrow()
{
local int Mode;
for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
{
// if _RO_
if( FireMode[Mode] == none )
continue;
// End _RO_
if ( FireMode[Mode].bFireOnRelease && FireMode[Mode].bIsFiring )
return false;
if ( FireMode[Mode].NextFireTime > Level.TimeSeconds)
return false;
}
return (bCanThrow && (ClientState == WS_ReadyToFire || (Level.NetMode == NM_DedicatedServer) || (Level.NetMode == NM_ListenServer))
&& HasAmmo() );
}
function DropFrom(vector StartLocation)
{
local int m;
local Pickup Pickup;
if (!bCanThrow || !HasAmmo())
return;
ClientWeaponThrown();
for (m = 0; m < NUM_FIRE_MODES; m++)
{
// if _RO_
if( FireMode[m] == none )
continue;
// End _RO_
if (FireMode[m].bIsFiring)
StopFire(m);
}
if ( Instigator != None )
{
DetachFromPawn(Instigator);
}
Pickup = Spawn(PickupClass,,, StartLocation);
if ( Pickup != None )
{
Pickup.InitDroppedPickupFor(self);
Pickup.Velocity = Velocity;
if (Instigator.Health > 0)
WeaponPickup(Pickup).bThrown = true;
}
Destroy();
}
simulated function DetachFromPawn(Pawn P)
{
Super.DetachFromPawn(P);
P.AmbientSound = None;
}
simulated function ClientWeaponThrown()
{
local int m;
AmbientSound = None;
Instigator.AmbientSound = None;
if( Level.NetMode != NM_Client )
return;
Instigator.DeleteInventory(self);
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if (Ammo[m] != None)
Instigator.DeleteInventory(Ammo[m]);
}
}
function GiveTo(Pawn Other, optional Pickup Pickup)
{
local int m;
local weapon w;
local bool bPossiblySwitch, bJustSpawned;
Instigator = Other;
W = Weapon(Instigator.FindInventoryType(class));
if ( W == None || W.Class != Class ) // added class check because somebody made FindInventoryType() return subclasses for some reason
{
bJustSpawned = true;
Super.GiveTo(Other);
bPossiblySwitch = true;
W = self;
}
else if ( !W.HasAmmo() )
bPossiblySwitch = true;
if ( Pickup == None )
bPossiblySwitch = true;
for (m = 0; m < NUM_FIRE_MODES; m++)
{
if ( FireMode[m] != None )
{
FireMode[m].Instigator = Instigator;
W.GiveAmmo(m,WeaponPickup(Pickup),bJustSpawned);
}
}
if ( Instigator.Weapon != W )
W.ClientWeaponSet(bPossiblySwitch);
if ( !bJustSpawned )
{
for (m = 0; m < NUM_FIRE_MODES; m++)
Ammo[m] = None;
Destroy();
}
}
function GiveAmmo(int m, WeaponPickup WP, bool bJustSpawned)
{
local bool bJustSpawnedAmmo;
local int addAmount, InitialAmount;
if ( FireMode[m] != None && FireMode[m].AmmoClass != None )
{
Ammo[m] = Ammunition(Instigator.FindInventoryType(FireMode[m].AmmoClass));
bJustSpawnedAmmo = false;
if ( bNoAmmoInstances )
{
if ( (FireMode[m].AmmoClass == None) || ((m != 0) && (FireMode[m].AmmoClass == FireMode[0].AmmoClass)) )
return;
InitialAmount = FireMode[m].AmmoClass.Default.InitialAmount;
if ( (WP != None) && ((WP.AmmoAmount[0] > 0) || (WP.AmmoAmount[1] > 0)) )
{
InitialAmount = WP.AmmoAmount[m];
}
if ( Ammo[m] != None )
{
addamount = InitialAmount + Ammo[m].AmmoAmount;
Ammo[m].Destroy();
}
else
addAmount = InitialAmount;
AddAmmo(addAmount,m);
}
else
{
if ( (Ammo[m] == None) && (FireMode[m].AmmoClass != None) )
{
Ammo[m] = Spawn(FireMode[m].AmmoClass, Instigator);
Instigator.AddInventory(Ammo[m]);
bJustSpawnedAmmo = true;
}
else if ( (m == 0) || (FireMode[m].AmmoClass != FireMode[0].AmmoClass) )
bJustSpawnedAmmo = ( bJustSpawned || ((WP != None) && !WP.bWeaponStay) );
if ( (WP != None) && ((WP.AmmoAmount[0] > 0) || (WP.AmmoAmount[1] > 0)) )
{
addAmount = WP.AmmoAmount[m];
}
else if ( bJustSpawnedAmmo )
{
addAmount = Ammo[m].InitialAmount;
}
Ammo[m].AddAmmo(addAmount);
Ammo[m].GotoState('');
}
}
}
simulated function ClientWeaponSet(bool bPossiblySwitch)
{
local int Mode;
Instigator = Pawn(Owner);
bPendingSwitch = bPossiblySwitch;
if( Instigator == None )
{
GotoState('PendingClientWeaponSet');
return;
}
for( Mode = 0; Mode < NUM_FIRE_MODES; Mode++ )
{
if( FireModeClass[Mode] != None )
{
// laurent -- added check for vehicles (ammo not replicated but unlimited)
if( ( FireMode[Mode] == None ) || ( FireMode[Mode].AmmoClass != None ) && !bNoAmmoInstances && Ammo[Mode] == None && FireMode[Mode].AmmoPerFire > 0 )
{
GotoState('PendingClientWeaponSet');
return;
}
}
FireMode[Mode].Instigator = Instigator;
FireMode[Mode].Level = Level;
}
ClientState = WS_Hidden;
GotoState('Hidden');
if( Level.NetMode == NM_DedicatedServer || !Instigator.IsHumanControlled() )
return;
if( Instigator.Weapon == self || Instigator.PendingWeapon == self ) // this weapon was switched to while waiting for replication, switch to it now
{
if (Instigator.PendingWeapon != None)
Instigator.ChangedWeapon();
else
BringUp();
return;
}
if( Instigator.PendingWeapon != None && Instigator.PendingWeapon.bForceSwitch )
return;
if( Instigator.Weapon == None )
{
Instigator.PendingWeapon = self;
Instigator.ChangedWeapon();
}
else if ( bPossiblySwitch && !Instigator.Weapon.IsFiring() )
{
if ( PlayerController(Instigator.Controller) != None && PlayerController(Instigator.Controller).bNeverSwitchOnPickup )
return;
if ( Instigator.PendingWeapon != None )
{
if ( RateSelf() > Instigator.PendingWeapon.RateSelf() )
{
Instigator.PendingWeapon = self;
Instigator.Weapon.PutDown();
}
}
else if ( RateSelf() > Instigator.Weapon.RateSelf() )
{
Instigator.PendingWeapon = self;
Instigator.Weapon.PutDown();
}
}
}
// jdf ---
simulated function ClientPlayForceFeedback( String EffectName )
{
local PlayerController PC;
PC = PlayerController(Instigator.Controller);
if ( PC != None && PC.bEnableWeaponForceFeedback )
{
PC.ClientPlayForceFeedback( EffectName );
}
}
simulated function StopForceFeedback( String EffectName )
{
local PlayerController PC;
PC = PlayerController(Instigator.Controller);
if ( PC != None && PC.bEnableWeaponForceFeedback )
{
PC.StopForceFeedback( EffectName );
}
}
// --- jdf
simulated function BringUp(optional Weapon PrevWeapon)
{
local int Mode;
if ( ClientState == WS_Hidden )
{
PlayOwnedSound(SelectSound, SLOT_Interact,,,,, false);
ClientPlayForceFeedback(SelectForce); // jdf
if ( Instigator.IsLocallyControlled() )
{
if ( (Mesh!=None) && HasAnim(SelectAnim) )
PlayAnim(SelectAnim, SelectAnimRate, 0.0);
}
ClientState = WS_BringUp;
SetTimer(BringUpTime, false);
}
for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
{
FireMode[Mode].bIsFiring = false;
FireMode[Mode].HoldTime = 0.0;
FireMode[Mode].bServerDelayStartFire = false;
FireMode[Mode].bServerDelayStopFire = false;
FireMode[Mode].bInstantStop = false;
}
if ( (PrevWeapon != None) && PrevWeapon.HasAmmo() && !PrevWeapon.bNoVoluntarySwitch )
OldWeapon = PrevWeapon;
else
OldWeapon = None;
}
simulated function bool PutDown()
{
local int Mode;
if (ClientState == WS_BringUp || ClientState == WS_ReadyToFire)
{
if ( (Instigator.PendingWeapon != None) && !Instigator.PendingWeapon.bForceSwitch )
{
for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
{
// if _RO_
if( FireMode[Mode] == none )
continue;
// End _RO_
if ( FireMode[Mode].bFireOnRelease && FireMode[Mode].bIsFiring )
return false;
if ( FireMode[Mode].NextFireTime > Level.TimeSeconds + FireMode[Mode].FireRate*(1.f - MinReloadPct))
DownDelay = FMax(DownDelay, FireMode[Mode].NextFireTime - Level.TimeSeconds - FireMode[Mode].FireRate*(1.f - MinReloadPct));
}
}
if (Instigator.IsLocallyControlled())
{
for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
{
// if _RO_
if( FireMode[Mode] == none )
continue;
// End _RO_
if ( FireMode[Mode].bIsFiring )
ClientStopFire(Mode);
}
if ( DownDelay <= 0 )
{
if ( ClientState == WS_BringUp )
TweenAnim(SelectAnim,PutDownTime);
else if ( HasAnim(PutDownAnim) )
PlayAnim(PutDownAnim, PutDownAnimRate, 0.0);
}
}
ClientState = WS_PutDown;
if ( Level.GRI.bFastWeaponSwitching )
DownDelay = 0;
if ( DownDelay > 0 )
SetTimer(DownDelay, false);
else
SetTimer(PutDownTime, false);
}
for (Mode = 0; Mode < NUM_FIRE_MODES; Mode++)
{
// if _RO_
if( FireMode[Mode] == none )
continue;
// End _RO_
FireMode[Mode].bServerDelayStartFire = false;
FireMode[Mode].bServerDelayStopFire = false;
}
Instigator.AmbientSound = None;
OldWeapon = None;
return true; // return false if preventing weapon switch
}
simulated function Fire(float F)
{
}
simulated function AltFire(float F)
{
}
simulated event WeaponTick(float dt); // only called on currently held weapon
simulated function OutOfAmmo()
{
if ( Instigator == None || !Instigator.IsLocallyControlled() || HasAmmo() )
return;
DoAutoSwitch();
}
simulated function DoAutoSwitch()
{
Instigator.Controller.SwitchToBestWeapon();
}
//// client only ////
simulated event ClientStartFire(int Mode)
{
if ( Pawn(Owner).Controller.IsInState('GameEnded') || Pawn(Owner).Controller.IsInState('RoundEnded') )
return;
if (Role < ROLE_Authority)
{
if (StartFire(Mode))
{
ServerStartFire(Mode);
}
}
else
{
StartFire(Mode);
}
}
simulated event ClientStopFire(int Mode)
{
if (Role < ROLE_Authority)
{
//Log("ClientStopFire"@Level.TimeSeconds);
StopFire(Mode);
}
ServerStopFire(Mode);
}
simulated function ClientWriteFire(string Result)
{
log(self$" ServerStartFire! "$Result);
}
//// server only ////
event ServerStartFire(byte Mode)
{
if ( (Instigator != None) && (Instigator.Weapon != self) )
{
if ( Instigator.Weapon == None )
Instigator.ServerChangedWeapon(None,self);
else
Instigator.Weapon.SynchronizeWeapon(self);
return;
}
if ( (FireMode[Mode].NextFireTime <= Level.TimeSeconds + FireMode[Mode].PreFireTime)
&& StartFire(Mode) )
{
FireMode[Mode].ServerStartFireTime = Level.TimeSeconds;
FireMode[Mode].bServerDelayStartFire = false;
}
else if ( FireMode[Mode].AllowFire() )
{
FireMode[Mode].bServerDelayStartFire = true;
}
else
ClientForceAmmoUpdate(Mode, AmmoAmount(Mode));
}
simulated function ClientForceAmmoUpdate(int Mode, int NewAmount)
{
//log(self$" ClientForceAmmoUpdate mode "$Mode$" newamount "$NewAmount);
if ( bNoAmmoInstances )
AmmoCharge[Mode] = NewAmount;
else if ( Ammo[mode] != None )
Ammo[mode].AmmoAmount = NewAmount;
}
function SynchronizeWeapon(Weapon ClientWeapon)
{
Instigator.ServerChangedWeapon(self,ClientWeapon);
}
function ServerStopFire(byte Mode)
{
// if a stop was received on the same frame as a start then we need to delay the stop for one frame
if (FireMode[Mode].bServerDelayStartFire || FireMode[Mode].ServerStartFireTime == Level.TimeSeconds)
{
//log("Stop Delayed");
FireMode[Mode].bServerDelayStopFire = true;
}
else
{
//Log("ServerStopFire"@Level.TimeSeconds);
StopFire(Mode);
}
}
simulated function bool ReadyToFire(int Mode)
{
local int alt;
if ( Mode == 0 )
alt = 1;
else
alt = 0;
if ( ((FireMode[alt] != FireMode[Mode]) && FireMode[alt].bModeExclusive && FireMode[alt].bIsFiring)
|| !FireMode[Mode].AllowFire()
|| (FireMode[Mode].NextFireTime > Level.TimeSeconds + FireMode[Mode].PreFireTime) )
{
return false;
}
return true;
}
//// client & server ////
simulated function bool StartFire(int Mode)
{
local int alt;
if (!ReadyToFire(Mode))
return false;
if (Mode == 0)
alt = 1;
else
alt = 0;
FireMode[Mode].bIsFiring = true;
FireMode[Mode].NextFireTime = Level.TimeSeconds + FireMode[Mode].PreFireTime;
if (FireMode[alt].bModeExclusive)
{
// prevents rapidly alternating fire modes
FireMode[Mode].NextFireTime = FMax(FireMode[Mode].NextFireTime, FireMode[alt].NextFireTime);
}
if (Instigator.IsLocallyControlled())
{
if (FireMode[Mode].PreFireTime > 0.0 || FireMode[Mode].bFireOnRelease)
{
FireMode[Mode].PlayPreFire();
}
FireMode[Mode].FireCount = 0;
}
return true;
}
simulated event StopFire(int Mode)
{
if ( FireMode[Mode].bIsFiring )
FireMode[Mode].bInstantStop = true;
if (Instigator.IsLocallyControlled() && !FireMode[Mode].bFireOnRelease)
FireMode[Mode].PlayFireEnd();
FireMode[Mode].bIsFiring = false;
FireMode[Mode].StopFiring();
if (!FireMode[Mode].bFireOnRelease)
ZeroFlashCount(Mode);
}
//hack to stop all firing and release any charging firemodes RIGHT THIS INSTANT
//used when getting into vehicles
simulated function ImmediateStopFire()
{
local int i;
for (i = 0; i < NUM_FIRE_MODES; i++)
{
ClientStopFire(i);
if (FireMode[i].bFireOnRelease)
{
if (Level.TimeSeconds > FireMode[i].NextFireTime && (FireMode[i].bInstantStop || !FireMode[i].bNowWaiting))
FireMode[i].ModeDoFire();
if (FireMode[i].bWaitForRelease)
FireMode[i].bNowWaiting = true;
}
}
}
simulated function Timer()
{
local int Mode;
local float OldDownDelay;
OldDownDelay = DownDelay;
DownDelay = 0;
if (ClientState == WS_BringUp)
{
for( Mode = 0; Mode < NUM_FIRE_MODES; Mode++ )
FireMode[Mode].InitEffects();
PlayIdle();
ClientState = WS_ReadyToFire;
}
else if (ClientState == WS_PutDown)
{
if ( OldDownDelay > 0 )
{
if ( HasAnim(PutDownAnim) )
PlayAnim(PutDownAnim, PutDownAnimRate, 0.0);
SetTimer(PutDownTime, false);
return;
}
if ( Instigator.PendingWeapon == None )
{
PlayIdle();
ClientState = WS_ReadyToFire;
}
else
{
ClientState = WS_Hidden;
Instigator.ChangedWeapon();
if ( Instigator.Weapon == self )
{
PlayIdle();
ClientState = WS_ReadyToFire;
}
else
{
for( Mode = 0; Mode < NUM_FIRE_MODES; Mode++ )
FireMode[Mode].DestroyEffects();
}
}
}
}
simulated function bool IsFiring() // called by pawn animation, mostly
{
if (Level.NetMode == NM_DedicatedServer || Level.NetMode == NM_ListenServer)
return (FireMode[0].IsFiring() || FireMode[1].IsFiring());
return ( ClientState == WS_ReadyToFire && (FireMode[0].IsFiring() || FireMode[1].IsFiring()) );
}
function bool IsRapidFire() // called by pawn animation
{
if (FireMode[1] != None && FireMode[1].bIsFiring)
return FireMode[1].bPawnRapidFireAnim;
else if (FireMode[0] != None)
return FireMode[0].bPawnRapidFireAnim;
else
return false;
}
// called every time owner takes damage while holding this weapon - used by shield gun
function AdjustPlayerDamage( out int Damage, Pawn InstigatedBy, Vector HitLocation,
out Vector Momentum, class<DamageType> DamageType)
{
}
simulated function CheckSuperBerserk()
{
if ( Level.GRI.WeaponBerserk > 1.0 )
{
if (FireMode[0] != None)
FireMode[0].StartSuperBerserk();
if (FireMode[1] != None)
FireMode[1].StartSuperBerserk();
}
}
simulated function StartBerserk()
{
if ( (Level.GRI != None) && Level.GRI.WeaponBerserk > 1.0 )
return;
bBerserk = true;
if (FireMode[0] != None)
FireMode[0].StartBerserk();
if (FireMode[1] != None)
FireMode[1].StartBerserk();
}
simulated function StopBerserk()
{
bBerserk = false;
if ( (Level.GRI != None) && Level.GRI.WeaponBerserk > 1.0 )
return;
if (FireMode[0] != None)
FireMode[0].StopBerserk();
if (FireMode[1] != None)
FireMode[1].StopBerserk();
}
simulated function AnimEnd(int channel)
{
local name anim;
local float frame, rate;
GetAnimParams(0, anim, frame, rate);
if (ClientState == WS_ReadyToFire)
{
if (anim == FireMode[0].FireAnim && HasAnim(FireMode[0].FireEndAnim)) // rocket hack
{
PlayAnim(FireMode[0].FireEndAnim, FireMode[0].FireEndAnimRate, 0.0);
}
else if (anim== FireMode[1].FireAnim && HasAnim(FireMode[1].FireEndAnim))
{
PlayAnim(FireMode[1].FireEndAnim, FireMode[1].FireEndAnimRate, 0.0);
}
else if ((FireMode[0] == None || !FireMode[0].bIsFiring) && (FireMode[1] == None || !FireMode[1].bIsFiring))
{
PlayIdle();
}
}
}
simulated function PlayIdle()
{
LoopAnim(IdleAnim, IdleAnimRate, 0.2);
}
state PendingClientWeaponSet
{
simulated function Timer()
{
if ( Pawn(Owner) != None )
ClientWeaponSet(bPendingSwitch);
if ( IsInState('PendingClientWeaponSet') )
SetTimer(0.05, false);
}
simulated function BeginState()
{
SetTimer(0.05, false);
}
simulated function EndState()
{
}
}
state Hidden
{
}
function bool CheckReflect( Vector HitLocation, out Vector RefNormal, int AmmoDrain )
{
return false;
}
function DoReflectEffect(int Drain)
{
}
function bool HandlePickupQuery( pickup Item )
{
local WeaponPickup wpu;
local int i;
if ( bNoAmmoInstances )
{
// handle ammo pickups
for ( i=0; i<2; i++ )
{
if ( (item.inventorytype == AmmoClass[i]) && (AmmoClass[i] != None) )
{
if ( AmmoCharge[i] >= MaxAmmo(i) )
return true;
item.AnnouncePickup(Pawn(Owner));
AddAmmo(Ammo(item).AmmoAmount, i);
item.SetRespawn();
return true;
}
}
}
if (class == Item.InventoryType)
{
wpu = WeaponPickup(Item);
if (wpu != None)
return !wpu.AllowRepeatPickup();
else
return false;
}
if ( Inventory == None )
return false;
return Inventory.HandlePickupQuery(Item);
}
simulated function bool WantsZoomFade()
{
return false;
}
/* CanHeal()
used by bot AI
should return true if this weapon is able to heal Other
*/
function bool CanHeal(Actor Other)
{
return false;
}
//called by AI when camping/defending
//return true if it is useful to fire this weapon even though bot doesn't have a target
//for example, a weapon that launches turrets or mines
function bool ShouldFireWithoutTarget()
{
return false;
}
// ugly hack for tutorial
function bool ShootHoop(Controller B, Vector ShootLoc)
{
return false;
}
simulated function PawnUnpossessed();
// FIXME - hack to get classes building again after fixing the bug that was allowing all protected variables to
// be referenced in other classes - maybe there's already an accessor that does this and I just don't know about it
// ...in that case, just remove this function -- rjp
simulated function WeaponFire GetFireMode( byte Mode )
{
if ( Mode < NUM_FIRE_MODES )
return FireMode[Mode];
return None;
}
defaultproperties
{
DrawType=DT_Mesh
Style=STY_Normal
PlayerViewOffset=(X=0,Y=0,Z=0)
InventoryGroup=1
FireModeClass(0)=None
FireModeClass(1)=None
// animation //
IdleAnim=Idle
RestAnim=Rest
AimAnim=Aim
RunAnim=Run
SelectAnim=Select
PutDownAnim=Down
IdleAnimRate=1.0
RestAnimRate=1.0
AimAnimRate=1.0
RunAnimRate=1.0
// other useful stuff //
MessageNoAmmo=" has no ammo"
DisplayFOV=90.0
bCanThrow=true
AIRating=0.5
CurrentRating=0.5
AttachmentClass=class'WeaponAttachment'
NetPriority=3.0
ScaleGlow=1.5
AmbientGlow=20
MaxLights=6
SoundVolume=255
HudColor=(r=255,g=255,b=0,a=255)
CenteredOffsetY=-10.0
CenteredRoll=2000
CenteredYaw=0
bUseOldWeaponMesh=False
ExchangeFireModes=0
CustomCrosshair=0
CustomCrossHairColor=(R=255,G=255,B=255,A=255)
CustomCrossHairScale=+1.0
CustomCrossHairTexture=None
// if _RO_
CustomCrossHairTextureName="InterfaceArt_tex.Cursors.Crosshair_Cross2"
// else
// CustomCrossHairTextureName="Crosshairs.Hud.Crosshair_Cross1"
// end if _RO_
OldCenteredOffsetY=-10.0
OldCenteredRoll=2000
OldCenteredYaw=0
OldDrawScale=+1.0
SelectAnimRate=1.3636
PutDownAnimRate=1.3636
BringUpTime=+0.33
PutDownTime=+0.33
MinReloadPct=+0.5
NetUpdateFrequency=2
bNoAmmoInstances=true
bNetNotify=true
}