class ScrnStoryGameInfo extends KFStoryGameInfo; var ScrnBalance ScrnBalanceMut; var protected transient int ObjTraderIndex; event InitGame( string Options, out string Error ) { local int ConfigMaxPlayers; ObjTraderIndex = -1; ConfigMaxPlayers = default.MaxPlayers; super.InitGame(Options, Error); CheckScrnBalance(); MaxPlayers = Clamp(GetIntOption( Options, "MaxPlayers", MaxPlayers ),0,32); default.MaxPlayers = Clamp( ConfigMaxPlayers, 0, 32 ); log("MonsterCollection = " $ MonsterCollection); } protected function CheckScrnBalance() { if ( ScrnBalanceMut == none ) { log("ScrnBalance is not loaded! Loading it now...", class.name); AddMutator(class.outer.name $ ".ScrnBalance", false); if ( ScrnBalanceMut == none ) log("Unable to spawn ScrnBalance!", class.name); } } event PostLogin( PlayerController NewPlayer ) { super.PostLogin(NewPlayer); if ( ScrnPlayerController(NewPlayer) != none ) ScrnPlayerController(NewPlayer).PostLogin(); } function RestartPlayer( Controller aPlayer ) { super.RestartPlayer(aPlayer); if ( FriendlyFireScale > 0 && aPlayer.Pawn != none && PlayerController(aPlayer) != none ) { ScrnBalanceMut.SendFriendlyFireWarning(PlayerController(aPlayer)); } } // C&P from Deathmatch strip color tags before name length check function ChangeName(Controller Other, string S, bool bNameChange) { local Controller APlayer,C, P; if ( S == "" ) return; S = StripColor(s); // Stip out color codes if (Other.PlayerReplicationInfo.playername~=S) return; if ( len(ScrnBalanceMut.StripColorTags(S)) > 20 ) S = Left( ScrnBalanceMut.StripColorTags(S), 20 ); ReplaceText(S, " ", "_"); ReplaceText(S, "|", "I"); if ( bEpicNames && (Bot(Other) != None) ) { if ( TotalEpic < 21 ) { S = EpicNames[EpicOffset % 21]; EpicOffset++; TotalEpic++; } else { S = NamePrefixes[NameNumber%10]$"CliffyB"$NameSuffixes[NameNumber%10]; NameNumber++; } } for( APlayer=Level.ControllerList; APlayer!=None; APlayer=APlayer.nextController ) if ( APlayer.bIsPlayer && (APlayer.PlayerReplicationInfo.playername~=S) ) { if ( Other.IsA('PlayerController') ) { PlayerController(Other).ReceiveLocalizedMessage( GameMessageClass, 8 ); return; } else { if ( Other.PlayerReplicationInfo.bIsFemale ) { S = FemaleBackupNames[FemaleBackupNameOffset%32]; FemaleBackupNameOffset++; } else { S = MaleBackupNames[MaleBackupNameOffset%32]; MaleBackupNameOffset++; } for( P=Level.ControllerList; P!=None; P=P.nextController ) if ( P.bIsPlayer && (P.PlayerReplicationInfo.playername~=S) ) { S = NamePrefixes[NameNumber%10]$S$NameSuffixes[NameNumber%10]; NameNumber++; break; } break; } S = NamePrefixes[NameNumber%10]$S$NameSuffixes[NameNumber%10]; NameNumber++; break; } if( bNameChange ) GameEvent("NameChange",s,Other.PlayerReplicationInfo); if ( S ~= "CliffyB" ) bEpicNames = true; Other.PlayerReplicationInfo.SetPlayerName(S); // notify local players if ( bNameChange ) for ( C=Level.ControllerList; C!=None; C=C.NextController ) if ( (PlayerController(C) != None) && (Viewport(PlayerController(C).Player) != None) ) PlayerController(C).ReceiveLocalizedMessage( class'GameMessage', 2, Other.PlayerReplicationInfo ); } // fixed GameRules.NetDamage() call function int ReduceDamage(int Damage, pawn injured, pawn instigatedBy, vector HitLocation, out vector Momentum, class DamageType) { local KFPlayerController PC; if ( KFPawn(Injured) != none ) { if ( KFPlayerReplicationInfo(Injured.PlayerReplicationInfo) != none && KFPlayerReplicationInfo(Injured.PlayerReplicationInfo).ClientVeteranSkill != none ) { Damage = KFPlayerReplicationInfo(Injured.PlayerReplicationInfo).ClientVeteranSkill.Static.ReduceDamage(KFPlayerReplicationInfo(Injured.PlayerReplicationInfo), KFPawn(Injured), instigatedBy, Damage, DamageType); } } if ( instigatedBy == None || DamageType == class'DamTypeVomit' || DamageType == class'DamTypeWelder' || DamageType == class'SirenScreamDamage') { return Super(xTeamGame).ReduceDamage( Damage,injured,instigatedBy,HitLocation,Momentum,DamageType ); } if ( Monster(Injured) != None ) { if ( instigatedBy != None ) { PC = KFPlayerController(instigatedBy.Controller); if ( Class(damageType) != none && PC != none ) { Class(damageType).Static.AwardDamage(KFSteamStatsAndAchievements(PC.SteamStatsAndAchievements), Clamp(Damage, 1, Injured.Health)); } } // fixed GameRules.NetDamage() call -- PooSH return super(UnrealMPGameInfo).ReduceDamage( Damage, injured, InstigatedBy, HitLocation, Momentum, DamageType ); } if ( KFFriendlyAI(InstigatedBy.Controller) != None && KFHumanPawn(Injured) != none ) Damage *= 0.25; else if ( injured == instigatedBy ) Damage = Damage * 0.5; if ( InvasionBot(injured.Controller) != None ) { if ( !InvasionBot(injured.controller).bDamagedMessage && (injured.Health - Damage < 50) ) { InvasionBot(injured.controller).bDamagedMessage = true; if ( FRand() < 0.5 ) injured.Controller.SendMessage(None, 'OTHER', 4, 12, 'TEAM'); else injured.Controller.SendMessage(None, 'OTHER', 13, 12, 'TEAM'); } if ( GameDifficulty <= 3 ) { if ( injured.IsPlayerPawn() && (injured == instigatedby) && (Level.NetMode == NM_Standalone) ) Damage *= 0.5; //skill level modification if ( MonsterController(InstigatedBy.Controller) != None ) Damage = Damage; } } if( injured.InGodMode() ) return 0; if( instigatedBy!=injured && MonsterController(InstigatedBy.Controller)==None && (instigatedBy.Controller==None || instigatedBy.GetTeamNum()==injured.GetTeamNum()) ) { if ( class(DamageType) != None || class(DamageType) != None ) Momentum *= TeammateBoost; if ( Bot(injured.Controller) != None ) Bot(Injured.Controller).YellAt(instigatedBy); if ( FriendlyFireScale==0.0 || (Vehicle(injured) != None && Vehicle(injured).bNoFriendlyFire) ) { if ( GameRulesModifiers != None ) return GameRulesModifiers.NetDamage( Damage, 0,injured,instigatedBy,HitLocation,Momentum,DamageType ); else return 0; } Damage *= FriendlyFireScale; } // Start code from DeathMatch.uc - Had to override this here because it was reducing // bite damage (which is 1) down to zero when the skill settings were low if ( (instigatedBy != None) && (InstigatedBy != Injured) && (Level.TimeSeconds - injured.SpawnTime < SpawnProtectionTime) && (class(DamageType) != None || class(DamageType) != None) ) return 0; Damage = super(UnrealMPGameInfo).ReduceDamage( Damage, injured, InstigatedBy, HitLocation, Momentum, DamageType ); if ( instigatedBy == None) return Damage; if ( Level.Game.GameDifficulty <= 3 ) { if ( injured.IsPlayerPawn() && (injured == instigatedby) && (Level.NetMode == NM_Standalone) ) Damage *= 0.5; } return (Damage * instigatedBy.DamageScaling); // End code from DeathMatch.uc } // Fixed Spawn rate for >6 players -- PooSH function float GetAdjustedSpawnInterval(float BaseInterval, float UsedWaveTimeElapsed, bool bIgnoreSineMod) { local float SineMod; local float FinalInterval; local int TotalNumPlayers; local float PlayerCountMultiplier; local float DifficultyMultiplier; PlayerCountMultiplier = 1.f; SineMod = 1.0 - Abs(sin(UsedWaveTimeElapsed * SineWaveFreq)); DifficultyMultiplier = 1.f; /* Scale the spawn interval by the number of players */ TotalnumPlayers = NumPlayers + NumBots ; /* We're gonna pull the difficulty / player count multipliers from the editor from now on instead of hard-coding them*/ if(StoryRules != none) { switch(TotalnumPlayers) { case 1 : PlayerCountMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_PlayerCount.Scale_1P; break; case 2 : PlayerCountMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_PlayerCount.Scale_2P; break; case 3 : PlayerCountMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_PlayerCount.Scale_3P; break; case 4 : PlayerCountMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_PlayerCount.Scale_4P; break; case 5 : PlayerCountMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_PlayerCount.Scale_5P; break; case 6 : PlayerCountMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_PlayerCount.Scale_6P; break; // >6 players: +0.25 per each next player -- PooSH default : PlayerCountMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_PlayerCount.Scale_6P + (TotalnumPlayers-6)*0.25; }; // Set difficulty based values if ( GameDifficulty >= 7.0 ) // Hell on Earth { DifficultyMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_GameDifficulty.Scale_HellOnEarth; } else if ( GameDifficulty >= 5.0 ) // Suicidal { DifficultyMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_GameDifficulty.Scale_Suicidal; } else if ( GameDifficulty >= 4.0 ) // Hard { DifficultyMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_GameDifficulty.Scale_Hard; } else if ( GameDifficulty >= 2.0 ) // Normal { } else // Beginner { DifficultyMultiplier = StoryRules.Spawn_Difficulty_Scaling.EnemySpawnRate.Scale_GameDifficulty.Scale_Beginner; } } DifficultyMultiplier = 1.f / DifficultyMultiplier; PlayerCountMultiplier = 1.f / PlayerCountMultiplier; //log("Base Spawn Interval : "@BaseInterval,'Story_Debug'); if( bIgnoreSineMod ) { //log("Sine Multiplier : Ignored",'Story_Debug'); FinalInterval = FMax(BaseInterval * PlayerCountMultiplier * DifficultyMultiplier,0.1); } else { //log("Sine Multiplier : "@SineMod,'Story_Debug'); FinalInterval = FMax((BaseInterval + (SineMod * (BaseInterval*2))) * PlayerCountMultiplier * DifficultyMultiplier,0.1) ; } /*log("Player Multiplier : "@PlayerCountMultiplier,'Story_Debug'); log("Difficulty Multiplier : "@DifficultyMultiplier,'Story_Debug'); log("Final Interval : "@FinalInterval); log("UsedWaveTimeElapsed : "@UsedWaveTimeElapsed);*/ return FinalInterval; } // added check for Scorer==none -- PooSH function bool CheckMaxLives(PlayerReplicationInfo Scorer) { local KF_StoryCheckPointVolume RespawnPoint; local Controller Failer; /* Team respawn is already taking place .. Don't bother checking anything else*/ if(bPendingTeamRespawn) { return false; } if(WholeTeamIsWipedOut()) { if(CurrentObjective != none) { // added to prevent "Accessed None" warnings -- PooSH if ( Scorer != none ) Failer = Controller(Scorer.Owner); CurrentObjective.ObjectiveFailed(Failer,true); } if( CurrentCheckPoint != none && CurrentCheckPoint.GrantASecondChance(RespawnPoint) ) { bPendingTeamRespawn = true; ResetGameState(); /* Make sure the checkpoint is active so that it can respawn everyone */ if( !RespawnPoint.bIsActive ) { RespawnPoint.CheckPointActivated(RespawnPoint.Instigator,true,false); } BroadcastLocalizedMessage( CheckPointMessageClass, 1, none , None, RespawnPoint ); return false ; } } return Super(KFGameType).CheckMaxLives(Scorer); } function bool CheckEndGame(PlayerReplicationInfo Winner, string Reason) { if ( bGameEnded ) return true; // this shouldn't happen, but you know TWIs coding style ;) // Set EndGameType and Winner before calling GameRules switch(Reason) { case "LoseAction" : KFGameReplicationInfo(GameReplicationInfo).EndGameType = 1; break; case "WinAction" : GameReplicationInfo.Winner = Teams[0]; KFGameReplicationInfo(GameReplicationInfo).EndGameType = 2; break; case "LastMan" : KFGameReplicationInfo(GameReplicationInfo).EndGameType = 1; break; } if ( !super(TeamGame).CheckEndGame(Winner,Reason) ) { GameReplicationInfo.Winner = none; KFGameReplicationInfo(GameReplicationInfo).EndGameType = 0; return false; } return true; } // function EndGame(PlayerReplicationInfo Winner, string Reason) // { // if ( bGameEnded ) // return; // this shouldn't happen... // super.EndGame(Winner, Reason); // } static event class SetGameType( string MapName ) { local string prefix; prefix = Caps(Left(MapName, InStr(MapName, "-"))); if ( prefix == "KFO") return default.class; else if ( prefix == "KF" ) return Class'ScrnBalanceSrv.ScrnGameType'; return super.SetGameType( MapName ); } // overrided to set bWaveInProgress // NumMonsters check replaced with bTradingDoorsOpen function bool IsTraderTime() { bWaveInProgress = !bTradingDoorsOpen && NumMonsters > 0 && ObjTraderIndex == -1; //&& CurrentObjective != none && !CurrentObjective.IsTraderObj(); KFGameReplicationInfo(Level.GRI).bWaveInProgress = bWaveInProgress; return !bWaveInProgress; } function SetActiveObjective( KF_StoryObjective NewObjective, optional pawn ObjInstigator) { local int i; super.SetActiveObjective(NewObjective, ObjInstigator); ObjTraderIndex = -1; if ( CurrentObjective != none ) { for(i = 0 ; i < CurrentObjective.SuccessConditions.length ; ++i) { if( ObjCondition_TraderTime(CurrentObjective.SuccessConditions[i]) != none ) { ObjTraderIndex = i; break; } } } } function AmmoPickedUp(KFAmmoPickup PickedUp) { super.AmmoPickedUp(PickedUp); ScrnBalanceMut.GameRules.WaveAmmoPickups++; } State MatchInProgress { function Timer() { super.Timer(); if ( CurrentObjective != none && ObjTraderIndex != -1 ) { KFGameReplicationInfo(GameReplicationInfo).TimeToNextWave = ObjCondition_Timed(CurrentObjective.SuccessConditions[ObjTraderIndex]).RemainingSeconds; } } } defaultproperties { GameName="ScrN Objective Mode" }