//============================================================================= // KFWeap_FlameBase //============================================================================= // Base weapon class used for flame weapons //============================================================================= // Killing Floor 2 // Copyright (C) 2015 Tripwire Interactive LLC // - John "Ramm-Jaeger" Gibson //============================================================================= class KFWeap_FlameBase extends KFWeapon abstract config(game); /** Internal. TRUE if fire is spewing, false otherwise. */ var protected transient bool bFireSpraying; var protected transient int NextFlamePoolIdx; /** A pool of fire spray actors to use for this attachment. Needed because if you fire quickly blending between the two looks better */ var protected transient KFSprayActor FlamePool[2]; /** The spray actor we are associated with, if currently firing. */ var transient protected KFSprayActor ActiveFlameSpray; /** The Archetype to spawn for our fire spray actors. */ var protected const KFSprayActor FlameSprayArchetype; /** Whether TurnOnPilot has already been called */ var protected bool bPilotLightOn; /** Pilot light sound play event */ var AkEvent PilotLightPlayEvent; /** Pilot light sound stop event */ var AkEvent PilotLightStopEvent; /** Effect for the pilot light. */ var() protected KFParticleSystemComponent PSC_PilotLight; /** Socket to attach the pilot light to. */ var() name PilotLightSocketName; /** How hot the Barrel Currently is */ var float BarrelHeat; /** How hot the Barrel was last frame */ var float LastBarrelHeat; /** Whether this weapon should warn AI when it fires */ var() const bool bWarnAIWhenFiring; /********************************************************************************************* * @name Optional dynamic pilot lights ********************************************************************************************* */ struct PilotLight { var() PointLightComponent Light; var() name LightAttachBone; var() float FlickerIntensity; var() float FlickerInterpSpeed; var() float LastLightBrightness; }; var(PilotLights) array PilotLights; /** Emitter to play when firing stops. */ var() const KFParticleSystemComponent PSC_EndSpray; /** The shortest amount of time this weapon can fire */ //var() protected float MinFireDuration; /** The minimum amount of ammo that must be consumed before this weapon can stop firing */ var() protected byte MinAmmoConsumed; /** How much ammo we've consumed since firing began */ var protected byte AmmoConsumed; /** Whether pilot lights are allowed or not */ var globalconfig bool bArePilotLightsAllowed; /********************************************************************************************* * @name Debugging *********************************************************************************************/ simulated function SetFlameComplex(bool bDoCollideComplex) { `if(`notdefined(ShippingPC)) FlamePool[0].bDoCollideComplex = bDoCollideComplex; FlamePool[1].bDoCollideComplex = bDoCollideComplex; `endif } simulated function SetFlameDebugDamage(bool bDebugDirectDamage, bool bDebugSplashDamage, bool bDebugShowSplashRadius, bool bDebugShowCollision) { `if(`notdefined(ShippingPC)) FlamePool[0].bDebugDirectDamage = bDebugDirectDamage; FlamePool[0].bDebugSplashDamage = bDebugSplashDamage; FlamePool[0].bDebugShowSplashRadius = bDebugShowSplashRadius; FlamePool[0].bDebugShowCollision = bDebugShowCollision; FlamePool[1].bDebugDirectDamage = bDebugDirectDamage; FlamePool[1].bDebugSplashDamage = bDebugSplashDamage; FlamePool[1].bDebugShowSplashRadius = bDebugShowSplashRadius; FlamePool[1].bDebugShowCollision = bDebugShowCollision; `endif } simulated function SetFlameDebugFX(bool bDebugShowSeeds, bool bDebugShowBones, bool bDebugForceNonPlayerParticles) { `if(`notdefined(ShippingPC)) FlamePool[0].bDebugShowSeeds = bDebugShowSeeds; FlamePool[0].bDebugShowBones = bDebugShowBones; FlamePool[0].bDebugForceNonPlayerParticles = bDebugForceNonPlayerParticles; FlamePool[1].bDebugShowSeeds = bDebugShowSeeds; FlamePool[1].bDebugShowBones = bDebugShowBones; FlamePool[1].bDebugForceNonPlayerParticles = bDebugForceNonPlayerParticles; `endif } /********************************************************************************************* * @name Actor *********************************************************************************************/ /** * Overridden to handle the dynamic flickering lights */ simulated event Tick(float DeltaTime) { local int Idx; local float FlameHeat; super.Tick(DeltaTime); if( WorldInfo.NetMode != NM_DedicatedServer && WeaponMICs.Length > 0 && Instigator != None && Instigator.IsLocallyControlled() && Instigator.IsHumanControlled() ) { if( bFireSpraying && ActiveFlameSpray != None) { FlameHeat = FlameHeatCalc(); // Blend the heat in if( BarrelHeat < FlameHeat ) { BarrelHeat += DeltaTime * 2.0; } else { BarrelHeat = FlameHeat; } } else { if( BarrelHeat > 0 ) { // Cool the barrel down when not shooting BarrelHeat -= DeltaTime * 0.5; if( BarrelHeat < 0 ) { BarrelHeat = 0; } } } ChangeMaterial(); LastBarrelHeat = BarrelHeat; } // hacky flicker for lights. for (Idx=0; Idx= ArrayCount(FlamePool)) { NextFlamePoolIdx = 0; } return Ret; } simulated protected function TurnOnFireSpray() { local KFSprayActor PrevFlameSpray; if (!bFireSpraying) { PrevFlameSpray = ActiveFlameSpray; // spawn flame actor ActiveFlameSpray = GetFlameSprayFromPool(); if (ActiveFlameSpray != None) { if( Role == ROLE_Authority ) { ActiveFlameSpray.bVisualOnly = false; ActiveFlameSpray.DamageModifier = GetUpgradeDamageMod(); } ActiveFlameSpray.BeginSpray(); // Disable damage from the other flame spray actor, we just want it to finish its animations if( Role == ROLE_Authority && PrevFlameSpray != none && PrevFlameSpray != ActiveFlameSpray ) { PrevFlameSpray.bVisualOnly = true; } } else { `Warn(GetFuncName() @ "ActiveFlameSpray is NONE!!!"); } bFireSpraying = TRUE; } } simulated protected function TurnOffFireSpray() { // play end-of-firing poof. will stop itself. if( PSC_EndSpray != None ) { PSC_EndSpray.ActivateSystem(); } if (ActiveFlameSpray != None) { ActiveFlameSpray.DetachAndFinish(); if (Role == ROLE_Authority) { ActiveFlameSpray.DamageModifier = 1.f; } } bFireSpraying = FALSE; } /********************************************************************************************* * state SprayingFire * This is the default Firing State for flame weapons. *********************************************************************************************/ /** Runs on a loop when firing to determine if AI should be warned */ function Timer_CheckForAIWarning(); simulated state SprayingFire extends WeaponFiring { simulated function BeginState( Name PreviousStateName ) { AmmoConsumed = 0; TurnOnFireSpray(); // Start timer to warn AI if( bWarnAIWhenFiring ) { Timer_CheckForAIWarning(); SetTimer( 0.5f, true, nameOf(Timer_CheckForAIWarning) ); } super.BeginState(PreviousStateName); } /** Leaving state, shut everything down. */ simulated function EndState(Name NextStateName) { if (bFireSpraying) { TurnOffFireSpray(); } ClearFlashLocation(); ClearTimer('RefireCheckTimer'); ClearPendingFire(0); // Clear AI warning timer if( bWarnAIWhenFiring ) { ClearTimer( nameOf(Timer_CheckForAIWarning) ); } super.EndState(NextStateName); } /** Overriden here to enforce a minimum amount of ammo consumed (to make sure the flame stays on a minimum duration) */ simulated function ConsumeAmmo( byte FireMode ) { global.ConsumeAmmo(FireMode); AmmoConsumed++; } /** * Check if current fire mode can/should keep on firing. * This is called from a firing state after each shot is fired * to decide if the weapon should fire again, or stop and go to the active state. * The default behavior, implemented here, is keep on firing while player presses fire * and there is enough ammo. (Auto Fire). * * @return true to fire again, false to stop firing and return to Active State. */ simulated function bool ShouldRefire() { // if doesn't have ammo to keep on firing, then stop if( !HasAmmo( CurrentFireMode ) ) { return false; } // refire if owner is still willing to fire or if we've matched or surpassed minimum // amount of ammo consumed return ( StillFiring(CurrentFireMode) || AmmoConsumed < MinAmmoConsumed ); } /** Runs on a loop when firing to determine if AI should be warned */ function Timer_CheckForAIWarning() { local vector Direction, DangerPoint; local vector TraceStart, Projection; local Pawn P; local KFPawn_Monster HitMonster; TraceStart = Instigator.GetWeaponStartTraceLocation(); Direction = vector( GetAdjustedAim(TraceStart) ); // Warn all zeds within range foreach WorldInfo.AllPawns( class'Pawn', P ) { if( P.GetTeamNum() != Instigator.GetTeamNum() && !P.IsHumanControlled() && P.IsAliveAndWell() ) { // Determine if AI is within range as well as within our field of view Projection = P.Location - TraceStart; if( VSizeSQ(Projection) < MaxAIWarningDistSQ ) { PointDistToLine( P.Location, Direction, TraceStart, DangerPoint ); if( VSizeSQ(DangerPoint - P.Location) < MaxAIWarningDistFromPointSQ ) { // Tell the AI to evade away from the DangerPoint HitMonster = KFPawn_Monster( P ); if( HitMonster != none && HitMonster.MyKFAIC != None ) { HitMonster.MyKFAIC.ReceiveLocationalWarning( DangerPoint, TraceStart, self ); } } } } } } simulated function bool IsFiring() { return TRUE; } simulated function bool TryPutDown() { bWeaponPutDown = TRUE; return FALSE; } }; /********************************************************************************************* * State WeaponEquipping * The Weapon is in this state while transitioning from Inactive to Active state. * Typically, the weapon will remain in this state while its selection animation is being played. * While in this state, the weapon cannot be fired. *********************************************************************************************/ simulated state WeaponEquipping { simulated function BeginState(Name PreviousStateName) { super.BeginState(PreviousStateName); TurnOnPilot(); } } /********************************************************************************************* * State WeaponPuttingDown * Putting down weapon in favor of a new one. * Weapon is transitioning to the Inactive state. *********************************************************************************************/ simulated state WeaponPuttingDown { simulated event BeginState(Name PreviousStateName) { super.BeginState(PreviousStateName); TurnOffPilot(); } } /********************************************************************************************* * State WeaponAbortEquip * Special PuttingDown state used when WeaponEquipping is interrupted. Must come after * WeaponPuttingDown definition or this willextend the super version. *********************************************************************************************/ simulated state WeaponAbortEquip { simulated event BeginState(Name PreviousStateName) { super.BeginState(PreviousStateName); TurnOffPilot(); } } /********************************************************************************************* * state Inactive * This state is the default state. It needs to make sure Zooming is reset when entering/leaving *********************************************************************************************/ auto simulated state Inactive { simulated function BeginState(name PreviousStateName) { Super.BeginState(PreviousStateName); TurnOffPilot(); } } /********************************************************************************************* * @name Parent overrides *********************************************************************************************/ /** * Detach weapon from skeletal mesh * * @param SkeletalMeshComponent weapon is attached to. */ simulated function DetachWeapon() { TurnOffPilot(); Super.DetachWeapon(); } simulated function CustomFire() { // tell remote clients that we fired, to trigger effects if ( CurrentFireMode != GRENADE_FIREMODE ) { IncrementFlashCount(); } super.CustomFire(); } /** * This function is called from the pawn when the visibility of the weapon changes */ simulated function ChangeVisibility(bool bIsVisible) { Super.ChangeVisibility(bIsVisible); if( bIsVisible && !bFireSpraying ) { FlamePool[0].SetHidden(true); FlamePool[1].SetHidden(true); FlamePool[0].SkeletalSprayMesh.SetHidden(true); FlamePool[1].SkeletalSprayMesh.SetHidden(true); } else { FlamePool[0].SetHidden(!bIsVisible); FlamePool[1].SetHidden(!bIsVisible); FlamePool[0].SkeletalSprayMesh.SetHidden(!bIsVisible); FlamePool[1].SkeletalSprayMesh.SetHidden(!bIsVisible); } if (bIsVisible) { TurnOnPilot(); } else { TurnOffPilot(); } } /** * Adjust the FOV for the first person weapon and arms. */ simulated event SetFOV( float NewFOV ) { Super.SetFOV(NewFOV); // Set the emitters to the same FOV as the weapon mesh if( MySkelMesh != none ) { // Handle setting the FOV for the flame mesh and emitters if( FlamePool[0] != none ) { FlamePool[0].SetFOV(MySkelMesh.FOV); } if( FlamePool[1] != none ) { FlamePool[0].SetFOV(MySkelMesh.FOV); } if( PSC_PilotLight != none ) { PSC_PilotLight.SetFOV(MySkelMesh.FOV); } if( PSC_EndSpray != none ) { PSC_EndSpray.SetFOV(MySkelMesh.FOV); } } } /********************************************************************************************* * Trader ********************************************************************************************/ /** Allows weapon to calculate its own damage for display in trader. * Overridden to use spray actor damage / damage type. */ static simulated function float CalculateTraderWeaponStatDamage() { local float BaseDamage, DoTDamage; local class DamageType; BaseDamage = default.FlameSprayArchetype.SprayDamage.X; DamageType = default.FlameSprayArchetype.MyDamageType; if( DamageType != none && DamageType.default.DoT_Type != DOT_None ) { DoTDamage = (DamageType.default.DoT_Duration / DamageType.default.DoT_Interval) * (BaseDamage * DamageType.default.DoT_DamageScale); } return BaseDamage + DoTDamage; } /** Returns trader filter index based on weapon type */ static simulated event EFilterTypeUI GetTraderFilter() { return FT_Flame; } defaultproperties { Begin Object Class=KFParticleSystemComponent Name=FlameEndSpray0 bAutoActivate=FALSE TickGroup=TG_PostUpdateWork End Object PSC_EndSpray=FlameEndSpray0 Begin Object Class=KFParticleSystemComponent Name=PilotLight0 DepthPriorityGroup=SDPG_Foreground bAutoActivate=TRUE TickGroup=TG_PostUpdateWork End Object PSC_PilotLight=PilotLight0 bWeaponNeedsServerPosition=true // Aim Assist AimCorrectionSize=0.f // AI Warning MaxAIWarningDistSQ=1000000 MaxAIWarningDistFromPointSQ=40000 }