WeaponSystemFramework
The Vehicle Weapon System Framework
I've written up this class to make implementation of weapons for vehicles both easily and quickly. A configurable zoom option and automated projectile firing/timing is already implemented and toggleable with use of the defaultproperties section.
A note about the zoom, in order to ensure the behavior intended, both the zoom and await variables needs to be set to true. It should still *work* but i don't suggest it.
Key Concepts
The server will exclusively call the TimerUpdateWithoutDriver and TimerUpdateWithDriver. The First every tick ( Be careful ), and the other every tick while a player is in the vehicle.
All of the Server* functions will be executed exclusively on the server.
All of the Client* functions will be executed exclusively on network-clients.
All of the base functions of this same family ( I.E. ServerPriFireInactive, ClientPriFireInactive, *PriFireInactive* ) are executed on the local machine of the person in the turret.
All client effects/rendering should be done in the base functions (PriFireInactive). This is the first function called, and is called locally on the client before any replication takes place, this will both be the fastest/most frequent to execute code.
Client* functions may not actually have a practical use but if you come up with something that should really be done in there let me know.
To make your primary fire spawn a projectile.
defaultproperties { bPriFireSpawnProject=true bRepeatedPriFire=true //optional PrimaryProjectileClass=MyPackage.MyClass FireInterval=1.0 }
To make your alternate fire zoom.
defaultproperties { bDoAltZoom=true bShouldAwaitAltRelease=true ZoomMinFov=25.000 ZoomMaxFov=120.0 ZoomIncrement=0.5 }
Note that setting zoomIncrement to a negative value makes a zoom out. A positive ZoomIncrement zooms in. (FOV-ZoomIncrement)
Here are all of the new default properties, with what i have thought to be fairly intuitive names.
defaultproperties { TimerUpdateFrequency=0.01; bDoPriZoom=false bDoAltZoom=false bPriFireSpawnProjectile=false bAltFireSpawnProjectile=false bShouldAwaitPriRelease=false bShouldAwaitAltRelease=false bRepeatedPriFire=false bRepeatedAltFire=false ZoomMinFov=25.000 ZoomMaxFov=120.0 ZoomIncrement=0.5 FireInterval=1.0 AltFireInterval=0.0 PrimaryProjectileClass=none SecondaryProjectileClass=none }
class MTPBaseStationaryWeapon extends ONSWeapon; // Real-Time Key Press Variables var bool bIsAltFiring, bWasAltFiring; var bool bIsPriFiring, bWasPriFiring; var bool bAwaitingAltRelease, bAwaitingPriRelease; // Realtime Zoom Variables var float ZoomLevel; var bool bZooming; // Real-Time Weapon Variables var float LastPriFireTime, LastAltFireTime; // Configuration Weapon Variables var float TimerUpdateFrequency; var class<Projectile> PrimaryProjectileClass; var class<Projectile> SecondaryProjectileClass; var bool bPriFireSpawnProjectile, bAltFireSpawnProjectile; var bool bRepeatedPriFire, bRepeatedAltFire; // Configuration Key Press Variables var bool bShouldAwaitAltRelease, bShouldAwaitPriRelease; // Config/Default Oriented Zoom Variables var float ZoomMinFov, ZoomMaxFov, ZoomIncrement; var bool bDoPriZoom, bDoAltZoom; replication { reliable if( Role == Role_Authority ) LastPriFireTime, LastAltFireTime, bAwaitingAltRelease, bAwaitingPriRelease; reliable if( Role < Role_Authority ) ServerPriFirePressed, ServerPriFireReleased, ServerPriFireInactive, ServerPriFireActive, ServerAltFirePressed, ServerAltFireReleased, ServerAltFireInactive, ServerAltFireActive, ServerTimerUpdateWithDriver, ServerTimerUpdateWithoutDriver, ServerCantPriFire, ServerCantAltFire, ServerAwaitRelease; reliable if( Role == Role_Authority ) ClientPriFirePressed, ClientPriFireReleased, ClientPriFireInactive, ClientPriFireActive, ClientAltFirePressed, ClientAltFireReleased, ClientAltFireInactive, ClientAltFireActive, ClientTimerUpdateWithDriver, ClientTimerUpdateWithoutDriver, ClientCantPriFire, ClientCantAltFire; } simulated function CalcWeaponFire() { Super.CalcWeaponFire() DualFireOffset*=-1; } event bool AttemptFire(Controller C, bool bAltFire) { local bool bCanFire; if(Role != ROLE_Authority || bForceCenterAim) return False; if( !bAltFire && CanPriFire() ) bCanFire = True; if( bAltFire && CanAltFire() ) bCanFire = true; if( bCanFire ) { if (bCorrectAim) WeaponFireRotation = AdjustAim(bAltFire); if (Spread > 0) WeaponFireRotation = rotator(vector(WeaponFireRotation) + VRand()*FRand()*Spread); Instigator.MakeNoise(1.0); if (bAltFire) { AltFire(C); } else { Fire(C); } AimLockReleaseTime = Level.TimeSeconds + FireCountdown * FireIntervalAimLock; } return bCanFire; } function class<Projectile> GetProjectileClass(bool bAltFire) { if( bAltFire ) return GetSecondaryProjectileClass(); else return GetPrimaryProjectileClass(); } function class<Projectile> GetPrimaryProjectileClass() { return PrimaryProjectileClass; } function class<Projectile> GetSecondaryProjectileClass() { return SecondaryProjectileClass; } function SpawnProjectileNone(bool bAltFire) { if( bAltFire ) SpawnProjectileNoneAltFire(); else SpawnProjectileNonePriFire(); } function SpawnProjectileNoneAltFire() { } function SpawnProjectileNonePriFire() { } function Projectile SpawnProjectileInit(Projectile P, bool bAltFire) { return P; } // You'll Probably Never Want To Super.Call() this when overriding function PlayFiringAnimations() { if (!Level.bDropDetail && Level.DetailMode != DM_Low) { if (DualFireOffset < 0) PlayAnim('RightFire'); else PlayAnim('LeftFire'); } } function Projectile SpawnProjectile(class<Projectile> ProjClass, bool bAltFire) { local Projectile P; local class<Projectile> ProClass; PlayFiringAnimations(); ProClass = GetProjectileClass(bAltFire); // Get Current Client Weapon Rotation CalcWeaponFire(); if( ProClass != none ) P = Super.SpawnProjectile(ProClass, bAltFire); if( P == none ) { SpawnProjectileNone(bAltFire); return none; } P.SetOwner(self); return SpawnProjectileInit(P, bAltFire); } simulated function WeaponInit() { LastPriFireTime=Level.TimeSeconds; LastAltFireTime=Level.TimeSeconds; bAwaitingPriRelease=false; bAwaitingAltRelease=false; } simulated function bool PriFireWaitingRelease() { return bAwaitingPriRelease && bShouldAwaitPriRelease; } simulated function bool AltFireWaitingRelease() { return bAwaitingAltRelease && bShouldAwaitAltRelease; } ///////////////////////////////////////////////////////// // Entry Point Timer Functions, Called On Client OR Server simulated function PriFirePressed() { if( ShouldPriZoom() ) StartZoom(false); ServerPriFirePressed(); } simulated function PriFireReleased() { if( bDoPriZoom ) EndZoom(); ServerPriFireReleased(); } simulated function PriFireInactive() { ServerPriFireInactive(); } simulated function PriFireActive() { if( ShouldPriZoom() ) DoZoom(); ServerPriFireActive(); } simulated function AltFirePressed() { if( ShouldAltZoom() ) StartZoom(true); ServerAltFirePressed(); } simulated function AltFireReleased() { if( bDoAltZoom ) EndZoom(); ServerAltFireReleased(); } simulated function AltFireInactive() { ServerAltFireInactive(); } simulated function AltFireActive() { if( ShouldAltZoom() ) DoZoom(); ServerAltFireActive(); } simulated function TimerUpdateWithDriver() { ServerTimerUpdateWithDriver(); } simulated function TimerUpdateWithoutDriver() { ServerTimerUpdateWithoutDriver(); } // End Entry Point Timer Functions //////////////////////////////////////// function ServerAwaitRelease( bool bAltFire ) { if( bAltFire ) bAwaitingAltRelease=true; else bAwaitingPriRelease=true; } ///////////////////////////////////////////////// // Zooming Functions // // Only One Zoom Feature Per Weapon simulated function StartZoom(bool bAltFire) { if( ZoomLevel != 0.0 ) { RevertZoom(); ServerAwaitRelease( bAltFire ); } else { ZoomInit(); DoZoom(); } } simulated function ZoomInit() { bZooming=True; } simulated function RevertZoom() { EndZoom(); ZoomLevel = 0.0; if( Instigator != none && Instigator.Controller != none && PlayerController(Instigator.Controller) != none ) PlayerController(Instigator.Controller).DesiredFOV = PlayerController(Instigator.Controller).DefaultFOV; } simulated function EndZoom() { bZooming=False; } simulated function bool IsZooming() { return bZooming; } simulated function DoZoom() { if( !IsZooming() ) return; PreZoom(); ZoomLevel += ZoomIncrement; if( 90.0 - ZoomLevel < ZoomMinFov ) ZoomLevel = 90 - ZoomMinFov; if( 90.0 - ZoomLevel > ZoomMaxFov ) ZoomLevel = 90 - ZoomMaxFov; if( Instigator != none && Instigator.Controller != none && PlayerController(Instigator.Controller) != none ) PlayerController(Instigator.Controller).DesiredFOV = FClamp(90.0 - ZoomLevel, 1, 170); PostZoom(); } simulated function PreZoom() { } simulated function PostZoom() { } simulated function bool ShouldAltZoom() { return bDoAltZoom && !AltFireWaitingRelease(); } simulated function bool ShouldPriZoom() { return bDoPriZoom && !PriFireWaitingRelease(); } ///////////////////////////////////////// // Begin Server Side Timer Functions // These Must Be Super.Called() // Or The Client Functions Won't Be Replicated function ServerPriFirePressed() { ServerTryPriFire(); ClientPriFirePressed(); } function ServerTryPriFire() { if( CanPriFire() ) ServerPriFire(); else ServerCantPriFire(); } // This Is Not The Place For (House Keeping), use Inactive, // This is for specific thigns happening when the mouse is released // I.E. Fire On Release. function ServerPriFireReleased() { ClientPriFireReleased(); ServerPriFireInactive(); } function ServerPriFireInactive() { if( PriFireWaitingRelease() ) { bAwaitingPriRelease=false; } ClientPriFireInactive(); } function ServerPriFireActive() { if( bRepeatedPriFire ) ServerTryPriFire(); ClientPriFireActive(); } ///////////////////////// // Server Alt Fire Routines function ServerAltFirePressed() { ServerTryAltFire(); ClientAltFirePressed(); } function ServerTryAltFire() { if( CanAltFire() ) ServerAltFire(); else ServerCantAltFire(); } function ServerAltFireReleased() { ClientAltFireReleased(); ServerAltFireInactive(); } function ServerAltFireInactive() { if( AltFireWaitingRelease() ) { bAwaitingAltRelease=false; } ClientAltFireInactive(); } function ServerAltFireActive() { if( bRepeatedAltFire ) ServerTryAltFire(); ClientAltFireActive(); } function ServerTimerUpdateWithDriver() { ClientTimerUpdateWithDriver(); } function ServerTimerUpdateWithoutDriver() { ClientTimerUpdateWithoutDriver(); } // End Server Side Timer Functions ////////////////////////////////////////// ///////////////////////////////////////////// // Pri Fire Guts simulated function bool CanPriFire() { return (LastPriFireTime + FireInterval) <= Level.TimeSeconds && !PriFireWaitingRelease(); } // Overriding Should Call Super After All Other Functions // Use ServerPostPriFire To Do Modifications After Projectile Exists function ServerPriFire() { LastPriFireTime=Level.TimeSeconds; if( bPriFireSpawnProjectile ) SpawnProjectile(none, false); ServerPostPriFire(); } function ServerCantPriFire() { ClientCantPriFire(); } simulated function ClientCantPriFire() { } function ServerPostPriFire() { } //////////////////////////////////////// //Alt Fire Guts simulated function bool CanAltFire() { return (LastAltFireTime + AltFireInterval) <= Level.TimeSeconds && !AltFireWaitingRelease(); } // Overriding Should Call super After All Other Commands // Use ServerPostAltFire To Do Modifications After Projectile Exists function ServerAltFire() { LastAltFireTime=Level.TimeSeconds; if( bAltFireSpawnProjectile ) SpawnProjectile(none, true); ServerPostAltFire(); } function ServerPostAltFire() { } function ServerCantAltFire() { ClientCantAltFire(); } simulated function ClientCantAltFire() { } ////////////////////////////////////////// // Begin Client Side Timer Functions simulated function ClientPriFirePressed() { } simulated function ClientPriFireReleased() { } simulated function ClientPriFireInactive() { } simulated function ClientPriFireActive() { } simulated function ClientAltFirePressed() { } simulated function ClientAltFireReleased() { } simulated function ClientAltFireInactive() { } simulated function ClientAltFireActive() { } simulated function ClientTimerUpdateWithDriver() { } simulated function ClientTimerUpdateWithoutDriver() { } // End Client Side Timer Functions ////////////////////////////////////// simulated state ProjectileFireMode { simulated function BeginState() { super.BeginState(); SetTimer(TimerUpdateFrequency,true); //Input Var Initializations bIsAltFiring = false; bIsPriFiring = false; // Zoom Init RevertZoom(); WeaponInit(); } simulated function EndState() { SetTimer(0.0, false); super.EndState(); } // For Bot Support function Fire(Controller C) { PriFirePressed(); } function AltFire(Controller C) { AltFirePressed(); } simulated function Timer() { if( ROLE == ROLE_Authority ) TimerUpdateWithoutDriver(); // Re-Init If We Are An Un-Powered Turret On The Server if( Role == ROLE_Authority && ONSStationaryWeaponPawn(Owner) != none && !ONSStationaryWeaponPawn(Owner).bPowered ) { WeaponInit(); } if( Instigator == none || Instigator.Controller == none ) return; if( ROLE == ROLE_Authority ) TimerUpdateWithDriver(); if( !Instigator.IsLocallyControlled() ) return; /////////////////////////////////////////// // Key Input Data Initilization bWasAltFiring = bIsAltFiring; bIsAltFiring = !(Instigator.Controller.bAltFire == 0); bWasPriFiring = bIsPriFiring; bIsPriFiring = !(Instigator.Controller.bFire == 0); ////////////////////////////////////////// if( bIsPriFiring && !bWasPriFiring ) // Primary Fire Was Just Pressed { // log("["$Level.TimeSeconds@"] MTPBaseStationaryWeapon: Pri Fire Pressed"); PriFirePressed(); } else if( !bIsPriFiring && bWasPriFiring ) // Primary Fire Was Just Released { // log("["$Level.TimeSeconds@"] MTPBaseStationaryWeapon: Pri Fire Released"); PriFireReleased(); } else if( !bIsPriFiring ) // Code Executed When Primary Fire is Not Pressed { // log("["$Level.TimeSeconds@"] MTPBaseStationaryWeapon: Pri Fire Inactive"); PriFireInactive(); } else // Primary Fire Is Pressed { // log("["$Level.TimeSeconds@"] MTPBaseStationaryWeapon: Pri Fire Active"); PriFireActive(); } if( bIsAltFiring && !bWasAltFiring ) // Alt Fire Was Just Pressed { // log("["$Level.TimeSeconds@"] MTPBaseStationaryWeapon: Alt Fire Pressed"); AltFirePressed(); } else if( !bIsAltFiring && bWasAltFiring ) // Alt Fire Was Just Released { // log("["$Level.TimeSeconds@"] MTPBaseStationaryWeapon: Alt Fire Released"); AltFireReleased(); } else if( !bIsAltFiring ) // Code Executed When Alt Fire is Not Pressed { // log("["$Level.TimeSeconds@"] MTPBaseStationaryWeapon: Alt Fire Inactive"); AltFireInactive(); } else // Alt Fire Is Pressed { // log("["$Level.TimeSeconds@"] MTPBaseStationaryWeapon: Alt Fire Active"); AltFireActive(); } } } defaultproperties { YawBone="TurretBase" PitchBone="Dummy01" PitchUpLimit=15000 PitchDownLimit=52500 WeaponFireAttachmentBone="TurretCockpit" GunnerAttachmentBone="TurretCockpit" WeaponFireOffset=25.000000 DualFireOffset=75.000000 bInstantRotation=True bInstantFire=False bDualIndependantTargeting=True AIInfo(0)=(bInstantHit=False,aimerror=750.000000) Mesh=SkeletalMesh'ONSWeapons-A.NewManualGun' CollisionRadius=50.000000 CollisionHeight=70.000000 TimerUpdateFrequency=0.01; bDoPriZoom=false bDoAltZoom=false bPriFireSpawnProjectile=false bAltFireSpawnProjectile=false bShouldAwaitPriRelease=false bShouldAwaitAltRelease=false bRepeatedPriFire=false bRepeatedAltFire=false ZoomMinFov=25.000 ZoomMaxFov=120.0 ZoomIncrement=0.5 FireInterval=1.0 AltFireInterval=0.0 PrimaryProjectileClass=none SecondaryProjectileClass=none }