rott/kf_sources/ScrnBalanceSrv/Classes/ScrnPlayerController.uc
2026-07-14 20:27:09 +07:00

2914 lines
93 KiB
Ucode

/* CODE BREAKING WARNING in v5.16 Beta 12
Removed AchInfo objects. Used AchDef.CurrentProgress instead
Changed variables and functions:
+ AchStrInfo
! PendingAchievements
- CurrentAchievement
+ CurrentAchHandler, CurrentAchIndex;
! DisplayAchievementStatus(), DisplayCurrentAchievement(), PlayerTick()
ScrnAchievementEarnedMsg - requires ScrnAchievement object to be passed as optional object, switch now indicates ach index
*/
class ScrnPlayerController extends KFPCServ;
var ScrnBalance Mut;
var transient bool bHadPawn;
var byte VeterancyChangeWave; // wave number, when player has changed his perk
var globalconfig bool bManualReload, bOtherPlayerLasersBlue;
var globalconfig bool bAlwaysDisplayAchProgression; // always display a message on any achievement progress
var globalconfig int AchGroupIndex;
var globalconfig int Custom3DScopeSens;
var globalconfig bool bSpeechVote; //allow using speeches Yes and No in voting
struct SWeaponSetting
{
var globalconfig class<KFWeapon> Weapon;
var globalconfig byte SkinIndex;
var transient class<KFWeapon> SkinnedWeapon; //used on server-side only
var transient KFWeapon LastWeapon; // last weapon on which skin was used
};
var globalconfig array<SWeaponSetting> WeaponSettings;
var transient array<ScrnAchievements.AchStrInfo>PendingAchievements; //earned achievements waiting to be displayed on the HUD
//current achievement object and index to display on the HUD
var transient ScrnAchievements CurrentAchHandler;
var transient int CurrentAchIndex;
var float AchievementDisplayCooldown; // time to wait between displaying achievements, if multiple achievements were earned
var transient class<KFVeterancyTypes> InitialPerkClass;
var bool bChangedPerkDuringGame; // player changed his perk during a game
var transient bool bCowboyForWave;
var transient byte BeggingForMoney; // how many times player asked for money during this wave
var transient bool bShoppedThisWave;
var byte PathDestination; // 0 - trader, 255 - TSC base
var byte MaxVoiceMsgIn10s; // maximum number of voice messages during 10 seconds
var bool bZEDTimeActive;
var bool bHadArmor; //player had armor during the game
var bool bWeaponsLocked; // disables player's weapons pick up by other players
var localized string strLocked, strUnlocked, strLockDisabled;
var transient float LastLockMsgTime; // last time when player received a message that weapon is locked
var localized string strAlreadySpectating;
var globalconfig bool bPrioritizePerkedWeapons, bPrioritizeBoomstick;
var int StartCash; // amount of cash given to this pawn on game/wave start
struct SDualWieldable {
var class<KFWeapon> Single, Dual;
};
var array<SDualWieldable> DualWieldables;
var private transient bool bDualWieldablesLoaded;
// TSC
var string ProfilePageClassString;
var string TSCLobbyMenuClassString;
var config bool bTSCAdvancedKeyBindings; // pressing altfire while carrying the guardian gnome, sets up the base
var config bool bTSCAutoDetectKeys; // turns off bTSCAdvancedKeyBindings if a dedicated key is bound for SetupBase
var config string RedCharacter, BlueCharacter;
// not replicated yet
// todo: find an efficient way to replicate
var array<string> RedCharacters, BlueCharacters;
var class<ScrnCustomPRI> CustomPlayerReplicationInfoClass;
var ScrnCustomPRI ScrnCustomPRI;
var transient bool bDestroying; // indicates that Destroyed() is executing
var bool bDamageAck; // does server needs to acknowledge client of damages he made?
var transient byte PerkChangeWave; // wave num, when perk was changed last time
var localized string strNoPerkChanges, strPerkLocked;
var transient Controller FavoriteSpecs[2];
var localized string strSpecFavoriteAssigned, strSpecNoFavorites;
var transient Actor OldViewTarget;
var transient rotator PrevRot;
var transient int ab_warning;
var globalconfig bool bDebugRepLink, bWaveGarbageCollection;
var globalconfig string PlayerName;
// DLC checks
struct SDLC {
var int AppID;
var bool bOwnsDLC;
var bool bChecked;
};
var array<SDLC> DLC;
var transient int AchResetPassword;
struct SMusicRecord {
var byte PL, Wave;
var bool bTrader;
var string Song;
var string Artist, Title;
};
var globalconfig byte ActiveMusicPlaylist;
var globalconfig array<String> MusicPlaylistNames;
var globalconfig array<SMusicRecord> MyMusic;
var private transient array<int> MusicPlaylistIndex; // MusicPlaylistIndex[PL] = index of first playlist entry in MyMusic
var private transient bool bMusicPlaylistIndexReady;
var private transient int ActiveMusicSongIndex;
var private transient string PendingSong;
var localized string strPlayingSong;
var class<ScrnFlareCloud> FlareCloudClass;
replication
{
reliable if ( Role == ROLE_Authority )
ClientMonsterBlamed, ClientPostLogin;
unreliable if ( bDamageAck && Role == ROLE_Authority )
ClientPlayerDamaged;
unreliable if ( Role == ROLE_Authority )
ClientFlareDamage;
reliable if ( Role < ROLE_Authority )
SrvAchReset, ResetMyAchievements, ResetMapAch,
ServerDropAllWeapons, ServerLockWeapons, ServerGunSkin,
ServerAcknowledgeDamages, ServerShowPathTo,
ServerDebugRepLink, ServerFixQuickMelee,
ServerTourneyCheck, ServerKillMut, ServerKillRules,
ServerSwitchViewMode, ServerSetViewTarget;
reliable if ( bNetOwner && (bNetDirty || bNetInitial) && Role < ROLE_Authority )
bPrioritizePerkedWeapons, StartCash;
// TSC stuff
reliable if ( Role < ROLE_Authority )
ServerScoreFlag;
}
simulated function PreBeginPlay()
{
super.PreBeginPlay();
class'ScrnMagnum44Fire'.static.PreloadAssets(Level);
class'ScrnDual44MagnumFire'.static.PreloadAssets(Level);
class'ScrnMAC10Fire'.static.PreloadAssets(Level);
class'KFMod.M79Fire'.static.PreloadAssets(Level);
}
// this function is called on on server side only
// and only if using ScrnGameType, ScrnStoryGameInfo or descendants
function PostLogin()
{
ClientPostLogin();
ScrnCustomPRI.GotoState('');
if ( Level.NetMode == NM_Standalone || (Level.NetMode == NM_ListenServer && Viewport(Player) != None) )
ScrnCustomPRI.SetSteamID64(Mut.MySteamID64);
else
ScrnCustomPRI.SetSteamID64(GetPlayerIDHash());
ScrnCustomPRI.NetUpdateTime = Level.TimeSeconds - 1;
if ( PlayerName != "" )
SetName(PlayerName);
Mut.GameRules.PlayerEntering(self);
}
simulated function ClientPostLogin()
{
// DLC check moved to ScrnClientPerkRepLink
// if ( Viewport(Player) != None ) {
// CheckDLC();
// }
}
simulated function LoadMutSettings()
{
if ( Mut != none ) {
if ( Mut.bForceManualReload)
bManualReload = Mut.bManualReload;
if ( Mut.bHardCore )
bOtherPlayerLasersBlue = false;
if ( ScrnHumanPawn(Pawn) != none )
ScrnHumanPawn(Pawn).bTraderSpeedBoost = Mut.bTraderSpeedBoost;
}
else {
//this shouldn't happen
log("Player Controller can not find ScrnBalance!", 'ScrnBalance');
if ( class'ScrnBalance'.default.Mut.bForceManualReload)
bManualReload = class'ScrnBalance'.default.bManualReload;
if ( class'ScrnBalance'.default.Mut.bHardcore )
bOtherPlayerLasersBlue = false;
}
}
function InitPlayerReplicationInfo()
{
local LinkedReplicationInfo L;
super.InitPlayerReplicationInfo();
for( L=PlayerReplicationInfo.CustomReplicationInfo; L!=None; L=L.NextReplicationInfo ) {
if ( ScrnCustomPRI(L) != none )
return; // wtf?
if( L.NextReplicationInfo==None )
break;
}
ScrnCustomPRI = spawn(CustomPlayerReplicationInfoClass, self);
if ( ScrnCustomPRI != none ) {
if ( PlayerReplicationInfo.CustomReplicationInfo != none )
ScrnCustomPRI.NextReplicationInfo = PlayerReplicationInfo.CustomReplicationInfo;
PlayerReplicationInfo.CustomReplicationInfo = ScrnCustomPRI;
}
else
warn("Can not create ScrnCustomPRI: " $ CustomPlayerReplicationInfoClass);
}
//copypaste from KFPlayerController with constant value of 24 replaced by Custom3DScopeSens to set custom sensitivity for 3D scopes
function float GetMouseModifier()
{
local KFWeapon weap;
if (Pawn == none || Pawn.Weapon == none)
return -1.0;
weap = KFWeapon(Pawn.Weapon);
if (weap== none )
return -1.0;
if(weap.KFScopeDetail == KF_ModelScope && weap.ShouldDrawPortal())
{
return Custom3DScopeSens;
}
else if(weap.KFScopeDetail == KF_ModelScopeHigh && weap.ShouldDrawPortal())
{
return Custom3DScopeSens;
}
else
{
return -1.0;
}
}
simulated function ClientForceCollectGarbage()
{
ConsoleCommand("MEMSTAT", true); // write memory stats into log
if ( bWaveGarbageCollection ) {
super.ClientForceCollectGarbage();
ConsoleCommand("MEMSTAT", true);
}
}
simulated function PreloadFireModeAssetsSE(class<WeaponFire> WF, optional WeaponFire SpawnedFire)
{
local class<Projectile> P;//log("ScrnPlayerController.PreloadFireModeAssets()" @ WF, 'ScrnBalance');
if ( WF == none || WF == Class'KFMod.NoFire' )
return;
if ( class<KFFire>(WF) != none && class<KFFire>(WF).default.FireSoundRef != "" )
class<KFFire>(WF).static.PreloadAssets(Level, KFFire(SpawnedFire));
else if ( class<KFMeleeFire>(WF) != none && class<KFMeleeFire>(WF).default.FireSoundRef != "" )
class<KFMeleeFire>(WF).static.PreloadAssets(KFMeleeFire(SpawnedFire));
else if ( class<KFShotgunFire>(WF) != none && class<KFShotgunFire>(WF).default.FireSoundRef != "" )
class<KFShotgunFire>(WF).static.PreloadAssets(Level, KFShotgunFire(SpawnedFire));
// preload projectile assets
P = WF.default.ProjectileClass;
//log("Projectile =" @ P, 'ScrnBalance');
if ( P == none )
return;
if ( class<ScrnCustomShotgunBullet>(P) != none )
class<ScrnCustomShotgunBullet>(P).static.PreloadAssets();
else if ( class<CrossbuzzsawBlade>(P) != none )
class<CrossbuzzsawBlade>(P).static.PreloadAssets();
else if ( class<LAWProj>(P) != none && class<LAWProj>(P).default.StaticMeshRef != "" )
class<LAWProj>(P).static.PreloadAssets();
else if ( class<M79GrenadeProjectile>(P) != none && class<M79GrenadeProjectile>(P).default.StaticMeshRef != "" )
class<M79GrenadeProjectile>(P).static.PreloadAssets();
else if ( class<SPGrenadeProjectile>(P) != none && class<SPGrenadeProjectile>(P).default.StaticMeshRef != "" )
class<SPGrenadeProjectile>(P).static.PreloadAssets();
else if ( class<HealingProjectile>(P) != none && class<HealingProjectile>(P).default.StaticMeshRef != "" )
class<HealingProjectile>(P).static.PreloadAssets();
else if ( class<CrossbowArrow>(P) != none && class<CrossbowArrow>(P).default.MeshRef != "" )
class<CrossbowArrow>(P).static.PreloadAssets();
else if ( class<M99Bullet>(P) != none )
class<M99Bullet>(P).static.PreloadAssets();
else if ( class<PipeBombProjectile>(P) != none && class<PipeBombProjectile>(P).default.StaticMeshRef != "" )
class<PipeBombProjectile>(P).static.PreloadAssets();
// More DLC
else if ( class<SealSquealProjectile>(P) != none && class<SealSquealProjectile>(P).default.StaticMeshRef != "" )
class<SealSquealProjectile>(P).static.PreloadAssets();
}
simulated function ClientWeaponSpawned(class<Weapon> WClass, Inventory Inv)
{
local class<KFWeapon> W;
local class<KFWeaponAttachment> Att;
local Weapon Spawned;
//log("ScrnPlayerController.ClientWeaponSpawned()" @ WClass $ ". Default Mesh = " $ WClass.default.Mesh, 'ScrnBalance');
//super.ClientWeaponSpawned(WClass, Inv);
W = class<KFWeapon>(WClass);
// preload assets only for weapons that have no static ones
// damned Tripwire's code doesn't bother for cheking is there ref set or not!
if ( W != none) {
//preload weapon assets
if ( W.default.Mesh == none )
W.static.PreloadAssets(Inv);
Att = class<KFWeaponAttachment>(W.default.AttachmentClass);
// 2013/01/22 EDIT: bug fix
if ( Att != none && Att.default.Mesh == none ) {
if ( Inv != none )
Att.static.PreloadAssets(KFWeaponAttachment(Inv.ThirdPersonActor));
else
Att.static.PreloadAssets();
}
// 2014-11-23 fix
Spawned = Weapon(Inv);
if ( Spawned != none ) {
PreloadFireModeAssetsSE(W.default.FireModeClass[0], Spawned.GetFireMode(0));
PreloadFireModeAssetsSE(W.default.FireModeClass[1], Spawned.GetFireMode(0));
}
else {
PreloadFireModeAssetsSE(W.default.FireModeClass[0]);
PreloadFireModeAssetsSE(W.default.FireModeClass[1]);
}
}
}
/*
function AcknowledgePossession(Pawn P)
{
// make sure old pawn controller is right
if ( Pawn != AcknowledgedPawn ) {
if( ScrnHumanPawn(AcknowledgedPawn) != none )
ScrnHumanPawn(AcknowledgedPawn).ScrnPlayer = none;
}
if( ScrnHumanPawn(P) != none ) {
ScrnHumanPawn(P).ScrnPlayer = self;
}
super.AcknowledgePossession(P);
}
function ServerAcknowledgePossession(Pawn P, float NewHand, bool bNewAutoTaunt)
{
// make sure old pawn controller is right
if ( Pawn != AcknowledgedPawn ) {
if( ScrnHumanPawn(AcknowledgedPawn) != none )
ScrnHumanPawn(AcknowledgedPawn).ScrnPlayer = none;
}
if( ScrnHumanPawn(P) != none ) {
ScrnHumanPawn(P).ScrnPlayer = self;
}
super.ServerAcknowledgePossession(P, NewHand, bNewAutoTaunt);
}
*/
function PawnDied(Pawn P)
{
ServerDropFlag();
super.PawnDied(P);
}
function ShowBuyMenu(string wlTag,float maxweight)
{
StopForceFeedback();
ClientOpenMenu(string(Class'ScrnGUIBuyMenu'),,wlTag,string(maxweight));
}
exec function CookGrenade()
{
if ( ScrnHumanPawn(Pawn) != none )
ScrnHumanPawn(Pawn).CookGrenade();
}
exec function ThrowCookedGrenade()
{
if ( ScrnHumanPawn(Pawn) != none )
ScrnHumanPawn(Pawn).ThrowCookedGrenade();
}
exec function QuickMelee()
{
if ( ScrnHumanPawn(Pawn) != none )
ScrnHumanPawn(Pawn).QuickMelee();
}
/**
* DamTypeNum:
* 0 - normal
* 1 - headshot
* 2 - fire DoT
*/
function DamageMade(int Damage, vector HitLocation, byte DamTypeNum)
{
local ScrnPlayerController ScrnPC;
local int i;
ClientPlayerDamaged(Damage, HitLocation, DamTypeNum);
if ( Role == ROLE_Authority && ScrnHumanPawn(Pawn) != none && ScrnHumanPawn(Pawn).bViewTarget ) {
// show damage popups on spectating players
for ( i=0; i<Level.GRI.PRIArray.length; ++i ) {
if ( Level.GRI.PRIArray[i] != none && Level.GRI.PRIArray[i].bOnlySpectator ) {
ScrnPC = ScrnPlayerController(Level.GRI.PRIArray[i].Owner);
if ( ScrnPC != none && ScrnPC != self && ScrnPC.ViewTarget == Pawn )
ScrnPC.ClientPlayerDamaged(Damage, HitLocation, DamTypeNum);
}
}
}
}
simulated function ClientPlayerDamaged(int Damage, vector HitLocation, byte DamTypeNum)
{
local ScrnHUD hud;
hud = ScrnHUD(myHUD);
if ( hud != none ) {
if ( hud.bShowDamages )
hud.ShowDamage(Damage, Level.TimeSeconds, HitLocation, DamTypeNum);
else
ServerAcknowledgeDamages(false); // tell server we don't need damage acks
}
}
simulated function ClientFlareDamage(KFMonster Victim, byte DamageX4, byte BurnTime)
{
local ScrnFlareCloud cloud;
if ( Level.NetMode == NM_DedicatedServer )
return;
if ( Victim == none )
return; // victim is not replicated
cloud = Spawn(FlareCloudClass, Victim,, Victim.Location, rotator(vect(0,0,1)));
cloud.SetDamage(DamageX4 * 4);
cloud.SetLifeSpan(BurnTime);
}
function ServerAcknowledgeDamages(bool bWantDamage)
{
bDamageAck = bWantDamage;
}
simulated function ClientMonsterBlamed(class<KFMonster> BlamedMonsterClass)
{
local ScrnHUD hud;
hud = ScrnHUD(myHUD);
if ( hud != none ) {
hud.BlameCountdown = hud.default.BlameCountdown;
hud.BlamedMonsterClass = BlamedMonsterClass;
}
}
exec function ScrnInit()
{
local LinkedReplicationInfo L;
// reload ScrnBalance settings
Mut.InitSettings();
// reload custom weapon bonuses
for ( L = Mut.CustomWeaponLink; L != none; L = L.NextReplicationInfo ) {
ScrnCustomWeaponLink(L).LoadWeaponBonuses();
}
}
simulated function PlayerTick( float DeltaTime )
{
local float y, p;
super.PlayerTick(DeltaTime);
//log("Player Controller Tick", 'ScrnBalance');
if ( Viewport(Player) != None ) {
AchievementDisplayCooldown -= DeltaTime;
if ( PendingAchievements.Length > 0 && AchievementDisplayCooldown <= 0 ) {
CurrentAchHandler = PendingAchievements[0].AchHandler;
CurrentAchIndex = PendingAchievements[0].AchIndex;
PendingAchievements.remove(0, 1);
DisplayCurrentAchievement();
}
if ( Pawn != none && Pawn.Health > 0 ) {
if ( PrevRot.Yaw != 0 ) {
y = abs(Rotation.Yaw - PrevRot.Yaw);
p = abs(Rotation.Yaw - PrevRot.Yaw);
if ( y<10000 && p<10000 && p+y>2500 /*&& (p*p + y*y) > 4000000*/ ) {
if ( IsAimingToZedHead() )
ab_warning+=10; // you made inssanely quick mouse movement and pointed exactly into enemy head. What are you, aimbot?..
else
ab_warning--; // nah, seems like you need to reduce mouse sensitivity...
}
}
PrevRot = Rotation;
}
// set typing to true also when console is open
if ( Player.Console != none ) {
bIsTyping = Player.Console.bVisible || Player.Console.bTyping || Player.GUIController.bActive;
if ( Pawn != none && bIsTyping != Pawn.bIsTyping )
Typing(bIsTyping); // replicate state to server
}
if ( KFInterAct != none && PendingSong != "" && PendingSong == KFInterAct.ActiveSong ) {
// don't bother with this, if player has turned off music
if ( GetMusicVolume() > 0 ) {
if ( KFInterAct.ActiveHandel == 0) {
// unable to start song - play default one
ClientMessage("Unable to play music file: '"$PendingSong$".ogg'. Playing default song instead.", 'Log');
PlayDefaultMusic(class'ScrnMusicTrigger');
}
else
SongPlaying(PendingSong, ActiveMusicSongIndex);
}
PendingSong = "";
}
}
}
// TraderMenuOpened and TraderMenuClosed execute on client-side only
simulated function TraderMenuOpened()
{
bShopping = true;
}
simulated function TraderMenuClosed()
{
if ( Mut.bTSCGame )
ServerShowPathTo(1); // go to TSC base
bShopping = false;
}
function SetShowPathToTrader(bool bShouldShowPath)
{
if ( bShouldShowPath )
PathDestination = 0; // trader
else if ( PathDestination == 0)
PathDestination = 255; // turn off only if we are showing path to trader (PathDestination = 0)
ServerShowPathTo(PathDestination);
}
exec function TogglePathToTrader()
{
switch (PathDestination) {
case 0:
PathDestination = 1; // TSC base
if ( Mut.bTSCGame )
break; // otherwise go to next steap
case 1:
PathDestination = 255; // hide
break;
default:
PathDestination = 0; // trader
}
ServerShowPathTo(PathDestination);
}
function ServerShowPathTo(byte NewDestination)
{
PathDestination = NewDestination;
SetTimer(TraderPathInterval, true);
Timer();
}
// Show the path the trader
function Timer()
{
if( Role < ROLE_Authority )
{
SetTimer(0, false);
}
else if ( bWantsTraderPath && PathDestination != 255 )
{
UnrealMPGameInfo(Level.Game).ShowPathTo(Self, PathDestination);
}
else
{
bShowTraderPath = false;
SetTimer(0, false);
}
}
event SongPlaying(String Song, int MyIndex)
{
if ( Song == "" )
return;
if ( MyIndex >=0 && MyIndex < MyMusic.length && MyMusic[MyIndex].Song == Song ) {
if ( MyMusic[MyIndex].Title == "" )
MyMusic[MyIndex].Title = MyMusic[MyIndex].Song;
ClientMessage(strPlayingSong
$ ConsoleColorString(MyMusic[MyIndex].Artist, 192, 1, 192)
@ ConsoleColorString(MyMusic[MyIndex].Title, 255, 255, 1)
);
}
else {
for ( MyIndex = 0; MyIndex < MyMusic.length; ++MyIndex ) {
if ( MyMusic[MyIndex].Song == Song )
SongPlaying(Song, MyIndex);
}
}
}
function UpdateMusicPlaylistIndex()
{
local int i, prev_pl;
bMusicPlaylistIndexReady = true;
for ( i=0; i<MyMusic.length; ++i ) {
if ( MyMusic[i].PL != prev_pl ) {
if ( MyMusic[i].PL < prev_pl ) {
ClientMessage("MyMusic broken! MyMusic must be sorded in (PL,Wave,bTrader) order! Line Number = " $i, 'Log');
MusicPlaylistIndex.Length = 0;
return;
}
prev_pl = MyMusic[i].PL;
MusicPlaylistIndex[prev_pl] = i;
}
}
}
/**
* Gets song name from MyMusic according to playlist (PL) and wave numbers.
* Function doesn't check if music file exists.
*
* @param PL Playlist number as defined in MyMusic.PL. Starting with 1. PL=0 is reserved for default music.
* Calling GetMySong with PL = 0 always returns empty string.
* @param Wave Player-friendly wave number, i.e. 1 - Wave 1, 11 - Boss wave etc.
* Calling GetMySong with Wave=0 returns default song for this playlist
* @param bTrader Are we looking for calm song for trader time (bTrader=True) or battle song (false)?
* @return Record index in MyMusic. -1 if not found
*/
function int GetMySongIndex(byte PL, byte Wave, bool bTrader)
{
local int i, start, end;
local bool bFound;
if ( !bMusicPlaylistIndexReady )
UpdateMusicPlaylistIndex();
if ( PL == 0 )
return -1;
if ( PL >= MusicPlaylistIndex.Length ) {
if ( ActiveMusicPlaylist == PL )
ActiveMusicPlaylist = 0;
ClientMessage("Music Playlist #"$PL $" is not defined", 'Log');
return -1;
}
start = MusicPlaylistIndex[PL];
if ( start >= MyMusic.Length ) {
ClientMessage("Music Playlist #"$PL $" has no songs or MyMusic is not sorted", 'Log');
return -1;
}
if ( (PL+1) == MusicPlaylistIndex.Length )
end = MyMusic.length;
else
end = MusicPlaylistIndex[PL+1];
for ( i = start; i < end; ++i ) {
if ( bFound ) {
if ( MyMusic[i].Wave != Wave || MyMusic[i].bTrader != bTrader ) {
end = i;
break;
}
}
else if ( MyMusic[i].Wave == Wave && MyMusic[i].bTrader == bTrader ) {
bFound = true;
start = i;
}
}
if ( bFound ) {
if ( start + 1 == end )
return start;
else
return start + Rand(end-start);
}
else if ( Wave > 0)
return GetMySongIndex(PL, 0, bTrader); // no songs defined for given wave. return default
return -1;
}
function int GetMyActiveSongIndex()
{
local KFGameReplicationInfo KFGRI;
local byte w;
KFGRI = KFGameReplicationInfo(Level.GRI);
if ( KFGRI == none )
return -1; // wtf?
if ( KF_StoryGRI(KFGRI) != none ) {
// story mode
return -1; // story music must be set explicitly by L.D.
}
w = KFGRI.WaveNumber;
if ( KFGRI.bWaveInProgress ) {
if ( w == KFGRI.FinalWave && TSCGameReplicationInfo(KFGRI) == none )
w = 10; // boss battle song
}
return GetMySongIndex(ActiveMusicPlaylist, w+1, !KFGRI.bWaveInProgress);
}
function PlayDefaultMusic(class<KFMusicTrigger> M)
{
local string sng;
if ( KFInterAct == none )
return;
sng = GetSongFromMusicTrigger(M);
if ( sng != "" )
KFInterAct.SetSong(sng, M.default.FadeInTime, M.default.FadeOutTime);
}
function string GetSongFromMusicTrigger(class<KFMusicTrigger> M)
{
local KFGameReplicationInfo KFGRI;
local string sng;
local int w;
KFGRI = KFGameReplicationInfo(Level.GRI);
if ( KFGRI == none )
return sng; // wtf?
if ( KF_StoryGRI(KFGRI) != none )
return sng; // story music must be set explicitly by L.D.
w = KFGRI.WaveNumber;
if ( KFGRI.bWaveInProgress ) {
if ( w == KFGRI.FinalWave && M.default.WaveBasedSongs.length > 10 && TSCGameReplicationInfo(KFGRI) == none )
sng = M.default.WaveBasedSongs[10].CombatSong; // boss battle song
else if ( w < M.default.WaveBasedSongs.length )
sng = M.default.WaveBasedSongs[w].CombatSong;
else
sng = M.default.CombatSong;
}
else {
if ( KFGRI.TimeToNextWave > 10 )
w++; // KFGRI.WaveNumber is updated only at the end of the trader time
if ( w < M.default.WaveBasedSongs.length )
sng = M.default.WaveBasedSongs[w].CalmSong;
else
sng = M.default.Song;
}
return sng;
}
function int GetActiveMusicSongIndex()
{
return ActiveMusicSongIndex;
}
function NetPlayMusic( string Song, float FadeInTime, float FadeOutTime )
{
if ( ActiveMusicPlaylist > 0 ) {
ActiveMusicSongIndex = GetMyActiveSongIndex();
if ( ActiveMusicSongIndex != -1 )
Song = MyMusic[ActiveMusicSongIndex].Song;
}
else
ActiveMusicSongIndex = -1;
PendingSong = Song;
super.NetPlayMusic(Song, FadeInTime, FadeOutTime);
}
exec function PlayMyMusic(int PL)
{
local int i;
if ( !bMusicPlaylistIndexReady )
UpdateMusicPlaylistIndex();
if ( MusicPlaylistIndex.length <= 1 ) {
ConsoleMessage("No user-defined music available", 0, 255, 1, 1);
return;
}
if ( PL >= MusicPlaylistIndex.length ) {
ConsoleMessage("Wrong playlist number!", 0, 255, 1, 1);
PL = 0;
}
if ( PL == 0) {
ConsoleMessage("Available music playlists:");
ConsoleMessage("---------------------------------------------");
for ( i=1; i<MusicPlaylistIndex.length; ++i ) {
if ( ActiveMusicPlaylist == i )
ConsoleMessage(i @ MusicPlaylistNames[i], 0, 1, 255, 1);
else
ConsoleMessage(i @ MusicPlaylistNames[i]);
}
ConsoleMessage("---------------------------------------------");
if ( ActiveMusicSongIndex != -1 )
ConsoleMessage(strPlayingSong
$ ConsoleColorString(MyMusic[ActiveMusicSongIndex].Artist, 192, 1, 192)
@ ConsoleColorString(MyMusic[ActiveMusicSongIndex].Title, 255, 255, 1)
);
return;
}
ActiveMusicPlaylist = PL;
NetPlayMusic("ThisWillBeReplacedWithDefaultWaveTrack", 1, 1);
ClientMessage(ConsoleColorString(MusicPlaylistNames[PL], 1, 192, 1)
$ ConsoleColorString(" activated", 192, 192, 192));
SaveConfig();
}
exec function StopMyMusic(int PL)
{
if ( ActiveMusicPlaylist > 0 ) {
ActiveMusicPlaylist = 0;
ClientMessage("User-defined music disabled. Map-specific music will be restored next wave.");
SaveConfig();
}
}
function float GetMusicVolume()
{
return float(ConsoleCommand("get ini:Engine.Engine.AudioDevice MusicVolume"));
}
exec function MusicVolume(coerce string vol)
{
if ( vol == "" )
ClientMessage("Music Volume = " $ ConsoleCommand("get ini:Engine.Engine.AudioDevice MusicVolume"));
else {
ConsoleCommand("set ini:Engine.Engine.AudioDevice MusicVolume" @ vol);
ClientMessage("Music Volume set to " $ ConsoleCommand("get ini:Engine.Engine.AudioDevice MusicVolume"));
}
}
exec function SoundVolume(coerce string vol)
{
if ( vol == "" )
ClientMessage("Sound Volume = " $ ConsoleCommand("get ini:Engine.Engine.AudioDevice SoundVolume"));
else {
ConsoleCommand("set ini:Engine.Engine.AudioDevice SoundVolume" @ vol);
ClientMessage("Sound Volume set to " $ ConsoleCommand("get ini:Engine.Engine.AudioDevice SoundVolume"));
}
}
function bool IsAimingToZedHead()
{
local Vector vNull, StartTrace, HeadLoc, LookDir, HeadDir;
local KFMonster HitMonster;
local float cosine;
if ( Pawn == none )
return false;
StartTrace = Pawn.Location + Pawn.EyePosition(); // A
LookDir = Normal(Vector(Rotation)); // B-A
foreach CollidingActors(class'KFMonster', HitMonster, 2500, StartTrace) {
HeadLoc = HitMonster.GetBoneCoords('head').Origin;
if ( HeadLoc == vNull )
continue; // no head
if ( !FastTrace(HeadLoc, StartTrace) )
continue; // head isn't visible
// I should have better learned geometry in school. It'd really helped :)
// LookDir - direction where player is lookin
// HeadDir - direction to the zed's head (HeadLoc)
// We need to get a distance between HeadLoc and our LookDir line,
// i.e. perpenducular line from HeadLoc to LookDir line.
// We get right-angled triangle, where (HeadLoc-StartTrace) is Hypotenuse,
// LookDir line - Adjacent side and perpendicular line - opposite side.
// Opposite = Hypotenuse * sin, where sin = 1 - cos,
// and cos = dot product of two normalized direction vectors.
HeadDir = normal(HeadLoc - StartTrace);
// cos=1.0 - target in front of us
// cos=0.0 - target is at side (left or right)
// cos=0.5 - target is 45 degrees from our looking direction
cosine = LookDir dot HeadDir;
if ( cosine > 0.9 && VSizeSquared(HeadLoc - StartTrace) * (1.0-cosine) < 25 )
return true;
}
return false;
}
simulated function DisplayCurrentAchievement()
{
if ( Viewport(Player) == None )
return;
//log("DisplayCurrentAchievement:" @CurrentAchievement.ID @ CurrentAchievement.CurrentProgress$"/"$ CurrentAchievement.MaxProgress ,'ScrnBalance');
if ( CurrentAchHandler != none ) {
if ( CurrentAchHandler.AchDefs[CurrentAchIndex].bUnlockedJustNow ) {
//set cooldown only for earned achievements. Status updates can be immediately overrided
AchievementDisplayCooldown = default.AchievementDisplayCooldown;
ClientPlaySound(Sound'KF_InterfaceSnd.Perks.PerkAchieved',true,2.f,SLOT_Talk);
ClientPlaySound(Sound'KF_InterfaceSnd.Perks.PerkAchieved',true,2.f,SLOT_Interface);
}
else {
AchievementDisplayCooldown = fmax(0, AchievementDisplayCooldown);
}
CurrentAchHandler.AchDefs[CurrentAchIndex].bUnlockedJustNow = false;
ReceiveLocalizedMessage(Class'ScrnAchievementEarnedMsg',CurrentAchIndex,self.PlayerReplicationInfo,,CurrentAchHandler);
}
}
//inform client that he earned an achievement
simulated function DisplayAchievementStatus(ScrnAchievements AchHandler, int AchIndex)
{
if ( Viewport(Player) == None || AchHandler == none || !AchHandler.IsAchievementIndexValid(AchIndex) )
return;
if ( AchHandler.AchDefs[AchIndex].CurrentProgress > 0 ) {
if ( AchHandler.AchDefs[AchIndex].bUnlockedJustNow )
ConsoleMessage(Class'ScrnAchievementEarnedMsg'.default.EarnedString $ ": " $ AchHandler.AchDefs[AchIndex].DisplayName, 0, 1, 200, 1);
if ( PendingAchievements.Length == 0 && AchievementDisplayCooldown <= 0 ) {
// no need to use a queue for a single achievement
CurrentAchHandler = AchHandler;
CurrentAchIndex = AchIndex;
DisplayCurrentAchievement();
}
else if ( AchHandler.AchDefs[AchIndex].bUnlockedJustNow ){
//don't bother player with status updates, when other achievements have been really earned
PendingAchievements.insert(PendingAchievements.Length, 1);
PendingAchievements[PendingAchievements.Length-1].AchHandler = AchHandler;
PendingAchievements[PendingAchievements.Length-1].AchIndex = AchIndex;
}
}
}
static final function string ConsoleColorString(string s, byte R, byte G, byte B)
{
return chr(27)$chr(max(R,1))$chr(max(G,1))$chr(max(B,1))$s;
}
simulated function ConsoleMessage(string Msg, optional float MsgLife, optional byte R, optional byte G, optional byte B)
{
if ( Player != none && Player.Console != none ) {
if ( R != 0 || G != 0 || B != 0 )
Msg = ConsoleColorString(Msg, R, G, B);
Player.Console.Message(Msg, MsgLife);
}
}
simulated function string MyColoredName()
{
return Mut.ColoredPlayerName(PlayerReplicationInfo);
}
event ClientMessage( coerce string S, optional Name Type )
{
if ( Level.NetMode == NM_DedicatedServer || GameReplicationInfo == None )
return;
if ( Type == 'Log' ) {
log(S, 'ScrnBalance');
Type = '';
}
if ( Mut != none )
super.ClientMessage(Mut.ParseColorTags(S, PlayerReplicationInfo), Type);
else
super.ClientMessage(S, Type);
}
function LongMessage(string S, optional int MaxLen, optional string Divider)
{
class'ScrnBalance'.static.LongMessage(self, S, MaxLen, Divider);
}
event TeamMessage( PlayerReplicationInfo PRI, coerce string S, name Type )
{
local string c;
// Wait for player to be up to date with replication when joining a server, before stacking up messages
if ( Level.NetMode == NM_DedicatedServer || GameReplicationInfo == None )
return;
if( AllowTextToSpeech(PRI, Type) )
TextToSpeech( S, TextToSpeechVoiceVolume );
if ( Type == 'TeamSayQuiet' )
Type = 'TeamSay';
if ( myHUD != None )
myHUD.Message( PRI, c$S, Type );
if ( (Player != None) && (Player.Console != None) )
{
if ( PRI!=None )
{
if ( PRI.Team!=None && GameReplicationInfo.bTeamGame)
{
if (PRI.Team.TeamIndex==0)
c = chr(27)$chr(200)$chr(1)$chr(1);
else if (PRI.Team.TeamIndex==1)
c = chr(27)$chr(75)$chr(139)$chr(198);
}
S = Mut.ColoredPlayerName(PRI)$c$": "$ Mut.ParseColorTags(S, PRI);
}
Player.Console.Chat( c$s, 6.0, PRI );
}
}
simulated function ReceiveLocalizedMessage( class<LocalMessage> Message, optional int Switch, optional PlayerReplicationInfo RelatedPRI_1, optional PlayerReplicationInfo RelatedPRI_2, optional Object OptionalObject )
{
if ( Message == class'KFVetEarnedMessageSR' || Message == class'ScrnPromotedMessage'
|| (Message == class'KFVetEarnedMessagePL' && RelatedPRI_1 == PlayerReplicationInfo) ) {
Message = class'ScrnPromotedMessage';
}
else if ( Message == class'TSCMessages' ) {
switch (Switch) {
case 300: // trying to shop in enemy trader
ClientCloseMenu(true, true);
break;
case 311: // loosing base means wipe
if ( ScrnHUD(myHUD) != none )
ScrnHUD(myHUD).CriticalOverlayTimer = Level.TimeSeconds + 3.0;
break;
}
}
super.ReceiveLocalizedMessage(Message, Switch, RelatedPRI_1, RelatedPRI_2, OptionalObject);
}
function ResetWaveStats()
{
local KFPlayerReplicationInfo KFPRI;
KFPRI = KFPlayerReplicationInfo(PlayerReplicationInfo);
bCowboyForWave = true;
BeggingForMoney = 0;
bShoppedThisWave = false;
bHadArmor = bHadArmor || (KFPRI != none && KFPRI.ClientVeteranSkill != none
&& KFPRI.ClientVeteranSkill.static.ReduceDamage(KFPRI, KFPawn(Pawn), none, 100, none) < 100);
if ( ScrnHumanPawn(Pawn) != none )
ScrnHumanPawn(Pawn).ApplyWeaponStats(Pawn.Weapon);
}
exec function LogAch()
{
local ClientPerkRepLink L;
local SRCustomProgress S;
local ScrnAchievements A;
local int i, c;
L = class'ScrnClientPerkRepLink'.static.FindMe(self);
log("#;GROUP;ID;TITLE;DESCRIPTION", 'Achievement');
log("============================", 'Achievement');
for( S = L.CustomLink; S != none; S = S.NextLink ) {
A = ScrnAchievements(S);
if( A != none ) {
//log(A.class, 'Package');
for ( i = 0; i < A.AchDefs.length; ++i ) {
log(string(++c)
$";"$ A.AchDefs[i].Group
$";"$ A.AchDefs[i].ID
$";"$ A.AchDefs[i].DisplayName
$";"$ A.AchDefs[i].Description
, 'Achievement');
}
}
}
ConsoleMessage("Achievement list has been printed to the log file.", 0, 1, 200, 1);
}
exec function AchReset(optional int password)
{
if ( class'ScrnAchievements'.static.IsAchievementUnlocked(class'ScrnClientPerkRepLink'.static.FindMe(self), 'AchReset') ) {
if ( AchResetPassword == 0 ) {
AchResetPassword = 10000 + rand(90000);
ConsoleMessage("You already have 'AchReset' achievement!", 0, 200, 100, 1);
ConsoleMessage("If sure you want to do this again, then enter the following command:", 0, 200, 100, 1);
ConsoleMessage("AchReset " $ AchResetPassword, 0, 200, 200, 1);
return;
}
else if ( AchResetPassword != password ) {
ConsoleMessage("Wrong reset password!", 0, 200, 100, 1);
return;
}
}
SrvAchReset();
}
function SrvAchReset()
{
local ClientPerkRepLink L;
local array<name> GroupNames;
local array<string> GroupCaptions;
local int i;
L = Class'ScrnClientPerkRepLink'.Static.FindMe(self);
class'ScrnAchievements'.static.RetrieveGroups(L, GroupNames, GroupCaptions);
for ( i=0; i<GroupNames.length; ++i ) {
if ( GroupNames[i] != 'MAP' )
class'ScrnAchievements'.static.ResetAchievements(L, GroupNames[i]);
}
class'ScrnAchievements'.static.ProgressAchievementByID(L, 'AchReset', 1);
}
exec function ResetMyAchievements(name group)
{
class'ScrnAchievements'.static.ResetAchievements(Class'ScrnClientPerkRepLink'.Static.FindMe(self), group);
}
exec function ResetMapAch(string MapName)
{
local ClientPerkRepLink L;
local SRCustomProgress S;
local ScrnMapAchievements A;
local int i, j;
L = Class'ScrnClientPerkRepLink'.Static.FindMe(self);
if ( MapName == "" )
MapName = Mut.KF.GetCurrentMapName(Level);
if ( L == none || MapName == "" )
return;
for( S = L.CustomLink; S != none; S = S.NextLink ) {
A = ScrnMapAchievements(S);
if( A != none ) {
for ( i = 0; i < A.AchDefs.length; i += 4 ) {
if ( String(A.AchDefs[i].ID) ~= MapName ) {
for ( j=0; j < 4; ++j ) {
A.AchDefs[i+j].bUnlockedJustNow = false;
A.AchDefs[i+j].CurrentProgress = 0;
}
ConsoleMessage("Map achievements locked");
return;
}
}
}
}
}
exec function MVote(string VoteString)
{
ServerMutate("VOTE " $ VoteString);
}
exec function MVB(string Params)
{
ServerMutate("VOTE BLAME " $ Params);
}
exec function Say(string Msg)
{
// Msg = Level.Game.StripColor(Msg);
if (Msg ~= "!yes")
ServerMutate("VOTE YES");
else if (Msg ~= "!no")
ServerMutate("VOTE NO");
else
super.Say(Msg);
}
exec function TeamSay( string Msg )
{
//Msg = Level.Game.StripColor(Msg);
if (Msg ~= "!yes")
ServerMutate("VOTE YES");
else if (Msg ~= "!no")
ServerMutate("VOTE NO");
else
super.TeamSay(Msg);
}
function ServerSpeech( name Type, int Index, string Callsign )
{
if ( bSpeechVote && Type == 'ACK' ) {
if ( index == 0 )
ServerMutate("VOTE TRYYES");
else if ( index == 1 )
ServerMutate("VOTE TRYNO");
}
if ( Type == 'SUPPORT' && Index == 2 ) {
BeggingForMoney++;
}
super.ServerSpeech(Type, Index, Callsign);
}
exec function AmIMedic()
{
if ( ScrnHumanPawn(Pawn) != none )
ConsoleMessage(String(ScrnHumanPawn(Pawn).IsMedic()));
}
// overrided to call vote for pause in unable to pause directly
function bool SetPause( BOOL bPause )
{
bFire = 0;
bAltFire = 0;
if ( !Level.Game.SetPause(bPause, self) ) {
ServerMutate("VOTE PAUSE 120");
return false;
}
return true;
}
function BecomeSpectator()
{
local bool bWasOnlySpectator;
bWasOnlySpectator = PlayerReplicationInfo.bOnlySpectator;
super.BecomeSpectator();
if (Role < ROLE_Authority)
return;
if ( PlayerReplicationInfo.bOnlySpectator && !bWasOnlySpectator )
Mut.GameRules.PlayerLeaving(self);
if ( Mut.bDynamicLevelCap && Mut.KF.bTradingDoorsOpen )
Mut.DynamicLevelCap();
}
function BecomeActivePlayer()
{
local bool bWasOnlySpectator;
bWasOnlySpectator = PlayerReplicationInfo.bOnlySpectator;
super.BecomeActivePlayer();
if (Role < ROLE_Authority) {
if ( ScrnHUD(MyHUD) != none ) {
ScrnHUD(MyHUD).DisableHudHacks();
}
return;
}
if ( !PlayerReplicationInfo.bOnlySpectator ) {
if ( Mut.KF.bTradingDoorsOpen ) {
if ( Mut.bDynamicLevelCap )
Mut.DynamicLevelCap();
}
StartCash = PlayerReplicationInfo.Score;
if ( bWasOnlySpectator )
Mut.GameRules.PlayerEntering(self);
}
}
function Possess(Pawn aPawn)
{
local Rotator R, R2Door;
local KFTraderDoor door, BestDoor;
local ShopVolume shop;
local bool bTurnAroundValid;
//ClientMessage("Possess("$aPawn$"). Current Pawn="$Pawn);
if ( Role == ROLE_Authority && Mut != none ) {
if ( bHadPawn )
StartCash = max(StartCash, Mut.KF.MinRespawnCash);
else
StartCash = max(StartCash, Mut.KF.StartingCash);
if ( Mut.ScrnGT != none && Mut.ScrnGT.ScrnGameLength != none && Mut.ScrnGT.ScrnGameLength.Wave.bStartAtTrader )
{
R = aPawn.Rotation;
// spawnned at the trader on a trader teleporter.
// Releporters usually are facing away from the trader, so turn 180
R.yaw += 32768;
R = Normalize(R);
shop = Mut.ScrnGT.TeamShop(PlayerReplicationInfo.Team.TeamIndex);
foreach aPawn.VisibleCollidingActors(class'KFTraderDoor', door, 1000, aPawn.Location) {
if ( door.Tag == shop.Event ) {
if ( BestDoor == none )
BestDoor = door;
R2Door = R;
R2Door.yaw = rotator(door.Location - aPawn.Location).yaw;
if ( abs(Normalize(R2Door - R).yaw) < 4096 ) {
// rotation to the door is almost the same as rotation by 180.
// In that case use turnaround as it looks better
bTurnAroundValid = true;
}
break;
}
}
if ( !bTurnAroundValid && BestDoor != none ) {
// turnaround doesn't face any of trader doors. Rotate to door directly
R.yaw = rotator(door.Location - aPawn.Location).yaw;
}
aPawn.SetRotation(R);
}
}
super.Possess(aPawn);
// show path to trader if respawned during the trader time
if ( Role == ROLE_Authority && Pawn != none && Mut != none && Mut.KF.bTradingDoorsOpen )
SetShowPathToTrader(true);
bHadPawn = bHadPawn || Pawn != none;
}
simulated event Destroyed()
{
bDestroying = true;
if ( ScrnHumanPawn(Pawn) != none )
ScrnHumanPawn(Pawn).HealthBeforeDeath = Pawn.Health;
if ( Level.NetMode != NM_DedicatedServer && Level.GetLocalPlayerController() == self )
RemoveVotingMsg(); // delete voting interactions
if ( Role == ROLE_Authority ) {
if ( Mut != none && PlayerReplicationInfo != none ) {
if ( Mut.bLeaveCashOnDisconnect && PlayerReplicationInfo.Team != none
&& PlayerReplicationInfo.Score > StartCash )
{
PlayerReplicationInfo.Team.Score += PlayerReplicationInfo.Score - StartCash;
PlayerReplicationInfo.Score = StartCash; // just in case
}
if ( Mut.GameRules != none )
Mut.GameRules.PlayerLeaving(self);
}
}
super.Destroyed();
if ( Role == ROLE_Authority ) {
if ( Mut != none && Mut.bDynamicLevelCap && Mut.KF.bTradingDoorsOpen )
Mut.DynamicLevelCap();
}
}
simulated function ClientEnterZedTime()
{
super.ClientEnterZedTime();
bZEDTimeActive = true;
}
simulated function ClientExitZedTime()
{
super.ClientExitZedTime();
bZEDTimeActive = false;
}
function bool AllowVoiceMessage(name MessageType)
{
local float TimeSinceLastMsg;
if ( Level.NetMode == NM_Standalone || (PlayerReplicationInfo != none && PlayerReplicationInfo.bAdmin) )
return true;
TimeSinceLastMsg = Level.TimeSeconds - OldMessageTime;
if ( TimeSinceLastMsg < 3 )
{
if ( (MessageType == 'TAUNT') || (MessageType == 'AUTOTAUNT') )
return false;
if ( TimeSinceLastMsg < 1 )
return false;
}
// zed time screws up voice messages
if ( !bZEDTimeActive && MessageType != 'TRADER' && MessageType != 'AUTO' ) {
OldMessageTime = Level.TimeSeconds;
if ( TimeSinceLastMsg < 10 ) {
if ( MaxVoiceMsgIn10s > 0 )
MaxVoiceMsgIn10s--;
else {
ClientMessage("Keep quiet for " $ ceil(10-TimeSinceLastMsg) $"s");
return false;
}
}
else
MaxVoiceMsgIn10s = default.MaxVoiceMsgIn10s;
}
return true;
}
exec function RemoveVotingMsg()
{
local VHInteraction RemoveMe;
foreach AllObjects(class'VHInteraction', RemoveMe) {
RemoveMe.Master.RemoveInteraction(RemoveMe);
}
}
exec function DropAllWeapons()
{
ServerDropAllWeapons();
}
function ServerDropAllWeapons()
{
class'ScrnHumanPawn'.static.DropAllWeapons(Pawn);
}
exec function LockWeapons()
{
if ( Mut.bAllowWeaponLock ) {
bWeaponsLocked = true;
ServerLockWeapons(bWeaponsLocked);
ClientMessage(ConsoleColorString(strLocked, 192, 1, 1));
}
else
ClientMessage(strLockDisabled);
}
exec function UnlockWeapons()
{
if ( Mut.bAllowWeaponLock ) {
bWeaponsLocked = false;
ServerLockWeapons(bWeaponsLocked);
ClientMessage(ConsoleColorString(strUnlocked, 1, 192, 1));
}
else
ClientMessage(strLockDisabled);
}
exec function ToggleWeaponLock()
{
if ( bWeaponsLocked )
UnlockWeapons();
else
LockWeapons();
}
function ServerLockWeapons(bool bLock)
{
bWeaponsLocked = Mut.bAllowWeaponLock && bLock;
}
exec function Ready()
{
if ( PlayerReplicationInfo.bOnlySpectator )
BecomeActivePlayer();
if ( PlayerReplicationInfo.Team == none || Level.GRI.bMatchHasBegun )
return;
if ( Level.NetMode == NM_Standalone || !PlayerReplicationInfo.bReadyToPlay ) {
SendSelectedVeterancyToServer(true);
//Set Ready
ServerRestartPlayer();
PlayerReplicationInfo.bReadyToPlay = True;
if ( Level.GRI.bMatchHasBegun )
ClientCloseMenu(true, false);
}
}
exec function Spectate()
{
if ( PlayerReplicationInfo.bOnlySpectator ) {
ConsoleMessage(strAlreadySpectating);
return;
}
ClientCloseMenu(true, false);
BecomeSpectator();
}
exec function Unready()
{
if ( Level.GRI.bMatchHasBegun )
return;
ServerUnreadyPlayer();
PlayerReplicationInfo.bReadyToPlay = False;
}
exec function ShowMenu()
{
if ( Level.GRI.bMatchHasBegun || PlayerReplicationInfo.bOnlySpectator )
super.ShowMenu();
else
ShowLobbyMenu();
}
function ShowLobbyMenu()
{
StopForceFeedback(); // jdf - no way to pause feedback
bPendingLobbyDisplay = false;
// Open menu
if ( TSCGameReplicationInfo(Level.GRI) != none && !TSCGameReplicationInfo(Level.GRI).bSingleTeamGame )
ClientOpenMenu(TSCLobbyMenuClassString);
else
ClientOpenMenu(LobbyMenuClassString);
}
function LoadDualWieldables()
{
local ScrnClientPerkRepLink L;
local class<KFWeaponPickup> WP;
local class<KFWeapon> W;
local int i;
L = class'ScrnClientPerkRepLink'.static.FindMe(self);
if( L==None || L.ShopInventory.Length == 0 )
return; // Hmmmm?
for ( i=0; i<L.ShopInventory.Length; ++i ) {
WP = class<KFWeaponPickup>(L.ShopInventory[i].PC);
if ( WP == none )
continue;
W = class<KFWeapon>(WP.default.InventoryType);
if ( W != none && W.Default.DemoReplacement != none )
AddDualWieldable(class<KFWeapon>(W.Default.DemoReplacement), W);
}
bDualWieldablesLoaded = L.PendingWeapons == 0;
}
final function AddDualWieldable(class<KFWeapon> SingleWeapon, class<KFWeapon> DualWeapon)
{
local int i;
if ( SingleWeapon == none || DualWeapon == none )
return;
for ( i=0; i<DualWieldables.Length; ++i ) {
if ( DualWieldables[i].Single == SingleWeapon || DualWieldables[i].Dual == DualWeapon)
break;
}
if ( i == DualWieldables.Length )
DualWieldables.insert(i, 1);
DualWieldables[i].Single = SingleWeapon;
DualWieldables[i].Dual = DualWeapon;
}
function bool IsSinglePistol(class<KFWeapon> Weapon)
{
local int i;
if ( !bDualWieldablesLoaded )
LoadDualWieldables();
if ( Weapon == none )
return false;
for ( i=0; i<DualWieldables.Length; ++i ) {
if ( DualWieldables[i].Single == Weapon )
return true;
}
return false;
}
function bool AreDualPistols(class<KFWeapon> Weapon)
{
local int i;
if ( !bDualWieldablesLoaded )
LoadDualWieldables();
if ( Weapon == none )
return false;
for ( i=0; i<DualWieldables.Length; ++i ) {
if ( DualWieldables[i].Dual == Weapon )
return true;
}
return false;
}
// if pickup is in inventory, then always returns true, even if bCheckForEquivalent = false,
// that way fixing picking up of the same weapon -- PooSH
function bool IsInInventory(class<Pickup> PickupToCheck, bool bCheckForEquivalent, bool bCheckForVariant)
{
local Inventory CurInv;
local class<KFWeaponPickup> InvPickupClass;
local class<KFWeaponPickup> WeaponPickupToCheck;
local int i;
if ( PickupToCheck == none )
return false;
WeaponPickupToCheck = class<KFWeaponPickup>(PickupToCheck);
for ( CurInv = Pawn.Inventory; CurInv != none; CurInv = CurInv.Inventory ) {
if ( /* bCheckForEquivalent && */ CurInv.default.PickupClass == PickupToCheck ) {
if ( !bCheckForEquivalent && IsSinglePistol(class<KFWeapon>(CurInv.class)) )
return false; // allow picking up a second pistol
return true;
}
if( !bCheckForVariant )
continue;
// check if Item is variant of normal inventory item
InvPickupClass = class<KFWeaponPickup>(CurInv.default.PickupClass);
if( InvPickupClass != none ) {
for( i = 0; i < InvPickupClass.default.VariantClasses.Length; ++i ) {
if( InvPickupClass.default.VariantClasses[i] == PickupToCheck )
return true;
}
}
// check if Item is normal version of variant inventory item
if( WeaponPickupToCheck != none && CurInv.default.PickupClass != none ) {
for( i = 0; i < WeaponPickupToCheck.default.VariantClasses.Length; ++i ) {
if( WeaponPickupToCheck.default.VariantClasses[i] == CurInv.default.PickupClass )
return true;
}
}
}
return false;
}
exec function SetName(coerce string S)
{
if ( S == "#" )
S = PlayerName;
if ( S == "" || (class'ScrnSrvReplInfo'.static.Instance().bForceSteamNames
&& Player.GUIController.SteamGetUserName() != Mut.StripColorTags(S)) )
{
S = Player.GUIController.SteamGetUserName();
if ( S == "" )
PlayerName = "";
}
else {
PlayerName = S;
}
ChangeName(S);
UpdateURL("Name", S, true);
SaveConfig();
}
function ChangeName( coerce string S )
{
local string PlainName;
ReplaceText(S, " ", "_");
ReplaceText(S, "\"", "");
PlainName = Mut.StripColorTags(S);
if ( len(PlainName) > 20 )
S = left(PlainName, 20);
Level.Game.ChangeName( self, S, true );
}
// TSC stuff
exec function SwitchTeam()
{
if ( PlayerReplicationInfo == none || PlayerReplicationInfo.Team == None || PlayerReplicationInfo.Team.TeamIndex == 1 )
ServerChangeTeam(0);
else
ServerChangeTeam(1);
}
exec function ChangeTeam( int N )
{
ServerChangeTeam(N);
}
function ServerChangeTeam( int N )
{
super.ServerChangeTeam(N);
if ( Mut.bDynamicLevelCap && Mut.KF.bTradingDoorsOpen )
Mut.DynamicLevelCap();
}
exec function SetupBase()
{
ServerScoreFlag();
}
function ServerScoreFlag()
{
local GameObject Flag;
Flag = GameObject(PlayerReplicationInfo.HasFlag);
if ( Flag != none ) {
if ( Flag.bHeld )
Flag.Score();
else
Flag.ClearHolder(); // shouldn't happen
}
}
exec function AltFire( optional float F )
{
if ( Level.Pauser == PlayerReplicationInfo )
{
SetPause(false);
return;
}
if( bDemoOwner || (Pawn == None) )
return;
if ( !bTSCAdvancedKeyBindings && PlayerReplicationInfo.HasFlag != none
&& GameObject(PlayerReplicationInfo.HasFlag) != none
&& TSCGameReplicationInfo(Level.GRI) != none )
{
if ( bTSCAutoDetectKeys )
bTSCAdvancedKeyBindings = ConsoleCommand("BINDINGTOKEY SetupBase") != "";
if ( !bTSCAdvancedKeyBindings ) {
SetupBase();
return;
}
}
super.AltFire(F);
}
exec function ThrowWeapon()
{
if ( !bTSCAdvancedKeyBindings && GameObject(PlayerReplicationInfo.HasFlag) != none ) {
if ( bTSCAutoDetectKeys )
bTSCAdvancedKeyBindings = ConsoleCommand("BINDINGTOKEY DropFlag") != "";
if ( !bTSCAdvancedKeyBindings ) {
DropFlag();
return;
}
}
ServerThrowWeapon();
}
// character selection
simulated function bool IsTeamCharacter(string CharacterName)
{
if ( CharacterName == "" )
return false;
if ( Level.GRI.bNoTeamSkins || TSCGameReplicationInfo(Level.GRI) == none || PlayerReplicationInfo == none || PlayerReplicationInfo.Team == none )
return true; // no team = any character can be used
if ( PlayerReplicationInfo.Team.TeamIndex == 0 )
return IsRedCharacter(CharacterName);
else if ( PlayerReplicationInfo.Team.TeamIndex == 1 )
return IsBlueCharacter(CharacterName);
return false;
}
simulated function bool IsRedCharacter(string CharacterName)
{
local int i;
if ( CharacterName == "" )
return false;
for ( i=0; i<RedCharacters.Length; ++i )
if ( RedCharacters[i] ~= CharacterName )
return true;
return false;
}
simulated function bool IsBlueCharacter(string CharacterName)
{
local int i;
if ( CharacterName == "" )
return false;
for ( i=0; i<BlueCharacters.Length; ++i )
if ( BlueCharacters[i] ~= CharacterName )
return true;
return false;
}
exec function ChangeCharacter(string newCharacter, optional string inClass)
{
local ClientPerkRepLink L;
if ( newCharacter == "" || !IsTeamCharacter(newCharacter) )
return;
L = class'ScrnClientPerkRepLink'.static.FindMe(self);
if( L!=None )
L.SelectedCharacter(newCharacter);
else
Super(KFPlayerController_Story).ChangeCharacter(newCharacter,inClass);
}
function SetPawnClass(string inClass, string inCharacter)
{
ValidateCharacter(inCharacter);
super.SetPawnClass(inClass, inCharacter);
}
/**
* Checks if character can be used on this player.
* @param CharacterName [in] name of the character to validate (xUtil.PlayerRecord.DefaultName)
* [out] name of the valid character
* @return True, if given character can be used. CharacterName stays unchanged.
* False, if given character is invalid. CharacterName changed to valid character.
*/
simulated function bool ValidateCharacter(out string CharacterName)
{
if ( IsTeamCharacter(CharacterName) )
return true;
if ( PlayerReplicationInfo.Team.TeamIndex == 0 ) {
if ( RedCharacter != "" && IsRedCharacter(RedCharacter) )
CharacterName = RedCharacter; // only on client side
else
CharacterName = RedCharacters[0];
}
else if ( PlayerReplicationInfo.Team.TeamIndex == 1 ) {
if ( BlueCharacter != "" && IsBlueCharacter(BlueCharacter) )
CharacterName = BlueCharacter; // only on client side
else
CharacterName = BlueCharacters[0];
}
return false;
}
function SendSelectedVeterancyToServer(optional bool bForceChange)
{
local class<KFVeterancyTypes> OldPerk;
if( Level.NetMode!=NM_Client && SRStatsBase(SteamStatsAndAchievements)!=none ) {
OldPerk = KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill;
SRStatsBase(SteamStatsAndAchievements).WaveEnded();
if ( OldPerk != KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill )
PerkChangeWave = KFGameType(Level.Game).WaveNum;
}
}
function SelectVeterancy(class<KFVeterancyTypes> VetSkill, optional bool bForceChange)
{
local ClientPerkRepLink L;
if( SRStatsBase(SteamStatsAndAchievements)!=none )
L = Class'ScrnClientPerkRepLink'.Static.FindMe(self);
if ( L != none ) {
if ( ScrnClientPerkRepLink(L) != none )
ScrnClientPerkRepLink(L).ServerSelectPerkSE(Class<ScrnVeterancyTypes>(VetSkill));
else
L.ServerSelectPerk(Class<SRVeterancyTypes>(VetSkill));
}
}
exec final function TourneyCheck()
{
ServerTourneyCheck();
}
final function ServerTourneyCheck()
{
local ScrnGameType GT;
local Mutator M;
local GameRules G;
local string s;
ClientMessage(string(Level.Game.class));
GT = ScrnGameType(Level.Game);
if ( GT == none )
ClientMessage("Current game type doesn't support Tourney Mode");
else {
if ( GT.IsTourney() )
ClientMessage("Tourney Mode: " $ GT.GetTourneyMode());
else
ClientMessage("Tourney Mode DISABLED");
}
s = "Mutators:";
for ( M = Level.Game.BaseMutator; M != None; M = M.NextMutator )
s @= string(M.class);
LongMessage(s, 80, " ");
s = "Rules:";
for ( G=Level.Game.GameRulesModifiers; G!=None; G=G.NextGameRules )
s @= string(G.class);
LongMessage(s, 80, " ");
}
exec final function AdminKillMut(string MutName)
{
if ( Level.NetMode == NM_Standalone || (PlayerReplicationInfo != none && PlayerReplicationInfo.bAdmin) )
ServerKillMut(MutName);
}
final function ServerKillMut(string MutatorName)
{
local Mutator M;
local ScrnBalance SBM;
if ( Level.NetMode != NM_Standalone && (PlayerReplicationInfo == none || !PlayerReplicationInfo.bAdmin) )
return;
if ( MutatorName ~= "KillingFloorMut" || MutatorName ~= "ScrnBalance" || MutatorName ~= "ServerPerksMut" )
return;
SBM = class'ScrnBalance'.static.Myself(Level);
if ( SBM == none || GetItemName(string(SBM.class)) ~= MutatorName
|| GetItemName(string(SBM.FindServerPerksMut().class)) ~= MutatorName )
return; // can't kill ScrnBalance or ServerPerks
for ( M = Level.Game.BaseMutator; M != None; M = M.NextMutator ) {
if ( GetItemName(string(M.class)) ~= MutatorName ) {
ClientMessage("Mutator '"$string(M.Class)$"' destroyed");
M.Destroy();
return;
}
}
}
exec final function AdminKillRules(string RuleName)
{
if ( Level.NetMode == NM_Standalone || (PlayerReplicationInfo != none && PlayerReplicationInfo.bAdmin) )
ServerKillRules(RuleName);
}
final function ServerKillRules(string RuleName)
{
local GameRules G, PrevG;
if ( Level.NetMode != NM_Standalone && (PlayerReplicationInfo == none || !PlayerReplicationInfo.bAdmin) )
return;
if ( RuleName ~= "SRGameRules" || RuleName ~= "ScrnGameRules" )
return; // can't kill ScrnGameRules
PrevG = Level.Game.GameRulesModifiers;
if ( GetItemName(string(PrevG.class)) ~= RuleName ) {
ClientMessage("Game Rules '"$string(PrevG.Class)$"' destroyed");
Level.Game.GameRulesModifiers = PrevG.NextGameRules;
PrevG.Destroy();
}
else {
for ( G=PrevG.NextGameRules; G!=None; PrevG=G ) {
if ( GetItemName(string(G.class)) ~= RuleName ) {
PrevG.NextGameRules = G.NextGameRules;
ClientMessage("Game Rules '"$string(G.Class)$"' destroyed");
G.Destroy();
return;
}
}
}
}
function CheckDLC()
{
local ScrnSteamStatsGetter A;
foreach AllActors(class'ScrnSteamStatsGetter', A)
return; // do not allow duplicates
spawn(class'ScrnSteamStatsGetter',self);
}
// returns true if players owns DLC
function bool OwnsDLC(int AppID)
{
local int i;
local bool bNeedCheck;
for ( i=0; i<DLC.length; ++i ) {
bNeedCheck = bNeedCheck || !DLC[i].bChecked;
if ( DLC[i].AppID == AppID)
break;
}
if ( i == DLC.length ) {
DLC.insert(i, 1);
DLC[i].AppID = AppID;
}
if ( bNeedCheck )
CheckDLC(); // this doesn't happen immediately, so function returns false in the first try
return DLC[i].bOwnsDLC;
}
/**
* Sets gun skin according to index in pickup's VariantClasses[]. Check DLC locks.
*
* @param index Skin index:
* 0 - switch to next skin
* 1 - switch to default skin
* 2+ switch to skin defined in Weapon.PickupClass.VariantClasses array.
* 2 - first item in array (VariantClasses[0]), 3 - second (VariantClasses[1]) etc.
* @param bTryNext If this skin is locked (DLC), then should we try selecting next skin?
*/
exec function GunSkin(optional byte index, optional bool bTryNext)
{
local KFWeapon W;
local class<KFWeaponPickup> P;
local int i;
local class<KFWeapon> SkinnedWeaponClass;
local byte k;
if ( Pawn == none || Pawn.Weapon == none )
return;
W = KFWeapon(Pawn.Weapon);
P = class<KFWeaponPickup>(Pawn.Weapon.PickupClass);
if ( W == none || P == none || P.default.VariantClasses.Length == 0 )
return;
if ( W.SleeveNum == 0 )
k = 1; // for weapons where hand skin is first in array
if ( k >= W.Skins.length ) {
warn(W.class $ " has no skins");
return;
}
if ( index == 0 ) {
bTryNext = true;
// find out current skin
if ( W.Skins[k] == W.default.Skins[k] )
index = 1; // default skin
else {
for ( i=0; i<P.default.VariantClasses.Length; ++i ) {
if ( P.default.VariantClasses[i] == none ) {
//bugfix
P.default.VariantClasses.remove(i--,1);
continue;
}
if ( W.Skins[k] == P.default.VariantClasses[i].default.InventoryType.default.Skins[k] )
{
index = i + 2;
break;
}
}
}
if ( P.default.VariantClasses.Length == 0 )
return; // bugfix, when VariantClasses have "none" elements
index++;
}
if ( index >= P.default.VariantClasses.Length+2 )
index = 1;
if ( index == 1 ) {
// load default skin
SkinnedWeaponClass = W.class;
}
else {
// load skin from variant class
SkinnedWeaponClass = class<KFWeapon>(P.default.VariantClasses[index-2].default.InventoryType);
// DLC check
if ( SkinnedWeaponClass == none || (!Mut.bBeta && SkinnedWeaponClass.default.AppID > 0
&& !OwnsDLC(SkinnedWeaponClass.default.AppID)) )
{
// this skin is locked
if ( bTryNext )
GunSkin(index+1, true); // try next one
return;
}
}
if ( SkinnedWeaponClass == none )
return;
ClientWeaponSpawned(SkinnedWeaponClass, none); // preload assets
SkinnedWeaponClass.static.PreloadAssets(W);
W.HandleSleeveSwapping(); // restore proper arms
ServerGunSkin(SkinnedWeaponClass);
// save skin in user.ini
for ( i=0; i<WeaponSettings.length; ++i )
if ( W.class == WeaponSettings[i].Weapon )
break;
if ( i == WeaponSettings.length ) {
WeaponSettings.insert(i,1);
WeaponSettings[i].Weapon = W.class;
}
if ( WeaponSettings[i].SkinIndex != index ) {
WeaponSettings[i].SkinIndex = index;
WeaponSettings[i].LastWeapon = W;
SaveConfig();
}
}
// replicate attachment (3-rd person model) skin to other players
function ServerGunSkin(class<KFWeapon> SkinnedWeaponClass)
{
local Controller C;
local int i;
local KFWeapon W;
// preload assets
for ( C = Level.ControllerList; C != none; C = C.nextController ) {
if ( C != self && KFPlayerController(C) != none )
KFPlayerController(C).ClientWeaponSpawned(SkinnedWeaponClass, none);
}
if ( Pawn != none )
W = KFWeapon(Pawn.Weapon);
if ( W != none && W.AttachmentClass != SkinnedWeaponClass.default.AttachmentClass ) {
W.ThirdPersonActor.Destroy();
W.ThirdPersonActor = none;
W.AttachmentClass = SkinnedWeaponClass.default.AttachmentClass;
W.AttachToPawn(Pawn);
//remember weapon choise
for ( i=0; i<WeaponSettings.length; ++i )
if ( W.class == WeaponSettings[i].Weapon )
break;
if ( i == WeaponSettings.length ) {
WeaponSettings.insert(i,1);
WeaponSettings[i].Weapon = W.class;
}
WeaponSettings[i].SkinnedWeapon = SkinnedWeaponClass;
}
}
function LoadGunSkinFromConfig()
{
local int i;
if ( Pawn == none || Pawn.Weapon == none )
return;
for ( i=0; i<WeaponSettings.length; ++i ) {
if ( Pawn.Weapon.class == WeaponSettings[i].Weapon ) {
if ( WeaponSettings[i].LastWeapon != Pawn.Weapon )
GunSkin(WeaponSettings[i].SkinIndex);
break;
}
}
}
// overrided KFWeapon behavior to block reloading while putting down, bringing up or throwing the nade
exec function ReloadMeNow()
{
local KFWeapon W;
if ( Pawn != none )
W = KFWeapon(Pawn.Weapon);
if ( W == none )
return;
// ClientMessage( "Reloading Weapon: "
// @ "Timer=" $ W.TimerCounter$"/"$W.TimerRate
// @ "ClientState=" $ GetEnum(Enum'EWeaponClientState', W.ClientState)
// @ "ClientGrenadeState=" $ GetEnum(Enum'EClientGrenadeState', W.ClientGrenadeState)
// );
if ( W.ClientState == WS_ReadyToFire /* && W.ClientGrenadeState == GN_None */ )
W.ReloadMeNow();
}
exec function GetWeapon(class<Weapon> NewWeaponClass )
{
if ( ScrnHumanPawn(Pawn) != none && ScrnHumanPawn(Pawn).bQuickMeleeInProgress )
return;
super.GetWeapon(NewWeaponClass);
}
exec function NextWeapon()
{
if ( ScrnHumanPawn(Pawn) != none && ScrnHumanPawn(Pawn).bQuickMeleeInProgress )
return;
super.NextWeapon();
}
exec function PrevWeapon()
{
if ( ScrnHumanPawn(Pawn) != none && ScrnHumanPawn(Pawn).bQuickMeleeInProgress )
return;
super.PrevWeapon();
}
// STATES
state Dead
{
// fix of null-reference
function Timer()
{
if ( KFStoryGameInfo(Level.Game) != none )
super.Timer();
else
super(KFPlayerController).Timer(); // bypass KFPlayerController_Story
}
}
// view next player
function ServerViewNextPlayer()
{
local Controller C, Pick;
local bool bFound, bWasSpec;
if( !IsInState('Spectating') )
return;
if ( ScrnGameType(Level.Game) == none ) {
super.ServerViewNextPlayer();
ViewTargetChanged();;
return;
}
// copy-pasted to remove team hack
bWasSpec = !bBehindView && (ViewTarget != Pawn) && (ViewTarget != self);
for ( C=Level.ControllerList; C!=None; C=C.NextController ) {
if ( Level.Game.CanSpectate(self,PlayerReplicationInfo.bOnlySpectator,C) ) {
if ( Pick == None )
Pick = C;
if ( bFound ) {
Pick = C;
break;
}
else
bFound = RealViewTarget == C || ViewTarget == C;
}
}
if ( Pick == none )
Pick = self;
SetViewTarget(Pick);
ClientSetViewTarget(Pick);
if ( (ViewTarget == self) || bWasSpec )
bBehindView = false;
else
bBehindView = true; //bChaseCam;
ClientSetBehindView(bBehindView);
ViewTargetChanged();
}
function ServerViewSelf()
{
local Actor A;
local vector Loc;
local rotator R;
if ( !PlayerReplicationInfo.bOnlySpectator && ScrnGameType(Level.Game) != none
&& ScrnGameType(Level.Game).IsTourney() )
{
// free roaming is prohibited in tourney mode
ServerViewNextPlayer();
return;
}
if ( bBehindView ) {
PlayerCalcView(A, Loc, R);
}
else {
Loc = ViewTarget.Location;
R = ViewTarget.Rotation;
}
SetLocation(Loc);
SetRotation(R);
ClientSetLocation(Loc, R);
bBehindView = false;
SetViewTarget(self);
ClientSetViewTarget(self);
ClientMessage(OwnCamera, 'Event');
ViewTargetChanged();
}
// Modes:
// 1 - switch to next team
// 2 - switch to flag carrier
// 3 - cycle through the team
// 4 - assign favorite player
// 5 - cycle between 2 favorite players
// 6 - cycle between big zeds (base HP >= 1000)
// 7 - cycle between Fleshpounds and Patriarch
function ServerSwitchViewMode(byte Mode)
{
local Controller C, Pick, CurrentTarget;
local bool bFound;
local TeamInfo ViewingTeam;
if( !IsInState('Spectating') || !PlayerReplicationInfo.bOnlySpectator )
return;
if ( ScrnGameType(Level.Game) == none )
return;
CurrentTarget = Controller(ViewTarget);
if ( CurrentTarget == none ) {
if ( Pawn(ViewTarget) != none )
CurrentTarget = Pawn(ViewTarget).Controller;
else
CurrentTarget = RealViewTarget;
}
if ( CurrentTarget != none && CurrentTarget.PlayerReplicationInfo != none )
ViewingTeam = CurrentTarget.PlayerReplicationInfo.Team;
if ( Mode == 4 ) {
if ( CurrentTarget == none )
return;
FavoriteSpecs[1] = FavoriteSpecs[0];
FavoriteSpecs[0] = CurrentTarget;
ClientMessage(strSpecFavoriteAssigned);
return;
}
else if ( Mode == 5 ) {
if ( FavoriteSpecs[0] == none && FavoriteSpecs[1] == none ) {
ClientMessage(strSpecNoFavorites);
return;
}
if ( FavoriteSpecs[0] == CurrentTarget )
Pick = FavoriteSpecs[1];
else
Pick = FavoriteSpecs[0];
}
else if ( Mode == 6 || Mode == 7 ) {
// view next Fleshpound / Patriarch
for ( C=Level.ControllerList; C!=None; C=C.NextController ) {
if ( C.Pawn == none || C.Pawn.Health <= 0 || C.Pawn.default.Health < 1000 || KFMonsterController(C) == none )
continue;
if ( Mode == 7 && FleshpoundZombieController(C) == none && BossZombieController(C) == none )
continue;
if ( CurrentTarget == C )
bFound = true;
else if ( Pick == None || bFound ) {
Pick = C;
if ( bFound )
break;
}
}
}
else {
// view next player
for ( C=Level.ControllerList; C!=None; C=C.NextController ) {
if ( C.PlayerReplicationInfo == none )
continue;
if ( Level.Game.CanSpectate(self,true,C) ) {
if ( CurrentTarget == C )
bFound = true;
else if ( Pick == None || bFound ) {
if ( (Mode == 1 && ViewingTeam != C.PlayerReplicationInfo.Team)
|| (Mode == 2 && C.PlayerReplicationInfo.HasFlag != none)
|| (Mode == 3 && ViewingTeam == C.PlayerReplicationInfo.Team))
{
Pick = C;
if ( bFound || Mode == 1 )
break;
}
}
}
}
}
if ( Pick == none )
return;
ServerSetViewTarget(Pick);
}
function ServerSetViewTarget(Actor NewViewTarget)
{
local bool bWasSpec;
if ( !IsInState('Spectating') || (!PlayerReplicationInfo.bOnlySpectator && Mut.SrvTourneyMode > 0) )
return;
bWasSpec = !bBehindView && ViewTarget != Pawn && ViewTarget != self;
SetViewTarget(NewViewTarget);
ViewTargetChanged();
ClientSetViewTarget(NewViewTarget);
if ( ViewTarget == self || bWasSpec )
bBehindView = false;
else
bBehindView = true; //bChaseCam;
ClientSetBehindView(bBehindView);
}
function ViewTargetChanged()
{
local Controller C;
local ScrnHumanPawn ScrnVT;
//log("ViewTargetChanged("$OldViewTarget$")", 'ScrnBalance');
if ( Role < ROLE_Authority || (!PlayerReplicationInfo.bOnlySpectator && Mut.SrvTourneyMode > 0) )
return;
ScrnVT = ScrnHumanPawn(OldViewTarget);
if ( ScrnVT != none && ScrnVT != ViewTarget ) {
// check if somebody else is spectating our old target
for ( C=Level.ControllerList; C!=None; C=C.NextController ) {
if ( C.Pawn != ScrnVT && PlayerController(C) != none && PlayerController(C).ViewTarget == ScrnVT )
break;
}
ScrnVT.bViewTarget = C!=none;
}
ScrnVT = ScrnHumanPawn(ViewTarget);
if ( ScrnVT != none )
ScrnVT.bViewTarget = true; // tell pawn that we are watching him
OldViewTarget = ViewTarget;
}
function ClientSetBehindView(bool B)
{
super.ClientSetBehindView(B);
if ( PlayerReplicationInfo != none && PlayerReplicationInfo.bOnlySpectator ) {
// auto show crosshair in first person spectator mode
if ( ScrnHUD(MyHUD) != none )
ScrnHUD(MyHUD).DebugCrosshair(!bBehindView);
}
else if ( B == false ) {
bFreeCamera = false;
}
}
exec function FreeCamera( bool B )
{
ClientMessage("FreeCamera is blocked due to exploits. Use ToggleBehindView instead");
// free roaming is prohibited in tourney mode
// if ( B && !PlayerReplicationInfo.bOnlySpectator && Mut.SrvTourneyMode != 0 )
// return;
// super.FreeCamera(B);
}
state Spectating
{
// Return to spectator's own camera.
exec function AltFire( optional float F )
{
// free roaming is prohibited in tourney mode
if ( !PlayerReplicationInfo.bOnlySpectator && Mut.SrvTourneyMode != 0 )
Fire(F);
else
super.AltFire(F);
}
exec function SwitchWeapon(byte T)
{
if ( PlayerReplicationInfo.bOnlySpectator )
ServerSwitchViewMode(T);
}
exec function Use()
{
local vector HitLocation, HitNormal, TraceEnd, TraceStart;
local rotator R;
local Actor A;
PlayerCalcView(A, TraceStart, R);
TraceEnd = TraceStart + 1000 * Vector(R);
A = Trace(HitLocation, HitNormal, TraceEnd, TraceStart, true);
if ( Pawn(A) != none)
ServerSetViewTarget(A);
}
}
// ============================== DEBUG STUFF ==============================
// ========================== UNHARMFUL FUNCTIONS ==========================
exec function TestColorTags(coerce string ColorTagString, optional int i)
{
local string c, s;
c = class'ScrnBalance'.default.Mut.ParseColorTags(ColorTagString, PlayerReplicationInfo);
s = class'ScrnBalance'.default.Mut.StripColorTags(ColorTagString);
if ( i > 0) {
c = class'ScrnBalance'.static.LeftCol(c, i);
s = left(s, i);
}
ConsoleMessage(c);
ConsoleMessage(s);
}
exec function MyTeam()
{
if ( PlayerReplicationInfo == none )
ClientMessage("No PlayerReplicationInfo received yet");
else if ( PlayerReplicationInfo.Team == none )
ClientMessage("Team is not selected");
else
ClientMessage("My Team Index = " $ PlayerReplicationInfo.Team.TeamIndex
$ " ("$ PlayerReplicationInfo.Team.class$")");
}
function DebugRepLink(string S)
{
local ClientPerkRepLink L;
local ScrnClientPerkRepLink SL;
local int count;
if ( Role < ROLE_Authority ) {
foreach DynamicActors(Class'ClientPerkRepLink',L) {
++count;
}
if ( count != 1 ) {
if ( count == 0 )
ClientMessage(S @ "WARNING! No RepLink objects found!", 'Log');
else
ClientMessage(S @ "WARNING! Multiple RepLink objects found! ("$count$")", 'Log');
}
L = Class'ScrnClientPerkRepLink'.Static.FindMe(self);
ClientMessage(S @ "RepLink: " $ L, 'Log');
}
else {
L = SRStatsBase(SteamStatsAndAchievements).Rep;
ClientMessage(S @ "Stats: " $ SteamStatsAndAchievements, 'Log');
ClientMessage(S @ "Stats.Rep: " $ L, 'Log');
}
ClientMessage(S @ "Categories ACK/TOTAL: " $ L.ClientAccknowledged[1]$"/"$L.ShopCategories.Length, 'Log');
ClientMessage(S @ "Weapons ACK/TOTAL: " $ L.ClientAccknowledged[0]$"/"$L.ShopInventory.Length, 'Log');
ClientMessage(S @ "Characters ACK/TOTAL: " $ L.ClientAckSkinNum$"/"$L.CustomChars.Length, 'Log');
SL = ScrnClientPerkRepLink(L);
if ( SL != none ) {
ClientMessage(S @ "Job: " $ SL.CurrentJob, 'Log');
}
}
function ServerDebugRepLink()
{
if ( Role == ROLE_Authority )
DebugRepLink("Server");
}
exec function TestRepLink()
{
DebugRepLink("Client");
if ( Role < ROLE_Authority )
ServerDebugRepLink();
}
exec function RestartRepLink()
{
local ScrnClientPerkRepLink L;
if ( KFGameReplicationInfo(Level.GRI).EndGameType > 0 ) {
ClientMessage("Unable to restart replication when game ended.");
return;
}
L = Class'ScrnClientPerkRepLink'.Static.FindMe(self);
if ( L == none ) {
ClientMessage("ScrnClientPerkRepLink not found");
return;
}
bDebugRepLink = true;
L.StartClientInitialReplication();
}
exec function RepLinkMessages(bool value)
{
bDebugRepLink = value;
Class'ScrnClientPerkRepLink'.Static.FindMe(self).bClientDebug = bDebugRepLink;
}
exec function TestQuickMelee()
{
local Inventory inv;
local KFWeapon W;
local string s;
local int c;
if ( Pawn == none )
return;
for ( inv = Pawn.Inventory; inv != none && ++c < 1000 ; inv = inv.Inventory ) {
W = KFWeapon(inv);
if ( W != none ) {
if ( W == Pawn.Weapon )
s = "[CURRENT]";
else
s = "";
s @= W.ItemName $ ": ClientState=" $ GetEnum(Enum'EWeaponClientState', W.ClientState);
s @= "ClientGrenadeState=" $ GetEnum(Enum'EClientGrenadeState', W.ClientGrenadeState);
ClientMessage(s, 'log');
}
}
ClientMessage("SecondaryItem: " $ ScrnHumanPawn(Pawn).SecondaryItem, 'log');
}
exec function FixQuickMelee()
{
local Inventory inv;
local KFWeapon W;
local int c;
if ( Pawn == none )
return;
if ( Level.NetMode == NM_Client ) {
ServerFixQuickMelee(); // do the same on server
}
KFPawn(Pawn).SecondaryItem = none;
KFPawn(Pawn).bThrowingNade = false;
for ( inv = Pawn.Inventory; inv != none && ++c < 1000 ; inv = inv.Inventory ) {
W = KFWeapon(inv);
if ( W != none ) {
if ( W != Pawn.Weapon ) {
W.ClientState = WS_Hidden;
W.ClientGrenadeState = GN_None;
}
W.SetTimer(0, false);
W.GotoState('');
}
}
if ( Pawn.Weapon != none && Pawn.Weapon.ClientState != WS_ReadyToFire ) {
Pawn.Weapon.ClientState = WS_Hidden;
KFWeapon(Pawn.Weapon).ClientGrenadeState = GN_BringUp;
Pawn.Weapon.BringUp();
}
}
function ServerFixQuickMelee()
{
if ( Level.NetMode != NM_Client ) {
FixQuickMelee();
}
}
// ======================== COMMENT BEFORE RELEASE !!! =====================
// exec function SetSID64(coerce string SteamID64)
// {
// //if ( Level.NetMode == NM_Standalone )
// if ( Role == ROLE_Authority )
// class'ScrnCustomPRI'.static.FindMe(PlayerReplicationInfo).SetSteamID64(SteamID64);
// }
//
// exec function TestSID()
// {
// SetSID64("76561197992537591");
// }
//
// exec function PerkLevel(int L)
// {
// if ( Role == ROLE_Authority )
// KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkillLevel = L;
// }
//
// exec function TestZedTime(optional float DesiredZedTimeDuration)
// {
// if ( Role == ROLE_Authority )
// KFGameType(Level.Game).DramaticEvent(1.0, DesiredZedTimeDuration);
// }
//
// exec function IncAch(name AchID)
// {
// if ( Level.NetMode == NM_Standalone )
// class'ScrnAchievements'.static.ProgressAchievementByID(Class'ScrnClientPerkRepLink'.Static.FindMe(self), AchID, 1);
// }
//
// exec function GiveAch(name AchID)
// {
// if ( Level.NetMode == NM_Standalone )
// class'ScrnAchievements'.static.ProgressAchievementByID(Class'ScrnClientPerkRepLink'.Static.FindMe(self), AchID, 1000);
// }
//
// exec function BuyAll()
// {
// local int i;
// local ScrnClientPerkRepLink L;
// local ScrnHumanPawn ScrnPawn;
//
// if ( Role != ROLE_Authority )
// return;
//
// PlayerReplicationInfo.Score = 1000000;
// ScrnPawn = ScrnHumanPawn(Pawn);
// L = class'ScrnClientPerkRepLink'.static.FindMe(self);
// for ( i=0; i<L.ShopInventory.length; ++i ) {
// ScrnPawn.DropAllWeapons(ScrnPawn);
// ScrnPawn.ServerBuyWeapon(class<Weapon>(L.ShopInventory[i].PC.default.InventoryType), 0);
// }
// }
//
//
// exec function GiveSpawnInv()
// {
// if ( Role != ROLE_Authority )
// return;
//
// KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.AddDefaultInventory(KFPlayerReplicationInfo(PlayerReplicationInfo), Pawn);
// }
//
// exec function TestEndGame()
// {
// if ( Role != ROLE_Authority )
// return;
//
// Mut.GameRules.bSuperPat = true;
// Mut.GameRules.bHasCustomZeds = true;
// Mut.GameRules.GameDoom3Kills = 100;
// Mut.GameRules.HardcoreLevel = 15;
// Mut.KF.WaveNum = Mut.KF.FinalWave + 1;
// Mut.KF.CheckEndGame(PlayerReplicationInfo, "Test");
// }
defaultproperties
{
bManualReload=True
bOtherPlayerLasersBlue=True
bAlwaysDisplayAchProgression=True
Custom3DScopeSens=24
bSpeechVote=True
AchievementDisplayCooldown=5.000000
bChangedPerkDuringGame=True
MaxVoiceMsgIn10s=5
strLocked="Weapon pickups LOCKED"
strUnlocked="Weapon pickups UNLOCKED"
strLockDisabled="Weapon lock is disabled by server"
strAlreadySpectating="Already spectating. Type READY, if you want to join the game."
DualWieldables(0)=(Single=Class'KFMod.Single',Dual=Class'KFMod.Dualies')
DualWieldables(1)=(Single=Class'KFMod.Deagle',Dual=Class'KFMod.DualDeagle')
DualWieldables(2)=(Single=Class'KFMod.GoldenDeagle',Dual=Class'KFMod.GoldenDualDeagle')
DualWieldables(3)=(Single=Class'KFMod.Magnum44Pistol',Dual=Class'KFMod.Dual44Magnum')
DualWieldables(4)=(Single=Class'KFMod.MK23Pistol',Dual=Class'KFMod.DualMK23Pistol')
DualWieldables(5)=(Single=Class'KFMod.FlareRevolver',Dual=Class'KFMod.DualFlareRevolver')
ProfilePageClassString="ScrnBalanceSrv.ScrnProfilePage"
TSCLobbyMenuClassString="ScrnBalanceSrv.TSCLobbyMenu"
bTSCAutoDetectKeys=True
RedCharacter="Pyro_Red"
BlueCharacter="Pyro_Blue"
RedCharacters(0)="Pyro_Red"
RedCharacters(1)="Agent_Wilkes"
RedCharacters(2)="Ash_Harding"
RedCharacters(3)="Chopper_Harris"
RedCharacters(4)="Corporal_Lewis"
RedCharacters(5)="Dave_The_Butcher_Roberts"
RedCharacters(6)="DJ_Scully"
RedCharacters(7)="Dr_Gary_Glover"
RedCharacters(8)="FoundryWorker_Aldridge"
RedCharacters(9)="Harold_Hunt"
RedCharacters(10)="Harold_Lott"
RedCharacters(11)="Kerry_Fitzpatrick"
RedCharacters(12)="Kevo_Chav"
RedCharacters(13)="KF_Soviet"
RedCharacters(14)="Lieutenant_Masterson"
RedCharacters(15)="Mrs_Foster"
RedCharacters(16)="Mr_Foster"
RedCharacters(17)="Paramedic_Alfred_Anderson"
RedCharacters(18)="Police_Constable_Briar"
RedCharacters(19)="Police_Sergeant_Davin"
RedCharacters(20)="Private_Schnieder"
RedCharacters(21)="Reverend_Alberts"
RedCharacters(22)="Ricky_Vegas"
RedCharacters(23)="Samuel_Avalon"
RedCharacters(24)="Security_Officer_Thorne"
RedCharacters(25)="Sergeant_Powers"
RedCharacters(26)="Shadow_Ferret"
RedCharacters(27)="Trooper_Clive_Jenkins"
RedCharacters(28)="Mr_Magma"
RedCharacters(29)="Ms_Clamley"
BlueCharacters(0)="Pyro_Blue"
BlueCharacters(1)="Baddest_Santa"
BlueCharacters(2)="Captian_Wiggins"
BlueCharacters(3)="ChickenNator"
BlueCharacters(4)="Commando_Chicken"
BlueCharacters(5)="Dr_Jeffrey_Tamm"
BlueCharacters(6)="DAR"
BlueCharacters(7)="Harchier_Spebbington"
BlueCharacters(8)="Harchier_Spebbington_II"
BlueCharacters(9)="Hayato_Tanaka"
BlueCharacters(10)="KF_German"
BlueCharacters(11)="LanceCorporal_Lee_Baron"
BlueCharacters(12)="Mike_Noble"
BlueCharacters(13)="Reaper"
BlueCharacters(14)="Reggie"
BlueCharacters(15)="Steampunk_Berserker"
BlueCharacters(16)="Steampunk_Commando"
BlueCharacters(17)="Steampunk_Demolition"
BlueCharacters(18)="Steampunk_DJ_Scully"
BlueCharacters(19)="Steampunk_Firebug"
BlueCharacters(20)="Steampunk_Medic"
BlueCharacters(21)="Steampunk_MrFoster"
BlueCharacters(22)="Steampunk_Mrs_Foster"
BlueCharacters(23)="Steampunk_Sharpshooter"
BlueCharacters(24)="Steampunk_Support_Specialist"
BlueCharacters(25)="Voltage"
CustomPlayerReplicationInfoClass=Class'ScrnBalanceSrv.ScrnCustomPRI'
bDamageAck=True
strNoPerkChanges="Mid-game perk changes disabled"
strPerkLocked="Perk is locked"
strSpecFavoriteAssigned="View target marked as favorite"
strSpecNoFavorites="No favorite view targets! Press '4' to assign favorite."
DLC(0)=(AppID=210934)
DLC(1)=(AppID=210938)
DLC(2)=(AppID=210939)
DLC(3)=(AppID=210942)
DLC(4)=(AppID=210943)
DLC(5)=(AppID=210944)
DLC(6)=(AppID=258751)
DLC(7)=(AppID=258752)
DLC(8)=(AppID=309991)
MusicPlaylistNames(0)="<DEFAULT>"
MusicPlaylistNames(1)="KF1 Classic Soundtrack"
MusicPlaylistNames(2)="DooM Metal Soundtrack"
MyMusic(0)=(PL=1,bTrader=True,Song="KF_Defection",Artist="zYnthetic",Title="Defection")
MyMusic(1)=(PL=1,bTrader=True,Song="KF_Harm",Artist="zYnthetic",Title="Harm Intended")
MyMusic(2)=(PL=1,bTrader=True,Song="KF_Insect",Artist="zYnthetic",Title="Insect Wings")
MyMusic(3)=(PL=1,bTrader=True,Song="KF_Mutagen",Artist="zYnthetic",Title="Mutagen")
MyMusic(4)=(PL=1,bTrader=True,Song="KF_Neurotoxin",Artist="zYnthetic",Title="Neurotoxin")
MyMusic(5)=(PL=1,bTrader=True,Song="KF_Peripheral",Artist="zYnthetic",Title="Peripheral")
MyMusic(6)=(PL=1,bTrader=True,Song="KF_SinSoma",Artist="zYnthetic",Title="Sin Soma and the Masquerade")
MyMusic(7)=(PL=1,bTrader=True,Song="KF_Smolder",Artist="zYnthetic",Title="Smolder")
MyMusic(8)=(PL=1,bTrader=True,Song="KF_SurfaceTension",Artist="zYnthetic",Title="Surface Tension")
MyMusic(9)=(PL=1,bTrader=True,Song="KF_TheEdge",Artist="zYnthetic",Title="The Edge Of The Abyss")
MyMusic(10)=(PL=1,bTrader=True,Song="KF_TheStitches",Artist="zYnthetic",Title="The Stitches Are A Reminder")
MyMusic(11)=(PL=1,bTrader=True,Song="KF_Treatments",Artist="zYnthetic",Title="Treatments Are More Profitable Than Cures")
MyMusic(12)=(PL=1,bTrader=True,Song="KF_Vapour",Artist="zYnthetic",Title="Vapour")
MyMusic(13)=(PL=1,bTrader=True,Song="KF_Wading",Artist="zYnthetic",Title="Wading Through The Bodies")
MyMusic(14)=(PL=1,Song="DirgeDefective1",Artist="Dirge",Title="Defective")
MyMusic(15)=(PL=1,Song="DirgeDefective2",Artist="Dirge",Title="Defective")
MyMusic(16)=(PL=1,Song="DirgeDisunion1",Artist="Dirge",Title="Disunion")
MyMusic(17)=(PL=1,Song="DirgeDisunion2",Artist="Dirge",Title="Disunion")
MyMusic(18)=(PL=1,Song="DirgeRepulse1",Artist="Dirge",Title="Repulse")
MyMusic(19)=(PL=1,Song="DirgeRepulse2",Artist="Dirge",Title="Repulse")
MyMusic(20)=(PL=1,Song="KF_Infectious_Cadaver",Artist="Six Feet of Foreplay",Title="Infectious Cadaver")
MyMusic(21)=(PL=1,Song="KF_BledDry",Artist="zYnthetic",Title="Bled Dry")
MyMusic(22)=(PL=1,Song="KF_Containment",Artist="zYnthetic",Title="Containment Breach")
MyMusic(23)=(PL=1,Song="KF_Hunger",Artist="zYnthetic",Title="Hunger")
MyMusic(24)=(PL=1,Song="KF_Pathogen",Artist="zYnthetic",Title="Pathogen")
MyMusic(25)=(PL=1,Song="KF_WPrevention",Artist="zYnthetic",Title="Witness Prevention")
MyMusic(26)=(PL=1,Wave=11,Song="KF_Abandon",Artist="zYnthetic",Title="Abandon All")
MyMusic(27)=(PL=2,bTrader=True,Song="EGT-ThisLove",Artist="elguitarTom",Title="Waiting For Romero To Play")
MyMusic(28)=(PL=2,bTrader=True,Song="EGT-Interlevel",Artist="elguitarTom",Title="Interlevel")
MyMusic(29)=(PL=2,Wave=1,Song="EGT-Entryway",Artist="elguitarTom",Title="Entryway")
MyMusic(30)=(PL=2,Wave=2,Song="EGT-E1M1",Artist="elguitarTom",Title="E1M1")
MyMusic(31)=(PL=2,Wave=3,Song="EGT-Demon1",Artist="elguitarTom",Title="The Demon's Dead, Part 1")
MyMusic(32)=(PL=2,Wave=4,Song="EGT-Shawn",Artist="elguitarTom",Title="Shawn's Got The Shotgun")
MyMusic(33)=(PL=2,Wave=5,Song="EGT-ToxinRefinery",Artist="elguitarTom",Title="Toxin Refinery")
MyMusic(34)=(PL=2,Wave=6,Song="EGT-Entryway",Artist="elguitarTom",Title="Entryway")
MyMusic(35)=(PL=2,Wave=7,Song="EGT-Demon2",Artist="elguitarTom",Title="The Demon's Dead, Part 2")
MyMusic(36)=(PL=2,Wave=8,Song="EGT-PhobosLab",Artist="elguitarTom",Title="Phobos Lab")
MyMusic(37)=(PL=2,Wave=9,Song="EGT-E1M1",Artist="elguitarTom",Title="E1M1")
MyMusic(38)=(PL=2,Wave=10,Song="EGT-Shawn",Artist="elguitarTom",Title="Shawn's Got The Shotgun")
MyMusic(39)=(PL=2,Wave=11,Song="EGT-SignOfEvil",Artist="elguitarTom",Title="Sign Of Evil")
strPlayingSong="Now playing: "
FlareCloudClass=Class'ScrnBalanceSrv.ScrnFlareCloud'
LobbyMenuClassString="ScrnBalanceSrv.ScrnLobbyMenu"
PawnClass=Class'ScrnBalanceSrv.ScrnHumanPawn'
bNotifyLocalPlayerTeamReceived=True
}