// Server extension mutator, by Marco. Class ServerExtMut extends KFMutator config(ServerExtMut); // Webadmin var array<FWebAdminConfigInfo> WebConfigs; struct FInventory { var class<Inventory> ItemClass; var int Values[4]; }; struct FSavedInvEntry { var Controller OwnerPlayer; var byte Gren; var array<FInventory> Inv; }; var array<FSavedInvEntry> PlayerInv; var config array<string> PerkClasses,CustomChars,AdminCommands,CustomItems,BonusGameSongs,BonusGameFX; var array< class<Ext_PerkBase> > LoadedPerks; var array<FCustomCharEntry> CustomCharList; var ExtPlayerStat ServerStatLoader; var KFPawn LastHitZed; var int LastHitHP; var ExtPlayerController LastDamageDealer; var vector LastDamagePosition; var private const array<string> DevList; var transient private array<UniqueNetId> DevNetID; var ExtXMLOutput FileOutput; var transient class<DamageType> LastKillDamageType; var SoundCue BonusGameCue; var Object BonusGameFXObj; var array<FCustomTraderItem> CustomItemList; var KFGFxObject_TraderItems CustomTrader; const SettingsTagVer=12; var KFGameReplicationInfo KF; var config int SettingsInit; var config int ForcedMaxPlayers,PlayerRespawnTime,LargeMonsterHP,StatAutoSaveWaves,MinUnloadPerkLevel,PostGameRespawnCost,MaxTopPlayers; var config float UnloadPerkExpCost; var globalconfig string ServerMOTD,StatFileDir; var array<Controller> PendingSpawners; var int LastWaveNum,NumWaveSwitches; var ExtSpawnPointHelper SpawnPointer; var bool bRespawnCheck,bSpecialSpawn,bGameHasEnded,bIsPostGame; var config bool bKillMessages,bDamageMessages,bEnableMapVote,bNoAdminCommands,bNoWebAdmin,bNoBoomstickJumping,bDumpXMLStats,bRagdollFromFall,bRagdollFromMomentum,bRagdollFromBackhit,bAddCountryTags; function PostBeginPlay() { local xVotingHandler MV; local int i,j; local class<Ext_PerkBase> PK; local UniqueNetId Id; local KFCharacterInfo_Human CH; local ObjectReferencer OR; local Object O; local string S; local FCustomTraderItem CI; local bool bLock; Super.PostBeginPlay(); if( WorldInfo.Game.BaseMutator==None ) WorldInfo.Game.BaseMutator = Self; else WorldInfo.Game.BaseMutator.AddMutator(Self); if( bDeleteMe ) // This was a duplicate instance of the mutator. return; SpawnPointer = class'ExtSpawnPointHelper'.Static.FindHelper(WorldInfo); // Start init world pathlist. //OnlineSubsystemSteamworks(class'GameEngine'.Static.GetOnlineSubsystem()).Int64ToUniqueNetId("",Id); //`Log("TEST"@class'OnlineSubsystem'.Static.UniqueNetIdToString(Id)); DevNetID.Length = DevList.Length; for( i=0; i<DevList.Length; ++i ) { class'OnlineSubsystem'.Static.StringToUniqueNetId(DevList[i],Id); DevNetID[i] = Id; } ServerStatLoader = new (None) class'ExtPlayerStat'; WorldInfo.Game.HUDType = class'KFExtendedHUD'; WorldInfo.Game.PlayerControllerClass = class'ExtPlayerController'; WorldInfo.Game.PlayerReplicationInfoClass = class'ExtPlayerReplicationInfo'; WorldInfo.Game.DefaultPawnClass = class'ExtHumanPawn'; KFGameInfo(WorldInfo.Game).CustomizationPawnClass = class'ExtPawn_Customization'; KFGameInfo(WorldInfo.Game).KFGFxManagerClass = class'ExtMoviePlayer_Manager'; if( ServerMOTD=="" ) ServerMOTD = "Message of the Day"; if( StatFileDir=="" ) { StatFileDir = "../../KFGame/Script/%s.usa"; Default.StatFileDir = "../../KFGame/Script/%s.usa"; } if( SettingsInit!=SettingsTagVer ) { if( SettingsInit==0 ) ForcedMaxPlayers = 6; if( SettingsInit<2 ) { bKillMessages = true; bDamageMessages = true; LargeMonsterHP = 800; } if( SettingsInit<3 ) bEnableMapVote = true; if( SettingsInit<5 ) { StatAutoSaveWaves = 1; PerkClasses.Length = 10; PerkClasses[0] = PathName(class'Ext_PerkBerserker'); PerkClasses[1] = PathName(class'Ext_PerkCommando'); PerkClasses[2] = PathName(class'Ext_PerkFieldMedic'); PerkClasses[3] = PathName(class'Ext_PerkSupport'); PerkClasses[4] = PathName(class'Ext_PerkDemolition'); PerkClasses[5] = PathName(class'Ext_PerkFirebug'); PerkClasses[6] = PathName(class'Ext_PerkGunslinger'); PerkClasses[7] = PathName(class'Ext_PerkSharpshooter'); PerkClasses[8] = PathName(class'Ext_PerkSWAT'); PerkClasses[9] = PathName(class'Ext_PerkSurvivalist'); } else if( SettingsInit<11 ) { PerkClasses.AddItem(PathName(class'Ext_PerkSharpshooter')); PerkClasses.AddItem(PathName(class'Ext_PerkSWAT')); PerkClasses.AddItem(PathName(class'Ext_PerkSurvivalist')); } else if( SettingsInit==11 ) PerkClasses.AddItem(PathName(class'Ext_PerkSurvivalist')); if( SettingsInit<6 ) { MinUnloadPerkLevel = 25; UnloadPerkExpCost = 0.1; } if( SettingsInit<8 ) { AdminCommands.Length = 2; AdminCommands[0] = "Kick:Kick Player"; AdminCommands[1] = "KickBan:Kick-Ban Player"; } if( SettingsInit<9 ) MaxTopPlayers = 50; SettingsInit = SettingsTagVer; SaveConfig(); } for( i=0; i<PerkClasses.Length; ++i ) { PK = class<Ext_PerkBase>(DynamicLoadObject(PerkClasses[i],class'Class')); if( PK!=None ) { LoadedPerks.AddItem(PK); PK.Static.CheckConfig(); } } j = 0; for( i=0; i<CustomChars.Length; ++i ) { bLock = Left(CustomChars[i],1)=="*"; S = (bLock ? Mid(CustomChars[i],1) : CustomChars[i]); CH = KFCharacterInfo_Human(DynamicLoadObject(S,class'KFCharacterInfo_Human',true)); if( CH!=None ) { CustomCharList.Length = j+1; CustomCharList[j].bLock = bLock; CustomCharList[j].Char = CH; ++j; continue; } OR = ObjectReferencer(DynamicLoadObject(S,class'ObjectReferencer')); if( OR!=None ) { foreach OR.ReferencedObjects(O) { if( KFCharacterInfo_Human(O)!=None ) { CustomCharList.Length = j+1; CustomCharList[j].bLock = bLock; CustomCharList[j].Char = KFCharacterInfo_Human(O); CustomCharList[j].Ref = OR; ++j; } } } } // Bonus (pong) game contents. if( BonusGameSongs.Length>0 ) { BonusGameCue = SoundCue(DynamicLoadObject(BonusGameSongs[Rand(BonusGameSongs.Length)],class'SoundCue')); } if( BonusGameFX.Length>0 ) { BonusGameFXObj = DynamicLoadObject(BonusGameFX[Rand(BonusGameFX.Length)],class'Object'); if( SoundCue(BonusGameFXObj)==None && ObjectReferencer(BonusGameFXObj)==None ) // Check valid type. BonusGameFXObj = None; } for( i=0; i<CustomItems.Length; ++i ) { CI.WeaponDef = class<KFWeaponDefinition>(DynamicLoadObject(CustomItems[i],class'Class')); if( CI.WeaponDef==None ) continue; CI.WeaponClass = class<KFWeapon>(DynamicLoadObject(CI.WeaponDef.Default.WeaponClassPath,class'Class')); if( CI.WeaponClass==None ) continue; CustomItemList.AddItem(CI); if( CustomTrader==None ) { CustomTrader = class'ExtPlayerReplicationInfo'.Static.CreateNewList(); SetTimer(0.1,false,'InitGRIList'); } class'ExtPlayerReplicationInfo'.Static.SetWeaponInfo(WorldInfo.NetMode==NM_DedicatedServer,CustomTrader.SaleItems.Length,CI,CustomTrader); } if( ForcedMaxPlayers>0 ) { SetMaxPlayers(); SetTimer(0.001,false,'SetMaxPlayers'); } bRespawnCheck = (PlayerRespawnTime>0); if( bRespawnCheck ) SetTimer(1,true); if( bEnableMapVote ) { foreach DynamicActors(class'xVotingHandler',MV) break; if( MV==None ) MV = Spawn(class'xVotingHandler'); MV.BaseMutator = Class; } SetTimer(1,true,'CheckWave'); if( !bNoWebAdmin && WorldInfo.NetMode!=NM_StandAlone ) SetTimer(0.1,false,'SetupWebAdmin'); if( bDumpXMLStats ) FileOutput = Spawn(class'ExtXMLOutput'); } static final function string GetStatFile( const out UniqueNetId UID ) { return Repl(Default.StatFileDir,"%s","U_"$class'OnlineSubsystem'.Static.UniqueNetIdToString(UID)); } final function bool IsDev( const out UniqueNetId UID ) { local int i; for( i=(DevNetID.Length-1); i>=0; --i ) if( DevNetID[i]==UID ) return true; return false; } function InitGRIList() { local ExtPlayerController PC; KFGameReplicationInfo(WorldInfo.GRI).TraderItems = CustomTrader; // Must sync up local client. if( WorldInfo.NetMode==NM_StandAlone ) { foreach LocalPlayerControllers(class'ExtPlayerController',PC) if( PC.PurchaseHelper!=None ) PC.PurchaseHelper.TraderItems = CustomTrader; } } function CheckWave() { if( KF==None ) { KF = KFGameReplicationInfo(WorldInfo.GRI); if( KF==None ) return; } if( LastWaveNum!=KF.WaveNum ) { LastWaveNum = KF.WaveNum; NotifyWaveChange(); } if( !bGameHasEnded && KF.bMatchIsOver ) // HACK, since KFGameInfo_Survival doesn't properly notify mutators of this! { SaveAllPerks(true); bGameHasEnded = true; } } function NotifyWaveChange() { local ExtPlayerController ExtPC; if( bRespawnCheck ) { bIsPostGame = (KF.WaveMax<KF.WaveNum); bRespawnCheck = (!bIsPostGame || PostGameRespawnCost>=0); if( bRespawnCheck ) SavePlayerInventory(); } if( StatAutoSaveWaves>0 && ++NumWaveSwitches>=StatAutoSaveWaves ) { NumWaveSwitches = 0; SaveAllPerks(); } if( !KF.bTraderIsOpen ) { foreach WorldInfo.AllControllers(class'ExtPlayerController',ExtPC) ExtPC.bSetPerk = false; } } function SetupWebAdmin() { local WebServer W; local WebAdmin A; local ExtWebApp xW; local byte i; foreach AllActors(class'WebServer',W) break; if( W!=None ) { for( i=0; (i<10 && A==None); ++i ) A = WebAdmin(W.ApplicationObjects[i]); if( A!=None ) { xW = new (None) class'ExtWebApp'; xW.MyMutator = Self; A.addQueryHandler(xW); } else `Log("ExtWebAdmin ERROR: No valid WebAdmin application found!"); } else `Log("ExtWebAdmin ERROR: No WebServer object found!"); } function SetMaxPlayers() { local OnlineGameSettings GameSettings; WorldInfo.Game.MaxPlayers = ForcedMaxPlayers; WorldInfo.Game.MaxPlayersAllowed = ForcedMaxPlayers; if( WorldInfo.Game.GameInterface!=None ) { GameSettings = WorldInfo.Game.GameInterface.GetGameSettings(WorldInfo.Game.PlayerReplicationInfoClass.default.SessionName); if( GameSettings!=None ) GameSettings.NumPublicConnections = ForcedMaxPlayers; } } function AddMutator(Mutator M) { if( M!=Self ) // Make sure we don't get added twice. { if( M.Class==Class ) M.Destroy(); else Super.AddMutator(M); } } function ScoreKill(Controller Killer, Controller Killed) { local KFPlayerController KFPC; local ExtPerkManager KillersPerk; if( bRespawnCheck && Killed.bIsPlayer ) CheckRespawn(Killed); if( KFPawn_Monster(Killed.Pawn)!=None && Killed.GetTeamNum()!=0 && Killer.bIsPlayer && Killer.GetTeamNum()==0 ) { if( ExtPlayerController(Killer)!=None && ExtPlayerController(Killer).ActivePerkManager!=None ) ExtPlayerController(Killer).ActivePerkManager.PlayerKilled(KFPawn_Monster(Killed.Pawn),LastKillDamageType); if( bKillMessages && Killer.PlayerReplicationInfo!=None ) BroadcastKillMessage(Killed.Pawn,Killer); //else if( Killer!=None && Killer!=Killed && Killer.GetTeamNum()==0 && Ext_T_MonsterPRI(Killer.PlayerReplicationInfo)!=None ) // BroadcastKillMessage(Killed.Pawn,Ext_T_MonsterPRI(Killer.PlayerReplicationInfo).OwnerController); } if ( MyKFGI != None && MyKFGI.IsZedTimeActive() && KFPawn_Monster(Killed.Pawn) != None ) { KFPC = KFPlayerController(Killer); if ( KFPC != none ) { KillersPerk = ExtPerkManager(KFPC.GetPerk()); if ( MyKFGI.ZedTimeRemaining > 0.f && KillersPerk != none && KillersPerk.GetZedTimeExtensions( KFPC.GetLevel() ) > MyKFGI.ZedTimeExtensionsUsed ) { MyKFGI.DramaticEvent(1.0); MyKFGI.ZedTimeExtensionsUsed++; } } } if( ExtPlayerController(Killed)!=None ) CheckPerkChange(ExtPlayerController(Killed)); if (NextMutator != None) NextMutator.ScoreKill(Killer, Killed); } function bool PreventDeath(Pawn Killed, Controller Killer, class<DamageType> damageType, vector HitLocation) { if( (KFPawn_Human(Killed)!=None && CheckPreventDeath(KFPawn_Human(Killed),Killer,damageType)) || Super.PreventDeath(Killed,Killer,damageType,HitLocation) ) return true; LastKillDamageType = damageType; if( Killed.Controller!=None && KFPawn_Monster(Killed)!=None ) { // Hack for when pet kills a zed. if( Killed.GetTeamNum()!=0 ) { if( Killer!=None && Killer!=Killed.Controller && Killer.GetTeamNum()==0 && Ext_T_MonsterPRI(Killer.PlayerReplicationInfo)!=None ) GT_PlayerKilled(Ext_T_MonsterPRI(Killer.PlayerReplicationInfo).OwnerController,Killed.Controller,damageType); } // Broadcast pet's deathmessage. else if( Killed.PlayerReplicationInfo!=None && PlayerController(Killed.Controller)==None && damageType!=class'KFDT_Healing' ) BroadcastFFDeath(Killer,Killed,damageType); } return false; } // Replica of KFGameInfo.Killed base. final function GT_PlayerKilled( Controller Killer, Controller Killed, class<DamageType> damageType ) { local ExtPlayerController KFPC; local KFPawn_Monster MonsterPawn; local KFGameInfo KFG; KFG = KFGameInfo(WorldInfo.Game); ScoreKill(Killer,Killed); // Broadcast kill message. KFPC = ExtPlayerController(Killer); MonsterPawn = KFPawn_Monster(Killed.Pawn); if( KFG!=None && KFPC != none && MonsterPawn!=none ) { //Chris: We have to do it earlier here because we need a damage type KFPC.AddZedKill( MonsterPawn.class, KFG.GameDifficulty, damageType ); if( KFPC.ActivePerkManager!=none && KFPC.ActivePerkManager.CanEarnSmallRadiusKillXP(damageType) ) KFG.CheckForBerserkerSmallRadiusKill( MonsterPawn, KFPC ); } } final function bool CheckPreventDeath( KFPawn_Human Victim, Controller Killer, class<DamageType> damageType ) { local ExtPlayerController E; if( Victim.IsA('KFPawn_Customization') ) return false; E = ExtPlayerController(Victim.Controller); return (E!=None && E.ActivePerkManager!=None && E.ActivePerkManager.CurrentPerk!=None && E.ActivePerkManager.CurrentPerk.PreventDeath(Victim,Killer,damageType)); } final function BroadcastKillMessage( Pawn Killed, Controller Killer ) { local ExtPlayerController E; if( Killer==None || Killer.PlayerReplicationInfo==None ) return; if( Killed.Default.Health>=LargeMonsterHP ) { foreach WorldInfo.AllControllers(class'ExtPlayerController',E) if( !E.bClientHideKillMsg ) E.ReceiveKillMessage(Killed.Class,true,Killer.PlayerReplicationInfo); } else if( ExtPlayerController(Killer)!=None && !ExtPlayerController(Killer).bClientHideKillMsg ) ExtPlayerController(Killer).ReceiveKillMessage(Killed.Class); } final function BroadcastFFDeath( Controller Killer, Pawn Killed, class<DamageType> damageType ) { local ExtPlayerController E; local PlayerReplicationInfo KillerPRI; local string P; local bool bFF; P = Killed.PlayerReplicationInfo.PlayerName; if( Killer==None || Killer==Killed.Controller ) { foreach WorldInfo.AllControllers(class'ExtPlayerController',E) E.ClientZedKillMessage(damageType,P); return; } bFF = (Killer.GetTeamNum()==0); KillerPRI = Killer.PlayerReplicationInfo; if( PlayerController(Killer)==None ) KillerPRI = None; foreach WorldInfo.AllControllers(class'ExtPlayerController',E) E.ClientZedKillMessage(damageType,P,KillerPRI,Killer.Pawn.Class,bFF); } function NetDamage(int OriginalDamage, out int Damage, Pawn Injured, Controller InstigatedBy, vector HitLocation, out vector Momentum, class<DamageType> DamageType, Actor DamageCauser) { if (NextMutator != None) NextMutator.NetDamage(OriginalDamage, Damage, Injured, InstigatedBy, HitLocation, Momentum, DamageType, DamageCauser); if( LastDamageDealer!=None ) // Make sure no other damagers interfear with the old thing going on. { ClearTimer('CheckDamageDone'); CheckDamageDone(); } if ( KFPawn_Monster(Injured) != None && InstigatedBy != none && InstigatedBy.GetTeamNum() == Injured.GetTeamNum() ) { Momentum = vect(0,0,0); Damage = 0; return; } if( Damage>0 && InstigatedBy!=None ) { if( KFPawn_Monster(Injured)!=None ) { if( Injured.GetTeamNum()!=0 ) { LastDamageDealer = ExtPlayerController(InstigatedBy); if( bDamageMessages && LastDamageDealer!=None && !LastDamageDealer.bNoDamageTracking ) { // Must delay this until next to get accurate damage dealt result. LastHitZed = KFPawn(Injured); LastHitHP = LastHitZed.Health; LastDamagePosition = HitLocation; SetTimer(0.001,false,'CheckDamageDone'); } else { LastDamageDealer = None; // Give credits to pet's owner. if( Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo)!=None ) HackSetHistory(KFPawn(Injured),Injured,Ext_T_MonsterPRI(InstigatedBy.PlayerReplicationInfo).OwnerController,Damage,HitLocation); } } else if( KFPawn(InstigatedBy.Pawn).GetTeamNum() != KFPawn(Injured).GetTeamNum() ) { Momentum = vect(0,0,0); Damage = 0; } } else if( bDamageMessages && KFPawn_Human(Injured)!=None && Injured.GetTeamNum()==0 && InstigatedBy.GetTeamNum()!=0 && ExtPlayerController(InstigatedBy)!=None ) { LastDamageDealer = ExtPlayerController(InstigatedBy); if( bDamageMessages && !LastDamageDealer.bClientHideNumbers ) { // Must delay this until next to get accurate damage dealt result. LastHitZed = KFPawn(Injured); LastHitHP = LastHitZed.Health; LastDamagePosition = HitLocation; SetTimer(0.001,false,'CheckDamageDone'); } } } } final function CheckDamageDone() { local int Damage; if( LastDamageDealer!=None && LastHitZed!=None && LastHitHP!=LastHitZed.Health ) { Damage = LastHitHP-Max(LastHitZed.Health,0); if( Damage>0 ) { if( !LastDamageDealer.bClientHideDamageMsg && KFPawn_Monster(LastHitZed)!=None ) LastDamageDealer.ReceiveDamageMessage(LastHitZed.Class,Damage); if( !LastDamageDealer.bClientHideNumbers ) LastDamageDealer.ClientNumberMsg(Damage,LastDamagePosition,DMG_PawnDamage); } } LastDamageDealer = None; } final function HackSetHistory( KFPawn C, Pawn Injured, Controller Player, int Damage, vector HitLocation ) { local int i; local ExtPlayerController PC; if( Player==None ) return; PC = ExtPlayerController(Player); if( bDamageMessages && PC!=None ) { if( !PC.bClientHideDamageMsg ) PC.ReceiveDamageMessage(Injured.Class,Damage); if( !PC.bClientHideNumbers ) PC.ClientNumberMsg(Damage,HitLocation,DMG_PawnDamage); } i = C.DamageHistory.Find('DamagerController',Player); if( i==-1 ) { i = C.DamageHistory.Length; C.DamageHistory.Length = i+1; C.DamageHistory[i].DamagerController = Player; C.DamageHistory[i].DamagerPRI = Player.PlayerReplicationInfo; C.DamageHistory[i].DamagePerks.AddItem(class'ExtPerkManager'); C.DamageHistory[i].Damage = Damage; } else if( (WorldInfo.TimeSeconds-C.DamageHistory[i].LastTimeDamaged)<10 ) C.DamageHistory[i].Damage += Damage; else C.DamageHistory[i].Damage = Damage; C.DamageHistory[i].LastTimeDamaged = WorldInfo.TimeSeconds; C.DamageHistory[i].TotalDamage += Damage; } function bool HandleRestartGame() { if( !bGameHasEnded ) { SaveAllPerks(true); bGameHasEnded = true; } return Super.HandleRestartGame(); } function NotifyLogout(Controller Exiting) { if( KFPlayerController(Exiting)!=None ) RemoveRespawn(Exiting); if( !bGameHasEnded && ExtPlayerController(Exiting)!=None ) { CheckPerkChange(ExtPlayerController(Exiting)); SavePlayerPerk(ExtPlayerController(Exiting)); } if ( NextMutator != None ) NextMutator.NotifyLogout(Exiting); } function NotifyLogin(Controller NewPlayer) { if( ExtPlayerController(NewPlayer)!=None ) { if( ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo)!=None ) InitCustomChars(ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo)); if( bAddCountryTags && NetConnection(PlayerController(NewPlayer).Player)!=None ) ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo).SetPlayerNameTag(class'CtryDatabase'.Static.GetClientCountryStr(PlayerController(NewPlayer).GetPlayerNetworkAddress())); ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo).bIsDev = IsDev(NewPlayer.PlayerReplicationInfo.UniqueId); if( WorldInfo.NetMode!=NM_StandAlone ) ExtPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo).OnRepNextItem = GetNextItem; if( BonusGameCue!=None || BonusGameFXObj!=None ) ExtPlayerController(NewPlayer).ClientSetBonus(BonusGameCue,BonusGameFXObj); if( bRespawnCheck ) CheckRespawn(NewPlayer); if( !bGameHasEnded ) InitializePerks(ExtPlayerController(NewPlayer)); SendMOTD(ExtPlayerController(NewPlayer)); } if ( NextMutator != None ) NextMutator.NotifyLogin(NewPlayer); } final function InitializePerks( ExtPlayerController Other ) { local ExtPerkManager PM; local Ext_PerkBase P; local int i; Other.OnChangePerk = PlayerChangePerk; Other.OnBoughtStats = PlayerBuyStats; Other.OnBoughtTrait = PlayerBoughtTrait; Other.OnPerkReset = ResetPlayerPerk; Other.OnAdminHandle = AdminCommand; Other.OnSetMOTD = AdminSetMOTD; Other.OnRequestUnload = PlayerUnloadInfo; Other.OnSpectateChange = PlayerChangeSpec; Other.OnClientGetStat = class'ExtStatList'.Static.GetStat; PM = Other.ActivePerkManager; PM.InitPerks(); for( i=0; i<LoadedPerks.Length; ++i ) { P = Spawn(LoadedPerks[i],Other); PM.RegisterPerk(P); } ServerStatLoader.FlushData(); if( ServerStatLoader.LoadStatFile(Other) ) { ServerStatLoader.ToStart(); PM.LoadData(ServerStatLoader); if( Default.MaxTopPlayers>0 ) class'ExtStatList'.Static.SetTopPlayers(Other); } PM.ServerInitPerks(); PM.InitiateClientRep(); } final function SendMOTD( ExtPlayerController PC ) { local string S; local int i; S = ServerMOTD; while( Len(S)>510 ) { PC.ReceiveServerMOTD(Left(S,500),false); S = Mid(S,500); } PC.ReceiveServerMOTD(S,true); for( i=0; i<AdminCommands.Length; ++i ) PC.AddAdminCmd(AdminCommands[i]); } final function SavePlayerPerk( ExtPlayerController PC ) { if( PC.ActivePerkManager!=None && PC.ActivePerkManager.bStatsDirty ) { // Verify broken stats. if( PC.ActivePerkManager.bUserStatsBroken ) { PC.ClientMessage("Warning: Your stats are broken, not saving.",'Priority'); return; } ServerStatLoader.FlushData(); if( ServerStatLoader.LoadStatFile(PC) && ServerStatLoader.GetSaveVersion()!=PC.ActivePerkManager.UserDataVersion ) { PC.ActivePerkManager.bUserStatsBroken = true; PC.ClientMessage("Warning: Your stats save data version differs from what is loaded, stat saving disabled to prevent stats loss.",'Priority'); return; } // Actually save. ServerStatLoader.FlushData(); PC.ActivePerkManager.SaveData(ServerStatLoader); ServerStatLoader.SaveStatFile(PC); PC.ActivePerkManager.bStatsDirty = false; // Write XML output. if( FileOutput!=None ) FileOutput.DumpXML(PC.ActivePerkManager); } } function SaveAllPerks( optional bool bOnEndGame ) { local ExtPlayerController PC; if( bGameHasEnded ) return; foreach WorldInfo.AllControllers(class'ExtPlayerController',PC) if( PC.ActivePerkManager!=None && PC.ActivePerkManager.bStatsDirty ) { if( bOnEndGame ) CheckPerkChange(PC); SavePlayerPerk(PC); } } function CheckRespawn( Controller PC ) { if( !PC.bIsPlayer || ExtPlayerReplicationInfo(PC.PlayerReplicationInfo)==None || PC.PlayerReplicationInfo.bOnlySpectator || WorldInfo.Game.bWaitingToStartMatch || WorldInfo.Game.bGameEnded ) return; // VS redead. if( ExtHumanPawn(PC.Pawn)!=None && ExtHumanPawn(PC.Pawn).bPendingRedead ) return; if( bIsPostGame && PC.PlayerReplicationInfo.Score<PostGameRespawnCost ) { if( PlayerController(PC)!=None ) PlayerController(PC).ClientMessage("You can't afford to respawn anymore (need "$PostGameRespawnCost@Chr(163)$")!",'LowCriticalEvent'); return; } ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = PlayerRespawnTime; PC.PlayerReplicationInfo.bForceNetUpdate = true; if( PendingSpawners.Find(PC)<0 ) PendingSpawners.AddItem(PC); } function RemoveRespawn( Controller PC ) { ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = -1; PendingSpawners.RemoveItem(PC); } final function InitPlayer( ExtHumanPawn Other ) { local ExtPlayerReplicationInfo PRI; PRI = ExtPlayerReplicationInfo(Other.PlayerReplicationInfo); if( PRI!=None && PRI.PerkManager!=None && PRI.PerkManager.CurrentPerk!=None ) PRI.PerkManager.CurrentPerk.ApplyEffectsTo(Other); Other.bRagdollFromFalling = bRagdollFromFall; Other.bRagdollFromMomentum = bRagdollFromMomentum; Other.bRagdollFromBackhit = bRagdollFromBackhit; } function ModifyPlayer(Pawn Other) { if( ExtHumanPawn(Other)!=None ) InitPlayer(ExtHumanPawn(Other)); if ( NextMutator != None ) NextMutator.ModifyPlayer(Other); } function Timer() { local int i; local Controller PC; local bool bSpawned,bAllDead; bAllDead = (KFGameInfo(WorldInfo.Game).GetLivingPlayerCount()<=0 || WorldInfo.Game.bGameEnded || !bRespawnCheck); for( i=0; i<PendingSpawners.Length; ++i ) { PC = PendingSpawners[i]; if( bAllDead || PC==None || PC.PlayerReplicationInfo.bOnlySpectator || (PC.Pawn!=None && PC.Pawn.IsAliveAndWell()) ) { if( PC!=None ) { ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = -1; PC.PlayerReplicationInfo.bForceNetUpdate = true; } PendingSpawners.Remove(i--,1); } else if( bIsPostGame && PC.PlayerReplicationInfo.Score<PostGameRespawnCost ) { ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = -1; PC.PlayerReplicationInfo.bForceNetUpdate = true; if( PlayerController(PC)!=None ) PlayerController(PC).ClientMessage("You can't afford to respawn anymore (need "$PostGameRespawnCost@Chr(163)$")!",'LowCriticalEvent'); PendingSpawners.Remove(i--,1); } else if( --ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter<=0 ) { PC.PlayerReplicationInfo.bForceNetUpdate = true; ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = 0; if( !bSpawned ) // Spawn only one player at time (so game doesn't crash if many players spawn in same time). { bSpawned = true; if( RespawnPlayer(PC) ) { if( bIsPostGame ) { if( PlayerController(PC)!=None ) PlayerController(PC).ClientMessage("This respawn cost you "$PostGameRespawnCost@Chr(163)$"!",'LowCriticalEvent'); PC.PlayerReplicationInfo.Score-=PostGameRespawnCost; } ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).RespawnCounter = -1; PC.PlayerReplicationInfo.bForceNetUpdate = true; } } } else PC.PlayerReplicationInfo.bForceNetUpdate = true; } } final function SavePlayerInventory() { local KFPawn_Human P; local int i,j; local Inventory Inv; local KFWeapon K; PlayerInv.Length = 0; i = 0; foreach WorldInfo.AllPawns(class'KFPawn_Human',P) if( P.IsAliveAndWell() && P.InvManager!=None && P.Controller!=None && P.Controller.PlayerReplicationInfo!=None ) { PlayerInv.Length = i+1; PlayerInv[i].OwnerPlayer = P.Controller; PlayerInv[i].Gren = KFInventoryManager(P.InvManager).GrenadeCount; j = 0; foreach P.InvManager.InventoryActors(class'Inventory',Inv) { if( KFInventory_Money(Inv)!=None ) continue; K = KFWeapon(Inv); if( K!=None && !K.bCanThrow ) // Skip non-throwable items. continue; PlayerInv[i].Inv.Length = j+1; PlayerInv[i].Inv[j].ItemClass = Inv.Class; if( K!=None ) { PlayerInv[i].Inv[j].Values[0] = K.SpareAmmoCount[0]; PlayerInv[i].Inv[j].Values[1] = K.SpareAmmoCount[1]; PlayerInv[i].Inv[j].Values[2] = K.AmmoCount[0]; PlayerInv[i].Inv[j].Values[3] = K.AmmoCount[1]; } ++j; } ++i; } } final function bool AddPlayerSpecificInv( Pawn Other ) { local int i,j; local Inventory Inv; local KFWeapon K; for( i=(PlayerInv.Length-1); i>=0; --i ) if( PlayerInv[i].OwnerPlayer==Other.Controller ) { KFInventoryManager(Other.InvManager).bInfiniteWeight = true; KFInventoryManager(Other.InvManager).GrenadeCount = PlayerInv[i].Gren; for( j=(PlayerInv[i].Inv.Length-1); j>=0; --j ) { Inv = Other.InvManager.FindInventoryType(PlayerInv[i].Inv[j].ItemClass,false); if( Inv==None ) { Inv = Other.InvManager.CreateInventory(PlayerInv[i].Inv[j].ItemClass); } K = KFWeapon(Inv); if( K!=None ) { K.SpareAmmoCount[0] = PlayerInv[i].Inv[j].Values[0]; K.SpareAmmoCount[1] = PlayerInv[i].Inv[j].Values[1]; K.AmmoCount[0] = PlayerInv[i].Inv[j].Values[2]; K.AmmoCount[1] = PlayerInv[i].Inv[j].Values[3]; K.ClientForceAmmoUpdate(K.AmmoCount[0],K.SpareAmmoCount[0]); K.ClientForceSecondaryAmmoUpdate(K.AmmoCount[1]); } } if( Other.InvManager.FindInventoryType(class'KFInventory_Money',true)==None ) Other.InvManager.CreateInventory(class'KFInventory_Money'); KFInventoryManager(Other.InvManager).bInfiniteWeight = false; return true; } return false; } final function Pawn SpawnDefaultPawnFor(Controller NewPlayer, Actor StartSpot) // Clone of GameInfo one, but with Actor StartSpot. { local class<Pawn> PlayerClass; local Rotator R; local Pawn ResultPawn; PlayerClass = WorldInfo.Game.GetDefaultPlayerClass(NewPlayer); R.Yaw = StartSpot.Rotation.Yaw; ResultPawn = Spawn(PlayerClass,,,StartSpot.Location,R,,true); return ResultPawn; } final function bool RespawnPlayer( Controller NewPlayer ) { local KFPlayerReplicationInfo KFPRI; local KFPlayerController KFPC; local Actor startSpot; local int Idx; local array<SequenceObject> Events; local SeqEvent_PlayerSpawned SpawnedEvent; local LocalPlayer LP; if( NewPlayer.Pawn!=None ) NewPlayer.Pawn.Destroy(); // figure out the team number and find the start spot StartSpot = SpawnPointer.PickBestSpawn(); // if a start spot wasn't found, if (startSpot == None) { // check for a previously assigned spot if (NewPlayer.StartSpot != None) { StartSpot = NewPlayer.StartSpot; `warn("Player start not found, using last start spot"); } else { // otherwise abort `warn("Player start not found, failed to restart player"); return false; } } // try to create a pawn to use of the default class for this player NewPlayer.Pawn = SpawnDefaultPawnFor(NewPlayer, StartSpot); if (NewPlayer.Pawn == None) { NewPlayer.GotoState('Dead'); if ( PlayerController(NewPlayer) != None ) PlayerController(NewPlayer).ClientGotoState('Dead','Begin'); return false; } else { // initialize and start it up if( NavigationPoint(startSpot)!=None ) NewPlayer.Pawn.SetAnchor(NavigationPoint(startSpot)); if ( PlayerController(NewPlayer) != None ) { PlayerController(NewPlayer).TimeMargin = -0.1; if( NavigationPoint(startSpot)!=None ) NavigationPoint(startSpot).AnchoredPawn = None; // SetAnchor() will set this since IsHumanControlled() won't return true for the Pawn yet } NewPlayer.Pawn.LastStartSpot = PlayerStart(startSpot); NewPlayer.Pawn.LastStartTime = WorldInfo.TimeSeconds; NewPlayer.Possess(NewPlayer.Pawn, false); NewPlayer.Pawn.PlayTeleportEffect(true, true); NewPlayer.ClientSetRotation(NewPlayer.Pawn.Rotation, TRUE); if ( !WorldInfo.bNoDefaultInventoryForPlayer ) { AddPlayerSpecificInv(NewPlayer.Pawn); WorldInfo.Game.AddDefaultInventory(NewPlayer.Pawn); } WorldInfo.Game.SetPlayerDefaults(NewPlayer.Pawn); // activate spawned events if (WorldInfo.GetGameSequence() != None) { WorldInfo.GetGameSequence().FindSeqObjectsByClass(class'SeqEvent_PlayerSpawned',TRUE,Events); for (Idx = 0; Idx < Events.Length; Idx++) { SpawnedEvent = SeqEvent_PlayerSpawned(Events[Idx]); if (SpawnedEvent != None && SpawnedEvent.CheckActivate(NewPlayer,NewPlayer)) { SpawnedEvent.SpawnPoint = startSpot; SpawnedEvent.PopulateLinkedVariableValues(); } } } } KFPC = KFPlayerController(NewPlayer); KFPRI = KFPlayerReplicationInfo(NewPlayer.PlayerReplicationInfo); // To fix custom post processing chain when not running in editor or PIE. if (KFPC != none) { LP = LocalPlayer(KFPC.Player); if(LP != None) { LP.RemoveAllPostProcessingChains(); LP.InsertPostProcessingChain(LP.Outer.GetWorldPostProcessChain(),INDEX_NONE,true); if(KFPC.myHUD != None) { KFPC.myHUD.NotifyBindPostProcessEffects(); } } } KFGameInfo(WorldInfo.Game).SetTeam( NewPlayer, KFGameInfo(WorldInfo.Game).Teams[0] ); if( KFPC != none ) { // Initialize game play post process effects such as damage, low health, etc. KFPC.InitGameplayPostProcessFX(); } if( KFPRI!=None ) { if( KFPRI.Deaths == 0 ) KFPRI.Score = KFGameInfo(WorldInfo.Game).DifficultyInfo.GetAdjustedStartingCash(); KFPRI.PlayerHealth = NewPlayer.Pawn.Health; KFPRI.PlayerHealthPercent = FloatToByte( float(NewPlayer.Pawn.Health) / float(NewPlayer.Pawn.HealthMax) ); } return true; } function PlayerBuyStats( ExtPlayerController PC, class<Ext_PerkBase> Perk, int iStat, int Amount ) { local Ext_PerkBase P; local int i; if( bGameHasEnded ) return; P = PC.ActivePerkManager.FindPerk(Perk); if( P==None || !P.bPerkNetReady || iStat>=P.PerkStats.Length ) return; Amount = Min(Amount,P.PerkStats[iStat].MaxValue-P.PerkStats[iStat].CurrentValue); if( Amount<=0 ) return; i = Amount*P.PerkStats[iStat].CostPerValue; if( i>P.CurrentSP ) { Amount = P.CurrentSP/P.PerkStats[iStat].CostPerValue; if( Amount<=0 ) return; i = Amount*P.PerkStats[iStat].CostPerValue; } P.CurrentSP-=i; if( !P.IncrementStat(iStat,Amount) ) PC.ClientMessage("Failed to buy stat."); } function PlayerChangePerk( ExtPlayerController PC, class<Ext_PerkBase> NewPerk ) { if( bGameHasEnded ) return; if( NewPerk==PC.ActivePerkManager.CurrentPerk.Class ) { if( PC.PendingPerkClass!=None ) { PC.ClientMessage("You will remain the same perk now."); PC.PendingPerkClass = None; } } else if( PC.ActivePerkManager.CurrentPerk==None || KFPawn_Customization(PC.Pawn)!=None || (!PC.bSetPerk && KFGameReplicationInfo(WorldInfo.GRI).bTraderIsOpen) ) { if( PC.ActivePerkManager.ApplyPerkClass(NewPerk) ) { PC.ClientMessage("You have changed your perk to "$NewPerk.Default.PerkName); PC.bSetPerk = true; } else PC.ClientMessage("Invalid perk "$NewPerk.Default.PerkName); } else if( PC.bSetPerk ) PC.ClientMessage("Can only change perks once per wave"); else { PC.ClientMessage("You will change to perk '"$NewPerk.Default.PerkName$"' during trader time."); PC.PendingPerkClass = NewPerk; } } function CheckPerkChange( ExtPlayerController PC ) { if( PC.PendingPerkClass!=None ) { if( PC.ActivePerkManager.ApplyPerkClass(PC.PendingPerkClass) ) { PC.ClientMessage("You have changed your perk to "$PC.PendingPerkClass.Default.PerkName); PC.bSetPerk = true; } else PC.ClientMessage("Invalid perk "$PC.PendingPerkClass.Default.PerkName); PC.PendingPerkClass = None; } } function Tick(float DeltaTime) { local bool bCheckedWave; local ExtPlayerController ExtPC; if( KFGameReplicationInfo(WorldInfo.GRI).bTraderIsOpen && !bCheckedWave ) { foreach WorldInfo.AllControllers(class'ExtPlayerController',ExtPC) CheckPerkChange(ExtPC); bCheckedWave = true; } else if( bCheckedWave ) bCheckedWave = false; } function PlayerBoughtTrait( ExtPlayerController PC, class<Ext_PerkBase> PerkClass, class<Ext_TraitBase> Trait ) { local Ext_PerkBase P; local int i,cost; if( bGameHasEnded ) return; P = PC.ActivePerkManager.FindPerk(PerkClass); if( P==None || !P.bPerkNetReady ) return; for( i=0; i<P.PerkTraits.Length; ++i ) { if( P.PerkTraits[i].TraitType==Trait ) { if( P.PerkTraits[i].CurrentLevel>=Trait.Default.NumLevels ) return; cost = Trait.Static.GetTraitCost(P.PerkTraits[i].CurrentLevel); if( cost>P.CurrentSP || !Trait.Static.MeetsRequirements(P.PerkTraits[i].CurrentLevel,P) ) return; PC.ActivePerkManager.bStatsDirty = true; P.CurrentSP-=cost; P.bForceNetUpdate = true; ++P.PerkTraits[i].CurrentLevel; P.ClientReceiveTraitLvl(i,P.PerkTraits[i].CurrentLevel); if( P.PerkTraits[i].CurrentLevel==1 ) P.PerkTraits[i].Data = Trait.Static.InitializeFor(P,PC); if( PC.ActivePerkManager.CurrentPerk==P ) { Trait.Static.TraitDeActivate(P,P.PerkTraits[i].CurrentLevel-1,P.PerkTraits[i].Data); Trait.Static.TraitActivate(P,P.PerkTraits[i].CurrentLevel,P.PerkTraits[i].Data); if( KFPawn_Human(PC.Pawn)!=None ) { Trait.Static.CancelEffectOn(KFPawn_Human(PC.Pawn),P,P.PerkTraits[i].CurrentLevel-1,P.PerkTraits[i].Data); Trait.Static.ApplyEffectOn(KFPawn_Human(PC.Pawn),P,P.PerkTraits[i].CurrentLevel,P.PerkTraits[i].Data); } } break; } } } function PlayerUnloadInfo( ExtPlayerController PC, byte CallID, class<Ext_PerkBase> PerkClass, bool bUnload ) { local Ext_PerkBase P; local int LostExp,NewLvl; // Verify if client tries to cause errors. if( PC==None || PerkClass==None || PC.ActivePerkManager==None ) return; // Perk unloading disabled on this server. if( MinUnloadPerkLevel==-1 ) { if( !bUnload ) PC.ClientGotUnloadInfo(CallID,0); return; } P = PC.ActivePerkManager.FindPerk(PerkClass); if( P==None ) // More client hack attempts. return; if( P.CurrentLevel<MinUnloadPerkLevel ) // Verify minimum level. { if( !bUnload ) PC.ClientGotUnloadInfo(CallID,1,MinUnloadPerkLevel); return; } // Calc how much EXP is lost on this progress. LostExp = Round(float(P.CurrentEXP) * UnloadPerkExpCost); if( !bUnload ) { if( LostExp==0 ) // Generous server admin! PC.ClientGotUnloadInfo(CallID,2,0,0); else { // Calc how many levels are dropped. NewLvl = P.CalcLevelForExp(P.CurrentEXP-LostExp); PC.ClientGotUnloadInfo(CallID,2,LostExp,P.CurrentLevel-NewLvl); } return; } P.UnloadStats(); P.CurrentEXP -= LostExp; P.SetInitialLevel(); PC.ActivePerkManager.PRIOwner.SetLevelProgress(P.CurrentLevel,P.CurrentPrestige,P.MinimumLevel,P.MaximumLevel); if( PC.Pawn!=None ) PC.Pawn.Suicide(); } function ResetPlayerPerk( ExtPlayerController PC, class<Ext_PerkBase> PerkClass, bool bPrestige ) { local Ext_PerkBase P; if( bGameHasEnded ) return; P = PC.ActivePerkManager.FindPerk(PerkClass); if( P==None || !P.bPerkNetReady ) return; if( bPrestige ) { if( !P.CanPrestige() ) { PC.ClientMessage("Prestige for this perk is not allowed."); return; } ++P.CurrentPrestige; } P.FullReset(bPrestige); } function bool CheckReplacement(Actor Other) { if( bNoBoomstickJumping && KFWeap_Shotgun_DoubleBarrel(Other)!=None ) KFWeap_Shotgun_DoubleBarrel(Other).DoubleBarrelKickMomentum = 5.f; return true; } final function InitCustomChars( ExtPlayerReplicationInfo PRI ) { PRI.CustomCharList = CustomCharList; } final function bool HasPrivs( ExtPlayerReplicationInfo P ) { return WorldInfo.NetMode==NM_StandAlone || (P!=None && P.ShowAdminName() && (P.AdminType<=1 || P.AdminType==255)); } function AdminCommand( ExtPlayerController PC, int PlayerID, int Action ) { local ExtPlayerController E; local int i; if( bNoAdminCommands ) { PC.ClientMessage("Admin level commands are disabled.",'Priority'); return; } if( !HasPrivs(ExtPlayerReplicationInfo(PC.PlayerReplicationInfo)) ) { PC.ClientMessage("You do not have enough admin priveleges.",'Priority'); return; } foreach WorldInfo.AllControllers(class'ExtPlayerController',E) if( E.PlayerReplicationInfo.PlayerID==PlayerID ) break; if( E==None ) { PC.ClientMessage("Action failed, missing playerID: "$PlayerID,'Priority'); return; } if( Action>=100 ) // Set perk level. { if( E.ActivePerkManager.CurrentPerk==None ) { PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); return; } if( Action>=100000 ) // Set prestige level. { if( E.ActivePerkManager.CurrentPerk.MinLevelForPrestige<0 ) { PC.ClientMessage("Perk "$E.ActivePerkManager.CurrentPerk.Default.PerkName$" has prestige disabled!",'Priority'); return; } Action = Min(Action-100000,E.ActivePerkManager.CurrentPerk.MaxPrestige); E.ActivePerkManager.CurrentPerk.CurrentPrestige = Action; PC.ClientMessage("Set "$E.PlayerReplicationInfo.PlayerName$"' perk "$E.ActivePerkManager.CurrentPerk.Default.PerkName$" prestige level to "$Action,'Priority'); E.ActivePerkManager.CurrentPerk.FullReset(true); } else { Action = Clamp(Action-100,E.ActivePerkManager.CurrentPerk.MinimumLevel,E.ActivePerkManager.CurrentPerk.MaximumLevel); E.ActivePerkManager.CurrentPerk.CurrentEXP = E.ActivePerkManager.CurrentPerk.GetNeededExp(Action-1); PC.ClientMessage("Set "$E.PlayerReplicationInfo.PlayerName$"' perk "$E.ActivePerkManager.CurrentPerk.Default.PerkName$" level to "$Action,'Priority'); E.ActivePerkManager.CurrentPerk.SetInitialLevel(); E.ActivePerkManager.CurrentPerk.UpdatePRILevel(); } return; } switch( Action ) { case 0: // Reset ALL Stats for( i=0; i<E.ActivePerkManager.UserPerks.Length; ++i ) E.ActivePerkManager.UserPerks[i].FullReset(); PC.ClientMessage("Reset EVERY perk for "$E.PlayerReplicationInfo.PlayerName,'Priority'); break; case 1: // Reset Current Perk Stats if( E.ActivePerkManager.CurrentPerk!=None ) { E.ActivePerkManager.CurrentPerk.FullReset(); PC.ClientMessage("Reset perk "$E.ActivePerkManager.CurrentPerk.Default.PerkName$" for "$E.PlayerReplicationInfo.PlayerName,'Priority'); } else PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); break; case 2: // Add 1,000 XP case 3: // Add 10,000 XP case 4: // Advance Perk Level if( E.ActivePerkManager.CurrentPerk!=None ) { if( Action==2 ) i = 1000; else if( Action==3 ) i = 10000; else i = Max(E.ActivePerkManager.CurrentPerk.NextLevelEXP - E.ActivePerkManager.CurrentPerk.CurrentEXP,0); E.ActivePerkManager.EarnedEXP(i); PC.ClientMessage("Gave "$i$" XP for "$E.PlayerReplicationInfo.PlayerName,'Priority'); } else PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); break; case 5: // Unload all stats if( E.ActivePerkManager.CurrentPerk!=None ) { E.ActivePerkManager.CurrentPerk.UnloadStats(1); PC.ClientMessage("Unloaded all stats for "$E.PlayerReplicationInfo.PlayerName,'Priority'); } else PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); break; case 6: // Unload all traits if( E.ActivePerkManager.CurrentPerk!=None ) { E.ActivePerkManager.CurrentPerk.UnloadStats(2); PC.ClientMessage("Unloaded all traits for "$E.PlayerReplicationInfo.PlayerName,'Priority'); } else PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); break; case 7: // Remove 1,000 XP case 8: // Remove 10,000 XP if( E.ActivePerkManager.CurrentPerk!=None ) { if( Action==6 ) i = 1000; else i = 10000; E.ActivePerkManager.CurrentPerk.CurrentEXP = Max(E.ActivePerkManager.CurrentPerk.CurrentEXP-i,0); PC.ClientMessage("Removed "$i$" XP from "$E.PlayerReplicationInfo.PlayerName,'Priority'); } else PC.ClientMessage(E.PlayerReplicationInfo.PlayerName$" has no perk selected!!!",'Priority'); break; case 9: // Show Debug Info PC.ClientMessage("DEBUG info for "$E.PlayerReplicationInfo.PlayerName,'Priority'); PC.ClientMessage("PerkManager "$E.ActivePerkManager$" Current Perk: "$E.ActivePerkManager.CurrentPerk,'Priority'); PC.ClientMessage("Perks Count: "$E.ActivePerkManager.UserPerks.Length,'Priority'); for( i=0; i<E.ActivePerkManager.UserPerks.Length; ++i ) PC.ClientMessage("Perk "$i$": "$E.ActivePerkManager.UserPerks[i]$" XP:"$E.ActivePerkManager.UserPerks[i].CurrentEXP$" Lv:"$E.ActivePerkManager.UserPerks[i].CurrentLevel$" Rep:"$E.ActivePerkManager.UserPerks[i].bPerkNetReady,'Priority'); break; default: PC.ClientMessage("Unknown admin action.",'Priority'); } } function AdminSetMOTD( ExtPlayerController PC, string S ) { if( !HasPrivs(ExtPlayerReplicationInfo(PC.PlayerReplicationInfo)) ) return; ServerMOTD = S; SaveConfig(); PC.ClientMessage("Message of the Day updated.",'Priority'); } function PlayerChangeSpec( ExtPlayerController PC, bool bSpectator ) { if( bSpectator==PC.PlayerReplicationInfo.bOnlySpectator || PC.NextSpectateChange>WorldInfo.TimeSeconds ) return; PC.NextSpectateChange = WorldInfo.TimeSeconds+0.5; if( WorldInfo.Game.bGameEnded ) PC.ClientMessage("Can't change spectate mode after end-game."); else if( WorldInfo.Game.bWaitingToStartMatch ) PC.ClientMessage("Can't change spectate mode before game has started."); else if( WorldInfo.Game.AtCapacity(bSpectator,PC.PlayerReplicationInfo.UniqueId) ) PC.ClientMessage("Can't change spectate mode because game is at its maximum capacity."); else if( bSpectator ) { PC.NextSpectateChange = WorldInfo.TimeSeconds+2.5; if( PC.PlayerReplicationInfo.Team!=None ) PC.PlayerReplicationInfo.Team.RemoveFromTeam(PC); PC.PlayerReplicationInfo.bOnlySpectator = true; if( PC.Pawn!=None ) PC.Pawn.KilledBy(None); PC.Reset(); --WorldInfo.Game.NumPlayers; ++WorldInfo.Game.NumSpectators; WorldInfo.Game.Broadcast(PC,PC.PlayerReplicationInfo.GetHumanReadableName()@"became a spectator"); RemoveRespawn(PC); } else { PC.PlayerReplicationInfo.bOnlySpectator = false; if( !WorldInfo.Game.ChangeTeam(PC,WorldInfo.Game.PickTeam(0,PC,PC.PlayerReplicationInfo.UniqueId),false) ) { PC.PlayerReplicationInfo.bOnlySpectator = true; PC.ClientMessage("Can't become an active player, failed to set a team."); return; } PC.NextSpectateChange = WorldInfo.TimeSeconds+2.5; ++WorldInfo.Game.NumPlayers; --WorldInfo.Game.NumSpectators; PC.Reset(); WorldInfo.Game.Broadcast(PC,PC.PlayerReplicationInfo.GetHumanReadableName()@"became an active player"); if( bRespawnCheck ) CheckRespawn(PC); } } function bool GetNextItem( ExtPlayerReplicationInfo PRI, int RepIndex ) { if( RepIndex>=CustomItemList.Length ) return false; PRI.ClientAddTraderItem(class'KFGameReplicationInfo'.Default.TraderItems.SaleItems.Length+RepIndex,CustomItemList[RepIndex]); return true; } function InitWebAdmin( ExtWebAdmin_UI UI ) { local int i; UI.AddSettingsPage("Main Server Ext",Class,WebConfigs,WebAdminGetValue,WebAdminSetValue); for( i=0; i<LoadedPerks.Length; ++i ) LoadedPerks[i].Static.InitWebAdmin(UI); } function string WebAdminGetValue( name PropName, int ElementIndex ) { switch( PropName ) { case 'StatFileDir': return StatFileDir; case 'ForcedMaxPlayers': return string(ForcedMaxPlayers); case 'PlayerRespawnTime': return string(PlayerRespawnTime); case 'StatAutoSaveWaves': return string(StatAutoSaveWaves); case 'PostGameRespawnCost': return string(PostGameRespawnCost); case 'bKillMessages': return string(bKillMessages); case 'LargeMonsterHP': return string(LargeMonsterHP); case 'bDamageMessages': return string(bDamageMessages); case 'bEnableMapVote': return string(bEnableMapVote); case 'bNoBoomstickJumping': return string(bNoBoomstickJumping); case 'bNoAdminCommands': return string(bNoAdminCommands); case 'bDumpXMLStats': return string(bDumpXMLStats); case 'bRagdollFromFall': return string(bRagdollFromFall); case 'bRagdollFromMomentum': return string(bRagdollFromMomentum); case 'bRagdollFromBackhit': return string(bRagdollFromBackhit); case 'bAddCountryTags': return string(bAddCountryTags); case 'MaxTopPlayers': return string(MaxTopPlayers); case 'MinUnloadPerkLevel': return string(MinUnloadPerkLevel); case 'UnloadPerkExpCost': return string(UnloadPerkExpCost); case 'PerkClasses': return (ElementIndex==-1 ? string(PerkClasses.Length) : PerkClasses[ElementIndex]); case 'CustomChars': return (ElementIndex==-1 ? string(CustomChars.Length) : CustomChars[ElementIndex]); case 'AdminCommands': return (ElementIndex==-1 ? string(AdminCommands.Length) : AdminCommands[ElementIndex]); case 'CustomItems': return (ElementIndex==-1 ? string(CustomItems.Length) : CustomItems[ElementIndex]); case 'ServerMOTD': return Repl(ServerMOTD,"|",Chr(10)); case 'BonusGameSongs': return (ElementIndex==-1 ? string(BonusGameSongs.Length) : BonusGameSongs[ElementIndex]); case 'BonusGameFX': return (ElementIndex==-1 ? string(BonusGameFX.Length) : BonusGameFX[ElementIndex]); } } final function UpdateArray( out array<string> Ar, int Index, const out string Value ) { if( Value=="#DELETE" ) Ar.Remove(Index,1); else { if( Index>=Ar.Length ) Ar.Length = Index+1; Ar[Index] = Value; } } function WebAdminSetValue( name PropName, int ElementIndex, string Value ) { switch( PropName ) { case 'StatFileDir': StatFileDir = Value; break; case 'ForcedMaxPlayers': ForcedMaxPlayers = int(Value); break; case 'PlayerRespawnTime': PlayerRespawnTime = int(Value); break; case 'StatAutoSaveWaves': StatAutoSaveWaves = int(Value); break; case 'PostGameRespawnCost': PostGameRespawnCost = int(Value); break; case 'bKillMessages': bKillMessages = bool(Value); break; case 'LargeMonsterHP': LargeMonsterHP = int(Value); break; case 'MinUnloadPerkLevel': MinUnloadPerkLevel = int(Value); break; case 'UnloadPerkExpCost': UnloadPerkExpCost = float(Value); break; case 'bDamageMessages': bDamageMessages = bool(Value); break; case 'bEnableMapVote': bEnableMapVote = bool(Value); break; case 'bNoAdminCommands': bNoAdminCommands = bool(Value); break; case 'bDumpXMLStats': bDumpXMLStats = bool(Value); break; case 'bNoBoomstickJumping': bNoBoomstickJumping = bool(Value); break; case 'bRagdollFromFall': bRagdollFromFall = bool(Value); break; case 'bRagdollFromMomentum': bRagdollFromMomentum = bool(Value); break; case 'bRagdollFromBackhit': bRagdollFromBackhit = bool(Value); break; case 'bAddCountryTags': bAddCountryTags = bool(Value); break; case 'MaxTopPlayers': MaxTopPlayers = int(Value); break; case 'ServerMOTD': ServerMOTD = Repl(Value,Chr(13)$Chr(10),"|"); break; case 'PerkClasses': UpdateArray(PerkClasses,ElementIndex,Value); break; case 'CustomChars': UpdateArray(CustomChars,ElementIndex,Value); break; case 'AdminCommands': UpdateArray(AdminCommands,ElementIndex,Value); break; case 'CustomItems': UpdateArray(CustomItems,ElementIndex,Value); break; case 'BonusGameSongs': UpdateArray(BonusGameSongs,ElementIndex,Value); break; case 'BonusGameFX': UpdateArray(BonusGameFX,ElementIndex,Value); break; default: return; } SaveConfig(); } defaultproperties { DevList.Add("0x0110000100E8984E") DevList.Add("0x01100001023DF8A8") WebConfigs.Add((PropType=0,PropName="StatFileDir",UIName="Stat File Dir",UIDesc="Location of the stat files on the HDD (%s = unique player ID)")) WebConfigs.Add((PropType=0,PropName="ForcedMaxPlayers",UIName="Server Max Players",UIDesc="A forced max players value of the server (0 = use standard KF2 setting)")) WebConfigs.Add((PropType=0,PropName="PlayerRespawnTime",UIName="Respawn Time",UIDesc="Players respawn time in seconds after they die (0 = no respawning)")) WebConfigs.Add((PropType=0,PropName="PostGameRespawnCost",UIName="Post-Game Respawn Cost",UIDesc="Amount of dosh it'll cost to be respawned after end-game (only for custom gametypes that support this).")) WebConfigs.Add((PropType=0,PropName="StatAutoSaveWaves",UIName="Stat Auto-Save Waves",UIDesc="How often should stats be auto-saved (1 = every wave, 2 = every second wave etc)")) WebConfigs.Add((PropType=0,PropName="MinUnloadPerkLevel",UIName="Min Unload Perk Level",UIDesc="Minimum level a player should be on before they can use the perk stat unload (-1 = never).")) WebConfigs.Add((PropType=0,PropName="UnloadPerkExpCost",UIName="Perk Unload XP Cost",UIDesc="The percent of XP it costs for a player to use a perk unload (1 = all XP, 0 = none).")) WebConfigs.Add((PropType=1,PropName="bKillMessages",UIName="Show Kill Messages",UIDesc="Display on players HUD a kill counter every time they kill something")) WebConfigs.Add((PropType=0,PropName="LargeMonsterHP",UIName="Large Monster HP",UIDesc="If the enemy kill a monster with more HP then this, broadcast kill message to everyone")) WebConfigs.Add((PropType=1,PropName="bDamageMessages",UIName="Show Damage Messages",UIDesc="Display on players HUD a damage counter every time they damage an enemy")) WebConfigs.Add((PropType=1,PropName="bEnableMapVote",UIName="Enable MapVote",UIDesc="Enable MapVote X on this server")) WebConfigs.Add((PropType=1,PropName="bNoBoomstickJumping",UIName="No Boomstick Jumps",UIDesc="Disable boomstick knockback, so people can't glitch with it on maps")) WebConfigs.Add((PropType=1,PropName="bNoAdminCommands",UIName="Disable Admin menu",UIDesc="Disable admin menu commands so admins can't modify XP or levels of players")) WebConfigs.Add((PropType=1,PropName="bDumpXMLStats",UIName="Dump XML stats",UIDesc="Dump XML stat files for some external stat loggers")) WebConfigs.Add((PropType=1,PropName="bRagdollFromFall",UIName="Ragdoll From Fall",UIDesc="Make players ragdoll if they fall from a high place")) WebConfigs.Add((PropType=1,PropName="bRagdollFromMomentum",UIName="Ragdoll From Momentum",UIDesc="Make players ragdoll if they take a damage with high momentum transfer")) WebConfigs.Add((PropType=1,PropName="bRagdollFromBackhit",UIName="Ragdoll From Backhit",UIDesc="Make players ragdoll if they take a big hit to their back")) WebConfigs.Add((PropType=1,PropName="bAddCountryTags",UIName="Add Country Tags",UIDesc="Add player country tags to their names")) WebConfigs.Add((PropType=0,PropName="MaxTopPlayers",UIName="Max top players",UIDesc="Maximum top players to broadcast of and to keep track of.")) WebConfigs.Add((PropType=2,PropName="PerkClasses",UIName="Perk Classes",UIDesc="List of RPG perks players can play as (careful with removing them, because any perks removed will permanently delete the gained XP for every player for that perk)!",NumElements=-1)) WebConfigs.Add((PropType=2,PropName="CustomChars",UIName="Custom Chars",UIDesc="List of custom characters for this server (prefix with * to mark as admin character).",NumElements=-1)) WebConfigs.Add((PropType=2,PropName="AdminCommands",UIName="Admin Commands",UIDesc="List of Admin commands to show on scoreboard UI for admins (use : to split actual command with display name for the command)",NumElements=-1)) WebConfigs.Add((PropType=2,PropName="CustomItems",UIName="Custom Inventory",UIDesc="List of custom inventory to add to trader (must be KFWeaponDefinition class).",NumElements=-1)) WebConfigs.Add((PropType=3,PropName="ServerMOTD",UIName="MOTD",UIDesc="Message of the Day")) WebConfigs.Add((PropType=2,PropName="BonusGameSongs",UIName="Bonus Game Songs",UIDesc="List of custom musics to play during level change pong game.",NumElements=-1)) WebConfigs.Add((PropType=2,PropName="BonusGameFX",UIName="Bonus Game FX",UIDesc="List of custom FX to play on pong game.",NumElements=-1)) }