//============================================================================= // KFMapObjective_ExterminateWave //============================================================================= // Objective type for exterminating zeds, including bosses. This objective // simulates a typical Killing Floor "survival" wave. //============================================================================= // Killing Floor 2 // Copyright (C) 2018 Tripwire Interactive LLC //============================================================================= class KFMapObjective_ExterminateWave extends KFMapObjective_ActorBase; //============================================================================= // KFMapObjective_ExterminateWave variables //============================================================================= var() bool bBossWave; var() EBossAIType BossType; var() bool bShowBossSpawnTheatrics; var() bool bRandomBoss; var repnotify float BossHealthPct; var transient repnotify int WaveProgressIdx, BossProgressIdx; replication { if (bNetInitial) bBossWave, bShowBossSpawnTheatrics; if (bNetDirty && !bBossWave) WaveProgressIdx; if (bNetDirty && bBossWave) BossProgressIdx, BossHealthPct; } simulated event ReplicatedEvent(name VarName) { switch (VarName) { case nameof(WaveProgressIdx): TriggerWavePctProgress(); break; case nameof(BossProgressIdx): TriggerBossHealthPctProgress(); break; default: super.ReplicatedEvent(VarName); } } //============================================================================= // KFMapObjective_ExterminateWave functions //============================================================================= function NotifyZedKilled(Controller Killer, Pawn KilledPawn, bool bIsBoss) { local float OldWavePct, NewWavePct; local KFGameReplicationInfo KFGRI; local int PrevWaveProgressIdx, GenEvtIdx, i; local KFSeqEvent_ExterminateWavePct GenEvt; PrevWaveProgressIdx = WaveProgressIdx; // the killed zed is a boss, so set the boss health to zero // if this isn't done, the objective won't know that it was successfully completed when deactivated if (bIsBoss) { BossHealthPct = 0.0f; } // check the first applicable generated event (assumes all applicable events are identical) for (GenEvtIdx = 0; GenEvtIdx < GeneratedEvents.Length; GenEvtIdx++) { GenEvt = KFSeqEvent_ExterminateWavePct(GeneratedEvents[GenEvtIdx]); if (GenEvt != None) { KFGRI = KFGameReplicationInfo(WorldInfo.GRI); OldWavePct = float(KFGRI.WaveTotalAICount - (KFGRI.AIRemaining + 1)) / float(KFGRI.WaveTotalAICount); NewWavePct = float(KFGRI.WaveTotalAICount - KFGRI.AIRemaining) / float(KFGRI.WaveTotalAICount); for (i = 0; i < GenEvt.ProgressThresholds.Length; i++) { if (OldWavePct < GenEvt.ProgressThresholds[i] && NewWavePct >= GenEvt.ProgressThresholds[i]) { WaveProgressIdx = GenEvt.ProgressOutputStartIndex + i; break; } } break; } } if (WaveProgressIdx != PrevWaveProgressIdx) { TriggerWavePctProgress(); } } simulated function TriggerWavePctProgress() { local int GenEvtIdx; local KFSeqEvent_ExterminateWavePct GenEvt; local array ActivateIndices; ActivateIndices.AddItem(WaveProgressIdx); // notify all applicable generated events (assumes all applicable events are identical) for (GenEvtIdx = 0; GenEvtIdx < GeneratedEvents.Length; GenEvtIdx++) { GenEvt = KFSeqEvent_ExterminateWavePct(GeneratedEvents[GenEvtIdx]); if (GenEvt != None) { GenEvt.Reset(); GenEvt.CheckActivate(self, self,, ActivateIndices); } } } function NotifyBossDamaged(KFPawn_Monster Boss, int Damage) { local float OldHealthPct, NewHealthPct; local int PrevBossProgressIdx, GenEvtIdx, i; local KFSeqEvent_ExterminateBossHealthPct GenEvt; PrevBossProgressIdx = BossProgressIdx; OldHealthPct = float(Boss.Health + Damage) / float(Boss.HealthMax); NewHealthPct = float(Boss.Health) / float(Boss.HealthMax); BossHealthPct = NewHealthPct; // check the first applicable generated event (assumes all applicable events are identical) for(GenEvtIdx = 0; GenEvtIdx < GeneratedEvents.Length; GenEvtIdx++) { GenEvt = KFSeqEvent_ExterminateBossHealthPct(GeneratedEvents[GenEvtIdx]); if(GenEvt != None) { for (i = 0; i < GenEvt.ProgressThresholds.Length; i++) { if (OldHealthPct >= GenEvt.ProgressThresholds[i] && NewHealthPct < GenEvt.ProgressThresholds[i]) { BossProgressIdx = GenEvt.ProgressOutputStartIndex + i; break; } } break; } } if (BossProgressIdx != PrevBossProgressIdx) { TriggerBossHealthPctProgress(); } } simulated function TriggerBossHealthPctProgress() { local int GenEvtIdx; local KFSeqEvent_ExterminateBossHealthPct GenEvt; local array ActivateIndices; ActivateIndices.AddItem(BossProgressIdx); // notify all applicable generated events (assumes all applicable events are identical) for(GenEvtIdx = 0; GenEvtIdx < GeneratedEvents.Length; GenEvtIdx++) { GenEvt = KFSeqEvent_ExterminateBossHealthPct(GeneratedEvents[GenEvtIdx]); if(GenEvt != None) { GenEvt.Reset(); GenEvt.CheckActivate(self, self,, ActivateIndices); } } } //============================================================================= // KFInterface_MapObjective functions //============================================================================= // Status simulated function ActivateObjective() { super.ActivateObjective(); if (Role == ROLE_Authority) { BossHealthPct = 1.0f; bIsActive = true; if (bBossWave) { if (KFGameInfo_Objective(WorldInfo.Game) != none) { KFGameInfo_Objective(WorldInfo.Game).SetBossIndex(); } } } } simulated function DeactivateObjective() { local KFPawn_Human KFPH; super.DeactivateObjective(); if (Role == ROLE_Authority) { // failed the objective if (GetProgress() < 1.0f) { if (FailureSoundEvent != none) { PlaySoundBase(FailureSoundEvent, false, WorldInfo.NetMode == NM_DedicatedServer); } } else { foreach WorldInfo.AllPawns(class'KFPawn_Human', KFPH) { GrantReward(KFPlayerReplicationInfo(KFPH.PlayerReplicationInfo), KFPlayerController(KFPH.Controller)); } } bIsActive = false; } } simulated function bool IsActive() { return bIsActive; } simulated function bool UsesProgress() { return true; } simulated function bool IsBonus(); function bool CanActivateObjective() { return !IsCurrentGameModeBlacklisted(); } function bool IsCurrentGameModeBlacklisted() { local class CurrGameClass; foreach GameModeBlacklist(CurrGameClass) { if (CurrGameClass == WorldInfo.GRI.GameClass) { return true; } } return false; } simulated function float GetProgress() { local KFGameReplicationInfo KFGRI; KFGRI = KFGameReplicationInfo(WorldInfo.GRI); if (bBossWave) { return FClamp(1.0f - BossHealthPct, 0.0f, 1.0f); } return FClamp(float(KFGRI.WaveTotalAICount - KFGRI.AIRemaining) / float(KFGRI.WaveTotalAICount), 0.0f, 1.0f); } simulated function bool IsComplete() { return false; } simulated function float GetActivationPctChance() { return 1.f; } simulated function string GetProgressText() { return int(GetProgress() * 100) $ "%"; } simulated function string GetLocalizedRequirements() { local KFGameReplicationInfo KFGRI; KFGRI = KFGameReplicationInfo(WorldInfo.GRI); return Localize("Objectives", default.RequirementsLocKey, "KFGame") @ KFGRI.WaveTotalAICount; } simulated function bool GetIsMissionCritical() { return bIsMissionCriticalObjective; } // HUD simulated function bool ShouldDrawIcon() { return false; } simulated function Vector GetIconLocation(); simulated function DrawHUD(KFHUDBase hud, Canvas drawCanvas); defaultproperties { Begin Object Class=SpriteComponent Name=Sprite Sprite=Texture2D'EditorResources.S_Actor' HiddenGame=True AlwaysLoadOnClient=False AlwaysLoadOnServer=False End Object Components.Add(Sprite) GameModeBlacklist.Add(class'KFGameInfo_Survival') GameModeBlacklist.Add(class'KFGameInfo_VersusSurvival') GameModeBlacklist.Add(class'KFGameInfo_Endless') GameModeBlacklist.Add(class'KFGameInfo_WeeklySurvival') LocalizationPackageName="KFGame" LocalizationKey="ExterminateWaveObjective" DescriptionLocKey="ExterminateWaveDescription" NameShortLocKey="ExterminateWaveObjective" DescriptionShortLocKey="ExterminateWaveDescriptionShort" RequirementsLocKey="ExterminateWaveRequired" //DefaultIcon=Texture2D'Objectives_UI.UI_Objectives_ObjectiveMode' ObjectiveIcon=Texture2D'Objectives_UI.UI_Objectives_ObjectiveMode' PerPlayerSpawnRateMod = (1.f, 1.f, 1.f, 1.f, 1.f, 1.f) bAlwaysRelevant=true RemoteRole=ROLE_SimulatedProxy SupportedEvents.Add(class'KFSeqEvent_ExterminateWavePct') SupportedEvents.Add(class'KFSeqEvent_ExterminateBossHealthPct') WaveProgressIdx=-1 BossProgressIdx=-1 BossHealthPct=1.0f }