450 lines
16 KiB
Ucode
450 lines
16 KiB
Ucode
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> 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<KFWeaponDamageType>(damageType) != none && PC != none )
|
|
{
|
|
Class<KFWeaponDamageType>(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<WeaponDamageType>(DamageType) != None || class<VehicleDamageType>(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<WeaponDamageType>(DamageType) != None || class<VehicleDamageType>(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<GameInfo> 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"
|
|
}
|