diff --git a/KFGame/Classes/KFDroppedPickup.uc b/KFGame/Classes/KFDroppedPickup.uc index 2d7a4dd..5b6f152 100644 --- a/KFGame/Classes/KFDroppedPickup.uc +++ b/KFGame/Classes/KFDroppedPickup.uc @@ -48,6 +48,9 @@ var protected float PostAuthorityChangeLifeSpan; /** Amount of delay before anything can pick this up */ var protected float PickupDelay; +/** Previous Owner (Added for Autoturret) */ +var KFPlayerController PreviousOwner; + // Visuals var bool bEmptyPickup; // pickup has an inventory with no ammo var LinearColor EmptyPickupColor; @@ -430,6 +433,11 @@ function GiveTo(Pawn P) KFW = KFWeapon(NewInventory); if (KFW != none) { + if (PreviousOWner != none) + { + KFW.KFPlayer = PreviousOwner; + } + KFW.SetOriginalValuesFromPickup(KFWeapon(Inventory)); KFW = KFIM.CombineWeaponsOnPickup(KFW); KFW.NotifyPickedUp(); diff --git a/KFGame/Classes/KFPerk_Commando.uc b/KFGame/Classes/KFPerk_Commando.uc index ebbac28..3221592 100644 --- a/KFGame/Classes/KFPerk_Commando.uc +++ b/KFGame/Classes/KFPerk_Commando.uc @@ -252,7 +252,12 @@ simulated function ModifyMagSizeAndNumber( KFWeapon KFW, out int MagazineCapacit // FAMAS needs its secondary ammo affected // Autoturret cannot modify its magazine capacity - if( (!bSecondary || IsFAMAS(KFW)) && !IsAutoTurret(KFW) && IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) && (KFW == none || !KFW.bNoMagazine) ) + if (IsAutoTurret(KFW) || WeaponClassName == 'KFWeap_Autoturret') + { + return; + } + + if( (!bSecondary || IsFAMAS(KFW)) && IsWeaponOnPerk( KFW, WeaponPerkClass, self.class ) && (KFW == none || !KFW.bNoMagazine) ) { if( IsLargeMagActive() ) { @@ -509,7 +514,7 @@ final private function bool IsEatLeadActive() * * @return true/false */ -final private function bool IsAmmoVestActive() +simulated final private function bool IsAmmoVestActive() { return PerkSkills[ECommandoAmmoVest].bActive && IsPerkLevelAllowed(ECommandoAmmoVest); } diff --git a/KFGame/Classes/KFPlayerController.uc b/KFGame/Classes/KFPlayerController.uc index e303260..442eadc 100644 --- a/KFGame/Classes/KFPlayerController.uc +++ b/KFGame/Classes/KFPlayerController.uc @@ -9280,6 +9280,7 @@ reliable client function ClientDrawDebugCylinder(vector CylinderLocation, float event Destroyed() { local KFProjectile KFProj; + local int i; // Stop currently playing stingers when the map is being switched if( StingerAkComponent != none ) @@ -9289,10 +9290,11 @@ event Destroyed() // Destroy deployed turrets // Destroyed event on Turret calls the KFPlayer to modify the same list, that's why we use a while loop... - while (DeployedTurrets.Length > 0) + for (i = 0; i < DeployedTurrets.Length; i++) { - DeployedTurrets[0].Destroy(); + DeployedTurrets[i].Destroy(); } + DeployedTurrets.Remove(0, DeployedTurrets.Length); SetRTPCValue( 'Health', 100, true ); PostAkEvent( LowHealthStopEvent ); diff --git a/KFGameContent/Classes/KFPawn_AutoTurret.uc b/KFGameContent/Classes/KFPawn_AutoTurret.uc index 400ed0e..d1734e4 100644 --- a/KFGameContent/Classes/KFPawn_AutoTurret.uc +++ b/KFGameContent/Classes/KFPawn_AutoTurret.uc @@ -58,6 +58,7 @@ var repnotify rotator ReplicatedRotation; var repnotify float CurrentAmmoPercentage; var repnotify AKEvent TurretWeaponAmbientSound; var repnotify int WeaponSkinID; +var repnotify int AutoTurretFlashCount; var transient rotator DeployRotation; @@ -107,7 +108,7 @@ var transient ParticleSystemComponent NoAmmoFX; replication { if( bNetDirty ) - CurrentState, ReplicatedRotation, CurrentAmmoPercentage, TurretWeaponAmbientSound, EnemyTarget, WeaponSkinID; + CurrentState, ReplicatedRotation, CurrentAmmoPercentage, TurretWeaponAmbientSound, EnemyTarget, WeaponSkinID, AutoTurretFlashCount; } simulated event ReplicatedEvent(name VarName) @@ -132,6 +133,14 @@ simulated event ReplicatedEvent(name VarName) { SetWeaponSkin(WeaponSkinID); } + else if (VarName == nameof(AutoTurretFlashCount)) + { + FlashCountUpdated(Weapon, AutoTurretFlashCount, TRUE); + } + else if (VarName == nameof(FlashCount)) + { + // Intercept Flash Count: do nothing + } else { super.ReplicatedEvent(VarName); @@ -158,6 +167,8 @@ simulated event PreBeginPlay() if (Weapon != none) { + Weapon.bReplicateInstigator=true; + Weapon.bReplicateMovement=true; Weapon.Instigator = Instigator; TurretWeapon.InstigatorDrone = self; Weapon.SetCollision(false, false); @@ -318,16 +329,6 @@ auto simulated state Throw } } - simulated function EndState(name NextStateName) - { - super.EndState(NextStateName); - - if (Role == Role_Authority) - { - UpdateReadyToUse(true); - } - } - simulated event Landed(vector HitNormal, actor FloorActor) { super.Landed(HitNormal, FloorActor); @@ -1036,6 +1037,28 @@ simulated function SetWeaponAmbientSound(AkEvent NewAmbientSound, optional AkEve } } +/** + * This function's responsibility is to signal clients that non-instant hit shot + * has been fired. Call this on the server and local player. + * + * Network: Server and Local Player + */ +simulated function IncrementFlashCount(Weapon InWeapon, byte InFiringMode) +{ + Super.IncrementFlashCount(InWeapon, InFiringMode); + AutoTurretFlashCount = FlashCount; + // bNetDirty = true; + bForceNetUpdate = true; +} + +simulated function ClearFlashCount(Weapon InWeapon) +{ + Super.ClearFlashCount(InWeapon); + + AutoTurretFlashCount = FlashCount; + bForceNetUpdate=true; +} + defaultproperties { bCollideComplex=TRUE @@ -1136,4 +1159,8 @@ defaultproperties bIsTurret=true NoAmmoFX=none + + bAlwaysRelevant=true + + AutoTurretFlashCount=0 } diff --git a/KFGameContent/Classes/KFWeap_AutoTurret.uc b/KFGameContent/Classes/KFWeap_AutoTurret.uc index 3039547..76019c6 100644 --- a/KFGameContent/Classes/KFWeap_AutoTurret.uc +++ b/KFGameContent/Classes/KFWeap_AutoTurret.uc @@ -17,8 +17,6 @@ var(Animations) const editconst name DetonateLastAnim; /** Sound to play upon successful detonation */ var() AkEvent DetonateAkEvent; -/** Sound to play upon attempted but unsuccessful detonation */ -var() AkEvent DryFireAkEvent; /** Strenght applied to forward dir to get the throwing velocity */ var const float ThrowStrength; @@ -119,6 +117,7 @@ simulated function Projectile ProjectileFire() KFPC.DeployedTurrets.AddItem( SpawnedActor ); NumDeployedTurrets = KFPC.DeployedTurrets.Length; + bTurretReadyToUse = false; bForceNetUpdate = true; } @@ -179,14 +178,20 @@ simulated function GetTurretSpawnLocationAndDir(out vector SpawnLocation, out ve /** Detonates the oldest turret */ simulated function Detonate() { + local int i; + local array TurretsCopy; + // auto switch weapon when out of ammo and after detonating the last deployed turret if( Role == ROLE_Authority ) { - if( KFPC.DeployedTurrets.Length > 0 ) + TurretsCopy = KFPC.DeployedTurrets; + for (i = 0; i < TurretsCopy.Length; i++) { - KFPawn_AutoTurret(KFPC.DeployedTurrets[0]).SetTurretState(ETS_Detonate); + KFPawn_AutoTurret(TurretsCopy[i]).SetTurretState(ETS_Detonate); } - + + KFPC.DeployedTurrets.Remove(0, KFPC.DeployedTurrets.Length); + SetReadyToUse(true); if( !HasAnyAmmo() && NumDeployedTurrets == 0 ) @@ -224,11 +229,13 @@ function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon ) super.SetOriginalValuesFromPickup( PickedUpWeapon ); - if (PickedUpWeapon.Instigator.Controller != none && PickedUpWeapon.Instigator.Controller != KFPC) + if (PickedUpWeapon.KFPlayer != none && PickedUpWeapon.KFPlayer != KFPC) { - KFPC.DeployedTurrets = KFPlayerController(PickedUpWeapon.Instigator.Controller).DeployedTurrets; + KFPC.DeployedTurrets = PickedUpWeapon.KFPlayer.DeployedTurrets; } + PickedUpWeapon.KFPlayer = none; + NumDeployedTurrets = KFPC.DeployedTurrets.Length; bForceNetUpdate = true; @@ -245,6 +252,62 @@ function SetOriginalValuesFromPickup( KFWeapon PickedUpWeapon ) } } + +/** + * Drop this item out in to the world + */ +function DropFrom(vector StartLocation, vector StartVelocity) +{ + local DroppedPickup P; + + // Offset spawn closer to eye location + StartLocation.Z += Instigator.BaseEyeHeight / 2; + + // for some reason, Inventory::DropFrom removes weapon from inventory whether it was able to spawn the pickup or not. + // we only want the weapon removed from inventory if pickup was successfully spawned, so instead of calling the supers, + // do all the super functionality here. + + if( !CanThrow() ) + { + return; + } + + if( DroppedPickupClass == None || DroppedPickupMesh == None ) + { + Destroy(); + return; + } + + // the last bool param is to prevent collision from preventing spawns + P = Spawn(DroppedPickupClass,,, StartLocation,,,true); + if( P == None ) + { + // if we can't spawn the pickup (likely for collision reasons), + // just return without removing from inventory or destroying, which removes from inventory + PlayerController(Instigator.Controller).ReceiveLocalizedMessage( class'KFLocalMessage_Game', GMT_FailedDropInventory ); + return; + } + + if( Instigator != None && Instigator.InvManager != None ) + { + Instigator.InvManager.RemoveFromInventory(Self); + + if( Instigator.IsAliveAndWell() && !Instigator.InvManager.bPendingDelete ) + { + `DialogManager.PlayDropWeaponDialog( KFPawn(Instigator) ); + } + } + + SetupDroppedPickup( P, StartVelocity ); + + KFDroppedPickup(P).PreviousOwner = KFPlayerController(Instigator.Controller); + + Instigator = None; + GotoState(''); + + AIController = None; +} + /** * Returns true if this weapon uses a secondary ammo pool */ @@ -288,19 +351,15 @@ simulated function BeginFire( byte FireModeNum ) { if (FireModeNum == DEFAULT_FIREMODE && NumDeployedTurrets >= MaxTurretsDeployed - && HasAmmo(THROW_FIREMODE)) + && HasAnyAmmo()) { if (!bTurretReadyToUse) { return; } - + PrepareAndDetonate(); } - else if (HasAmmo(THROW_FIREMODE) == false) - { - PlaySoundBase( DryFireAkEvent, true ); - } super.BeginFire( FireModeNum ); } @@ -322,10 +381,6 @@ simulated function PrepareAndDetonate() { PlaySoundBase( DetonateAkEvent, true ); } - else - { - PlaySoundBase( DryFireAkEvent, true ); - } if( bInSprintState ) { @@ -496,6 +551,11 @@ function CheckTurretAmmo() if (Role == Role_Authority) { + if (KFPC == none) + { + return; + } + if (KFPC.DeployedTurrets.Length > 0) { Weapon = KFWeapon(KFPawn_AutoTurret(KFPC.DeployedTurrets[0]).Weapon); @@ -514,7 +574,10 @@ function CheckTurretAmmo() else { KFP = KFPawn(Instigator); - KFP.OnWeaponSpecialAction( 1 + (CurrentAmmoPercentage * 100) ); + if (KFP != none) + { + KFP.OnWeaponSpecialAction( 1 + (CurrentAmmoPercentage * 100) ); + } } } } @@ -701,7 +764,6 @@ defaultproperties InventorySize=3 DetonateAkEvent=AkEvent'ww_wep_autoturret.Play_WEP_AutoTurret_Detonate_Trigger' - // Weapon Upgrade stat boosts //WeaponUpgrades[1]=(IncrementDamage=1.05f,IncrementWeight=1) diff --git a/KFGameContent/Classes/KFWeap_AutoTurretWeapon.uc b/KFGameContent/Classes/KFWeap_AutoTurretWeapon.uc index f7b2409..78afe4d 100644 --- a/KFGameContent/Classes/KFWeap_AutoTurretWeapon.uc +++ b/KFGameContent/Classes/KFWeap_AutoTurretWeapon.uc @@ -109,6 +109,20 @@ simulated function InstantFireClient() } } +simulated function HandleProjectileImpact(byte ProjectileFireMode, ImpactInfo Impact, optional float PenetrationValue) +{ + // local player only for clientside hit detection + if ( Instigator != None) + { + if ( Instigator.Role < ROLE_Authority ) + { + SendClientProjectileImpact(ProjectileFireMode, Impact, PenetrationValue); + } + + ProcessInstantHitEx(ProjectileFireMode, Impact,, PenetrationValue, 0); + } +} + simulated function Projectile ProjectileFire() { @@ -144,6 +158,55 @@ simulated function Projectile ProjectileFire() return None; } +simulated function KFProjectile SpawnProjectile( class KFProjClass, vector RealStartLoc, vector AimDir ) +{ + local KFProjectile SpawnedProjectile; + local int ProjDamage; + local Pawn OriginalInstigator; + + /* + * Instigator issues here. The instigator of the weapon here is the PlayerController which won't replicate the projectile. + * Changing it to the drone pawn for spawning, then swapping it again to be able to apply perk effects. + */ + + // Spawn projectile + OriginalInstigator = Instigator; + Instigator = InstigatorDrone; + SpawnedProjectile = Spawn( KFProjClass, self,, RealStartLoc); + if( SpawnedProjectile != none && !SpawnedProjectile.bDeleteMe ) + { + // Mirror damage and damage type from weapon. This is set on the server only and + // these properties are replicated via TakeHitInfo + if ( InstantHitDamage.Length > CurrentFireMode && InstantHitDamageTypes.Length > CurrentFireMode ) + { + ProjDamage = GetModifiedDamage(CurrentFireMode); + SpawnedProjectile.Damage = ProjDamage; + SpawnedProjectile.MyDamageType = InstantHitDamageTypes[CurrentFireMode]; + } + + // Set the penetration power for this projectile + // because of clientside hit detection, we need two variables -- + // one that replicates on init and one that updates but doesn't replicate + SpawnedProjectile.InitialPenetrationPower = GetInitialPenetrationPower(CurrentFireMode); + SpawnedProjectile.PenetrationPower = SpawnedProjectile.InitialPenetrationPower; + + SpawnedProjectile.UpgradeDamageMod = GetUpgradeDamageMod(); + SpawnedProjectile.Init( AimDir ); + } + + if (MedicComp != none && KFProj_HealingDart(SpawnedProjectile) != None) + { + if (TargetingComp != none && TargetingComp.LockedTarget[1] != none) + { + KFProj_HealingDart(SpawnedProjectile).SeekTarget = TargetingComp.LockedTarget[1]; + } + } + + Instigator = OriginalInstigator; + // return it up the line + return SpawnedProjectile; +} + simulated function IncrementFlashCount() { local KFPawn P; @@ -293,6 +356,22 @@ simulated function PlayFireEffects( byte FireModeNum, optional vector HitLocatio } } +simulated function WeaponPlayFireSound(AkBaseSoundObject DefaultSound, AkBaseSoundObject FirstPersonSound) +{ + // ReplicateSound needs an "out" vector + local vector SoundLocation; + + if( Owner != None && !bSuppressSounds ) + { + SoundLocation = KFPawn(Owner).GetPawnViewLocation(); + + if ( DefaultSound != None ) + { + Owner.PlaySoundBase( DefaultSound, false, false, false, SoundLocation ); + } + } +} + /** True if we want to override the looping fire sounds with fire sounds from another firemode */ simulated function bool ShouldForceSingleFireSound() { @@ -385,7 +464,7 @@ defaultproperties WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_LP_3P', FirstPersonCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_LP_1P') WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_EndLP_3P', FirstPersonCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shot_EndLP_1P') - WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shoot_3P', FirstPersonCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shoot_1P') + WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shoot_3P', FirstPersonCue=AkEvent'WW_WEP_Autoturret.Play_WEP_AutoTurret_Shoot_3P') SingleFireSoundIndex=ALTFIRE_FIREMODE bLoopingFireSnd(DEFAULT_FIREMODE)=true