1
0

first version

This commit is contained in:
2022-08-11 07:44:13 +03:00
parent 3c7db64ca5
commit 158c2b38a3
29 changed files with 2428 additions and 1 deletions

103
CVC/Classes/BaseVote.uc Normal file
View File

@ -0,0 +1,103 @@
class BaseVote extends Object
config(CVC)
abstract;
var public config String PositiveColorHex;
var public config String NegativeColorHex;
var public config bool bChatNotifications;
var public config bool bHudNotifications;
var public config float DefferedClearHUD;
public static function InitConfig(int Version, int LatestVersion, E_LogLevel LogLevel)
{
`Log_TraceStatic();
switch (Version)
{
case `NO_CONFIG:
ApplyDefault(LogLevel);
default: break;
}
if (LatestVersion != Version)
{
StaticSaveConfig();
}
}
public static function Load(E_LogLevel LogLevel)
{
`Log_TraceStatic();
if (!IsValidHexColor(default.PositiveColorHex, LogLevel))
{
`Log_Error("PositiveColorHex" @ "(" $ default.PositiveColorHex $ ")" @ "is not valid hex color");
default.PositiveColorHex = class'KFLocalMessage'.default.EventColor;
}
if (!IsValidHexColor(default.NegativeColorHex, LogLevel))
{
`Log_Error("NegativeColorHex" @ "(" $ default.NegativeColorHex $ ")" @ "is not valid hex color");
default.NegativeColorHex = class'KFLocalMessage'.default.InteractionColor;
}
if (default.DefferedClearHUD < 0)
{
`Log_Error("DefferedClearHUD" @ "(" $ default.DefferedClearHUD $ ")" @ "must be greater than 0");
default.DefferedClearHUD = 0.0f;
}
}
protected static function ApplyDefault(E_LogLevel LogLevel)
{
`Log_TraceStatic();
default.bChatNotifications = true;
default.bHudNotifications = true;
default.PositiveColorHex = class'KFLocalMessage'.default.EventColor;
default.NegativeColorHex = class'KFLocalMessage'.default.InteractionColor;
default.DefferedClearHUD = 1.0f;
}
protected static function bool IsValidHexColor(String HexColor, E_LogLevel LogLevel)
{
local byte Index;
`Log_TraceStatic();
if (len(HexColor) != 6) return false;
HexColor = Locs(HexColor);
for (Index = 0; Index < 6; ++Index)
{
switch (Mid(HexColor, Index, 1))
{
case "0": break;
case "1": break;
case "2": break;
case "3": break;
case "4": break;
case "5": break;
case "6": break;
case "7": break;
case "8": break;
case "9": break;
case "a": break;
case "b": break;
case "c": break;
case "d": break;
case "e": break;
case "f": break;
default: return false;
}
}
return true;
}
defaultproperties
{
}

387
CVC/Classes/CVC.uc Normal file
View File

@ -0,0 +1,387 @@
class CVC extends Info
dependson(CVC_LocalMessage)
config(CVC);
const LatestVersion = 1;
const CfgKickProtected = class'KickProtected';
const CfgKickVote = class'KickVote';
const CfgStartWaveKickProtection = class'StartWaveKickProtection';
const CfgSkipTraderVote = class'SkipTraderVote';
const CfgPauseVote = class'PauseVote';
const CfgMapStat = class'MapStat';
const CfgMapVote = class'MapVote';
var private config int Version;
var private config E_LogLevel LogLevel;
var private KFGameInfo KFGI;
var private KFGameInfo_Survival KFGIS;
var private KFGameReplicationInfo KFGRI;
var private Array<UniqueNetId> KickProtectedPlayers;
var private Array<CVC_RepInfo> RepInfos;
public simulated function bool SafeDestroy()
{
`Log_Trace();
return (bPendingDelete || bDeleteMe || Destroy());
}
public event PreBeginPlay()
{
`Log_Trace();
if (WorldInfo.NetMode == NM_Client)
{
`Log_Fatal("NetMode == NM_Client, Destroy...");
SafeDestroy();
return;
}
Super.PreBeginPlay();
PreInit();
}
public event PostBeginPlay()
{
`Log_Trace();
if (bPendingDelete || bDeleteMe) return;
Super.PostBeginPlay();
PostInit();
}
private function PreInit()
{
`Log_Trace();
if (Version == `NO_CONFIG)
{
LogLevel = LL_Info;
SaveConfig();
}
CfgMapStat.static.InitConfig(Version, LatestVersion, LogLevel);
CfgMapVote.static.InitConfig(Version, LatestVersion, LogLevel);
CfgSkipTraderVote.static.InitConfig(Version, LatestVersion, LogLevel);
CfgPauseVote.static.InitConfig(Version, LatestVersion, LogLevel);
CfgKickVote.static.InitConfig(Version, LatestVersion, LogLevel);
CfgKickProtected.static.InitConfig(Version, LatestVersion, LogLevel);
CfgStartWaveKickProtection.static.InitConfig(Version, LatestVersion, LogLevel);
switch (Version)
{
case `NO_CONFIG:
`Log_Info("Config created");
case MaxInt:
`Log_Info("Config updated to version"@LatestVersion);
break;
case LatestVersion:
`Log_Info("Config is up-to-date");
break;
default:
`Log_Warn("The config version is higher than the current version (are you using an old mutator?)");
`Log_Warn("Config version is" @ Version @ "but current version is" @ LatestVersion);
`Log_Warn("The config version will be changed to" @ LatestVersion);
break;
}
if (LatestVersion != Version)
{
Version = LatestVersion;
SaveConfig();
}
if (LogLevel == LL_WrongLevel)
{
LogLevel = LL_Info;
`Log_Warn("Wrong 'LogLevel', return to default value");
SaveConfig();
}
`Log_Base("LogLevel:" @ LogLevel);
CfgKickVote.static.Load(LogLevel);
CfgSkipTraderVote.static.Load(LogLevel);
CfgPauseVote.static.Load(LogLevel);
CfgMapStat.static.Load(LogLevel);
CfgMapVote.static.Load(LogLevel);
CfgStartWaveKickProtection.static.Load(LogLevel);
KickProtectedPlayers = CfgKickProtected.static.Load(LogLevel);
}
private function PostInit()
{
`Log_Trace();
if (WorldInfo == None || WorldInfo.Game == None)
{
SetTimer(1.0f, false, nameof(PostInit));
return;
}
KFGI = KFGameInfo(WorldInfo.Game);
if (KFGI == None)
{
`Log_Fatal("Incompatible gamemode:" @ WorldInfo.Game);
SafeDestroy();
return;
}
KFGIS = KFGameInfo_Survival(KFGI);
if (KFGIS == None)
{
`Log_Warn("Unknown gamemode (" $ KFGI $ "), KickProtectionStartWaves disabled");
CfgStartWaveKickProtection.default.Waves = 0;
}
if (KFGI.GameReplicationInfo == None)
{
SetTimer(1.0f, false, nameof(PostInit));
return;
}
KFGRI = KFGameReplicationInfo(KFGI.GameReplicationInfo);
if (KFGRI == None)
{
`Log_Fatal("Incompatible Replication info:" @ KFGI.GameReplicationInfo);
SafeDestroy();
return;
}
KFGRI.VoteCollectorClass = class'CVC_VoteCollector';
KFGRI.VoteCollector = new(KFGRI) KFGRI.VoteCollectorClass;
if (KFGRI.VoteCollector == None)
{
`Log_Fatal("Can't replace VoteCollector!");
SafeDestroy();
return;
}
else
{
CVC_VoteCollector(KFGRI.VoteCollector).CVC = Self;
CVC_VoteCollector(KFGRI.VoteCollector).LogLevel = LogLevel;
`Log_Info("VoteCollector replaced");
}
}
public function bool PlayerIsKickProtected(PlayerReplicationInfo PRI)
{
`Log_Trace();
return (KickProtectedPlayers.Find('Uid', PRI.UniqueId.Uid) != INDEX_NONE);
}
public function bool PlayerIsStartWaveKickProtected(KFPlayerController KFPC)
{
`Log_Trace();
return (PlayerOnStartWave(KFPC) && PlayerHasRequiredLevel(KFPC));
}
private function bool PlayerOnStartWave(KFPlayerController KFPC)
{
local CVC_RepInfo RepInfo;
`Log_Trace();
if (KFGIS != None && CfgStartWaveKickProtection.default.Waves != 0)
{
foreach RepInfos(RepInfo)
{
if (RepInfo.GetKFPC() == KFPC)
{
return (RepInfo.StartWave + CfgStartWaveKickProtection.default.Waves >= KFGIS.WaveNum);
}
}
}
return false;
}
private function bool PlayerHasRequiredLevel(KFPlayerController KFPC)
{
local KFPlayerReplicationInfo KFPRI;
`Log_Trace();
KFPRI = KFPlayerReplicationInfo(KFPC.PlayerReplicationInfo);
if (KFPRI == None)
{
return true;
}
return (KFPRI.GetActivePerkLevel() >= CfgStartWaveKickProtection.default.MinLevel);
}
public function bool PlayerCanKickVote(KFPlayerController KFPC, optional KFPlayerController KFPC_Kickee)
{
`Log_Trace();
if (KFPC_Kickee == None)
{
KFPC_Kickee = KFPlayerController(KFGRI.VoteCollector.CurrentKickVote.PlayerPRI.Owner);
}
if (KFPC_Kickee != None)
{
if (KFPC == KFPC_Kickee)
{
return false; // kickee cant vote
}
if (!PlayerHasRequiredLevel(KFPC_Kickee))
{
return true; // always can vote for players without req level
}
}
return !PlayerOnStartWave(KFPC);
}
public function BroadcastChatLocalized(E_CVC_LocalMessageType LMT, optional String HexColor, optional KFPlayerController ExceptKFPC = None, optional String String1, optional String String2)
{
local CVC_RepInfo RepInfo;
`Log_Trace();
foreach RepInfos(RepInfo)
{
if (RepInfo.GetKFPC() != ExceptKFPC)
{
RepInfo.WriteToChatLocalized(LMT, HexColor, String1, String2);
}
}
}
public function BroadcastHUDLocalized(E_CVC_LocalMessageType LMT, optional float DisplayTime = 0.0f, optional String String1, optional String String2, optional String String3)
{
local CVC_RepInfo RepInfo;
`Log_Trace();
foreach RepInfos(RepInfo)
{
if (RepInfo.GetKFPC() != None)
{
RepInfo.WriteToHUDLocalized(LMT, String1, String2, String3, DisplayTime);
}
}
}
public function BroadcastClearMessageHUD(optional float DefferedTime = 0.0f)
{
local CVC_RepInfo RepInfo;
`Log_Trace();
foreach RepInfos(RepInfo)
{
if (RepInfo.GetKFPC() != None)
{
RepInfo.DefferedClearMessageHUD(DefferedTime);
}
}
}
public function WriteToChatLocalized(KFPlayerController KFPC, E_CVC_LocalMessageType LMT, optional String HexColor, optional String String1, optional String String2)
{
local CVC_RepInfo RepInfo;
`Log_Trace();
foreach RepInfos(RepInfo)
{
if (RepInfo.GetKFPC() == KFPC)
{
RepInfo.WriteToChatLocalized(LMT, HexColor, String1, String2);
return;
}
}
}
public function WriteToHUDLocalized(KFPlayerController KFPC, E_CVC_LocalMessageType LMT, optional float DisplayTime = 0.0f, optional String String1, optional String String2, optional String String3)
{
local CVC_RepInfo RepInfo;
`Log_Trace();
foreach RepInfos(RepInfo)
{
if (RepInfo.GetKFPC() == KFPC)
{
RepInfo.WriteToHUDLocalized(LMT, String1, String2, String3, DisplayTime);
return;
}
}
}
public function NotifyLogin(Controller C)
{
`Log_Trace();
CreateRepInfo(C);
}
public function NotifyLogout(Controller C)
{
`Log_Trace();
DestroyRepInfo(C);
}
public function bool CreateRepInfo(Controller C)
{
local CVC_RepInfo RepInfo;
`Log_Trace();
if (C == None) return false;
RepInfo = Spawn(class'CVC_RepInfo', C);
if (RepInfo == None) return false;
RepInfo.CVC = Self;
RepInfo.LogLevel = LogLevel;
RepInfo.StartWave = ((KFGIS != None) ? KFGIS.WaveNum : 0);
RepInfos.AddItem(RepInfo);
return true;
}
public function bool DestroyRepInfo(Controller C)
{
local CVC_RepInfo RepInfo;
`Log_Trace();
if (C == None) return false;
foreach RepInfos(RepInfo)
{
if (RepInfo.Owner == C)
{
RepInfo.SafeDestroy();
RepInfos.RemoveItem(RepInfo);
return true;
}
}
return false;
}
DefaultProperties
{
}

4
CVC/Classes/CVC.upkg Normal file
View File

@ -0,0 +1,4 @@
[Flags]
AllowDownload=True
ClientOptional=False
ServerSideOnly=False

62
CVC/Classes/CVCMut.uc Normal file
View File

@ -0,0 +1,62 @@
class CVCMut extends KFMutator;
var private CVC CVC;
public simulated function bool SafeDestroy()
{
return (bPendingDelete || bDeleteMe || Destroy());
}
public event PreBeginPlay()
{
Super.PreBeginPlay();
if (WorldInfo.NetMode == NM_Client) return;
foreach WorldInfo.DynamicActors(class'CVC', CVC)
{
`Log_Base("Found 'CVC'");
break;
}
if (CVC == None)
{
`Log_Base("Spawn 'CVC'");
CVC = WorldInfo.Spawn(class'CVC');
}
if (CVC == None)
{
`Log_Base("Can't Spawn 'CVC', Destroy...");
SafeDestroy();
}
}
public function AddMutator(Mutator Mut)
{
if (Mut == Self) return;
if (Mut.Class == Class)
Mut.Destroy();
else
Super.AddMutator(Mut);
}
public function NotifyLogin(Controller C)
{
Super.NotifyLogin(C);
CVC.NotifyLogin(C);
}
public function NotifyLogout(Controller C)
{
Super.NotifyLogout(C);
CVC.NotifyLogout(C);
}
DefaultProperties
{
}

View File

@ -0,0 +1,157 @@
class CVC_LocalMessage extends Object
abstract;
var const String PlayerIsKickProtectedDefault;
var private localized String PlayerIsKickProtected;
var const String PlayerIsStartWaveKickProtectedDefault;
var private localized String PlayerIsStartWaveKickProtected;
var const String PlayerCantVoteDefault;
var private localized String PlayerCantVote;
var const String KickVoteNotEnoughPlayersDefault;
var private localized String KickVoteNotEnoughPlayers;
var const String KickVoteStartedDefault;
var private localized String KickVoteStarted;
var const String KickVoteStartedForPlayerDefault;
var private localized String KickVoteStartedForPlayer;
var const String KickVoteNotStartedForPlayerDefault;
var private localized String KickVoteNotStartedForPlayer;
var const String VotedPlayersDefault;
var private localized String VotedPlayers;
var const String DidntVotePlayersDefault;
var private localized String DidntVotePlayers;
// TODO:
/*
Kick vote hud:
start vote + only yes votes:
header: <player vote for player>
second line: yes votes
pause and skip:
first line: voted players
second line: dont voted players
*/
enum E_CVC_LocalMessageType
{
CVC_PlayerIsKickProtected,
CVC_PlayerIsStartWaveKickProtected,
CVC_PlayerCantVote,
CVC_KickVoteNotEnoughPlayers,
CVC_KickVoteStarted,
CVC_KickVoteStartedForPlayer,
CVC_KickVoteNotStartedForPlayer,
CVC_KickVoteYesReceived,
CVC_KickVoteNoReceived,
CVC_KickVoteStartedHUD,
CVC_KickVoteReceivedHUD,
CVC_SkipVoteYesReceived,
CVC_SkipVoteNoReceived,
CVC_PauseVoteYesReceived,
CVC_PauseVoteNoReceived,
CVC_VoteProgressHUD,
};
private static function String ReplKicker(String Str, String Kicker)
{
return Repl(Str, "<kicker>", Kicker, false);
}
private static function String ReplKickee(String Str, String Kickee)
{
return Repl(Str, "<kickee>", Kickee, false);
}
private static function String ReplWaves(String Str, String Waves)
{
return Repl(Str, "<waves>", Waves, false);
}
public static function String GetLocalizedString(
E_LogLevel LogLevel,
E_CVC_LocalMessageType LMT,
optional String String1,
optional String String2,
optional String String3)
{
`Log_TraceStatic();
switch (LMT)
{
case CVC_PlayerIsKickProtected:
return ReplKickee(default.PlayerIsKickProtected != "" ? default.PlayerIsKickProtected : default.PlayerIsKickProtectedDefault, String1);
case CVC_PlayerIsStartWaveKickProtected:
return ReplWaves(ReplKickee(default.PlayerIsStartWaveKickProtected != "" ? default.PlayerIsStartWaveKickProtected : default.PlayerIsStartWaveKickProtectedDefault, String1), String2);
case CVC_PlayerCantVote:
return ReplWaves(default.PlayerCantVote != "" ? default.PlayerCantVote : default.PlayerCantVoteDefault, String1);
case CVC_KickVoteNotEnoughPlayers:
return ReplWaves(default.KickVoteNotEnoughPlayers != "" ? default.KickVoteNotEnoughPlayers : default.KickVoteNotEnoughPlayersDefault, String1);
case CVC_KickVoteYesReceived:
return (String1 $ ":" @ class'KFCommon_LocalizedStrings'.default.YesString);
case CVC_KickVoteNoReceived:
return (String1 $ ":" @ class'KFCommon_LocalizedStrings'.default.NoString);
case CVC_KickVoteStartedHUD:
return ReplKickee(ReplKicker((default.KickVoteStarted != "" ? default.KickVoteStarted : default.KickVoteStartedDefault), String1), String2) $ "\n" $ class'KFCommon_LocalizedStrings'.default.YesString $ ":" @ String3;
case CVC_KickVoteReceivedHUD:
return class'KFCommon_LocalizedStrings'.default.YesString $ ":" @ String1 $ "\n" $ class'KFCommon_LocalizedStrings'.default.NoString $ ":" @ String2;
case CVC_KickVoteStarted:
return ReplKickee(ReplKicker((default.KickVoteStarted != "" ? default.KickVoteStarted : default.KickVoteStartedDefault), String1), String2);
case CVC_KickVoteStartedForPlayer:
return ReplKicker((default.KickVoteStartedForPlayer != "" ? default.KickVoteStartedForPlayer : default.KickVoteStartedForPlayerDefault), String1);
case CVC_KickVoteNotStartedForPlayer:
return ReplKicker((default.KickVoteNotStartedForPlayer != "" ? default.KickVoteNotStartedForPlayer : default.KickVoteNotStartedForPlayerDefault), String1);
case CVC_SkipVoteYesReceived:
return (String1 $ ":" @ class'KFCommon_LocalizedStrings'.default.YesString);
case CVC_SkipVoteNoReceived:
return (String1 $ ":" @ class'KFCommon_LocalizedStrings'.default.NoString);
case CVC_PauseVoteYesReceived:
return (String1 $ ":" @ class'KFCommon_LocalizedStrings'.default.YesString);
case CVC_PauseVoteNoReceived:
return (String1 $ ":" @ class'KFCommon_LocalizedStrings'.default.NoString);
case CVC_VoteProgressHUD:
return (default.VotedPlayers != "" ? default.VotedPlayers : default.VotedPlayersDefault) @ String1 $ (String2 != "" ? ("\n" $ (default.DidntVotePlayers != "" ? default.DidntVotePlayers : default.DidntVotePlayersDefault) @ String2) : "");
}
return "";
}
defaultproperties
{
PlayerIsKickProtectedDefault = "<kickee> is protected from kick"
PlayerIsStartWaveKickProtectedDefault = "You can't kick <kickee> right now. He can be kicked when he plays at least <waves> wave(s)"
PlayerCantVoteDefault = "You can't vote for kick now. You can vote when you play at least <waves> wave(s)"
KickVoteNotEnoughPlayersDefault = "Not enough players to start vote (only players who have played at least <waves> wave(s) can vote)"
KickVoteStartedDefault = "<kicker> has started a vote to kick <kickee>"
KickVoteStartedForPlayerDefault = "<kicker> started voting to kick you"
KickVoteNotStartedForPlayerDefault = "<kicker> tried to kick you"
VotedPlayersDefault = "Voted:"
DidntVotePlayersDefault = "Didn't vote:"
}

123
CVC/Classes/CVC_RepInfo.uc Normal file
View File

@ -0,0 +1,123 @@
class CVC_RepInfo extends ReplicationInfo;
const CVCLMT = class'CVC_LocalMessage';
var public E_LogLevel LogLevel;
var public CVC CVC;
var public int StartWave;
var private KFPlayerController KFPC;
replication
{
if (bNetInitial && Role == ROLE_Authority)
LogLevel;
}
public simulated function bool SafeDestroy()
{
`Log_Trace();
return (bPendingDelete || bDeleteMe || Destroy());
}
public reliable client function WriteToChatLocalized(E_CVC_LocalMessageType LMT, optional String HexColor, optional String String1, optional String String2, optional String String3)
{
`Log_Trace();
WriteToChat(CVCLMT.static.GetLocalizedString(LogLevel, LMT, String1, String2, String3), HexColor);
}
public reliable client function WriteToChat(String Message, optional String HexColor)
{
local KFGFxHudWrapper HUD;
`Log_Trace();
if (GetKFPC() == None) return;
if (KFPC.MyGFxManager.PartyWidget != None && KFPC.MyGFxManager.PartyWidget.PartyChatWidget != None)
{
KFPC.MyGFxManager.PartyWidget.PartyChatWidget.AddChatMessage(Message, HexColor);
}
HUD = KFGFxHudWrapper(KFPC.myHUD);
if (HUD != None && HUD.HUDMovie != None && HUD.HUDMovie.HudChatBox != None)
{
HUD.HUDMovie.HudChatBox.AddChatMessage(Message, HexColor);
}
}
public reliable client function WriteToHUDLocalized(E_CVC_LocalMessageType LMT, optional String String1, optional String String2, optional String String3, optional float DisplayTime = 0.0f)
{
`Log_Trace();
WriteToHUD(CVCLMT.static.GetLocalizedString(LogLevel, LMT, String1, String2, String3), DisplayTime);
}
public reliable client function WriteToHUD(String Message, optional float DisplayTime = 0.0f)
{
`Log_Trace();
if (GetKFPC() == None) return;
if (DisplayTime == 0.0f)
{
DisplayTime = CalcDisplayTime(Message);
}
if (KFPC.MyGFxHUD != None)
{
KFPC.MyGFxHUD.DisplayMapText(Message, DisplayTime, false);
}
}
public reliable client function DefferedClearMessageHUD(optional float Time = 0.0f)
{
`Log_Trace();
SetTimer(Time, false, nameof(ClearMessageHUD));
}
public reliable client function ClearMessageHUD()
{
`Log_Trace();
if (GetKFPC() == None) return;
if (KFPC.MyGFxHUD != None && KFPC.MyGFxHUD.MapTextWidget != None)
{
KFPC.MyGFxHUD.MapTextWidget.StoredMessageList.Length = 0;
KFPC.MyGFxHUD.MapTextWidget.HideMessage();
}
}
private function float CalcDisplayTime(String Message)
{
`Log_Trace();
return FClamp(Len(Message) / 20.0f, 3, 30);
}
public simulated function KFPlayerController GetKFPC()
{
`Log_Trace();
if (KFPC != None) return KFPC;
KFPC = KFPlayerController(Owner);
if (KFPC == None && ROLE < ROLE_Authority)
{
KFPC = KFPlayerController(GetALocalPlayerController());
}
return KFPC;
}
defaultproperties
{
bAlwaysRelevant = false
bOnlyRelevantToOwner = true
bSkipActorPropertyReplication = false
}

View File

@ -0,0 +1,918 @@
class CVC_VoteCollector extends KFVoteCollector;
const CfgKickProtected = class'KickProtected';
const CfgKickVote = class'KickVote';
const CfgStartWaveKickProtection = class'StartWaveKickProtection';
const CfgSkipTraderVote = class'SkipTraderVote';
const CfgPauseVote = class'PauseVote';
const CfgMapStat = class'MapStat';
const CfgMapStats = class'MapStats';
const CfgMapVote = class'MapVote';
struct S_KickVote
{
var String Name;
var String SteamID;
var String UniqueID;
var bool VoteYes;
var class<KFPerk> Perk;
var byte Level;
};
// KickVotes[0]: Kickee
// KickVotes[1]: Kicker
// KickVotes[2...]: Voters
var private Array<S_KickVote> KickVotes;
var public CVC CVC;
var public E_LogLevel LogLevel;
var private KFPlayerController KFPC_Kicker;
var private KFPlayerController KFPC_Kickee;
var private String KickerName;
var private String KickeeName;
var private String YesVotesPlayers, NoVotesPlayers;
var private bool AllowHudNotification;
replication
{
if (bNetInitial && Role == ROLE_Authority)
LogLevel;
}
public function ServerStartVoteKick(PlayerReplicationInfo PRI_Kickee, PlayerReplicationInfo PRI_Kicker)
{
local Array<KFPlayerReplicationInfo> KFPRIs;
local KFPlayerReplicationInfo KFPRI;
local KFPlayerController KFPC;
local KFGameInfo KFGI;
`Log_Trace();
KFGI = KFGameInfo(WorldInfo.Game);
KFPC_Kicker = KFPlayerController(PRI_Kicker.Owner);
KFPC_Kickee = KFPlayerController(PRI_Kickee.Owner);
KickerName = PRI_Kicker.PlayerName;
KickeeName = PRI_Kickee.PlayerName;
if (KFGI.bDisableKickVote)
{
KFPC_Kicker.ReceiveLocalizedMessage(class'KFLocalMessage', LMT_KickVoteDisabled);
return;
}
if (PRI_Kicker.bOnlySpectator)
{
KFPC_Kicker.ReceiveLocalizedMessage(class'KFLocalMessage', LMT_KickVoteNoSpectators);
return;
}
if (!CVC.PlayerCanKickVote(KFPC_Kicker, KFPC_Kickee))
{
CVC.WriteToChatLocalized(
KFPC_Kicker,
CVC_PlayerCantVote,
CfgKickVote.default.WarningColorHex,
String(CfgStartWaveKickProtection.default.Waves));
return;
}
if (CVC.PlayerIsStartWaveKickProtected(KFPC_Kickee))
{
CVC.WriteToChatLocalized(
KFPC_Kicker,
CVC_PlayerIsStartWaveKickProtected,
CfgKickVote.default.WarningColorHex,
KickeeName,
String(CfgStartWaveKickProtection.default.Waves));
return;
}
if (VotingPlayers(PRI_Kickee) < CfgKickVote.default.MinVotingPlayersToStartKickVote)
{
if (CfgStartWaveKickProtection.default.Waves == 0)
{
KFPC_Kicker.ReceiveLocalizedMessage(class'KFLocalMessage', LMT_KickVoteNotEnoughPlayers);
}
else
{
CVC.WriteToChatLocalized(
KFPC_Kicker,
CVC_KickVoteNotEnoughPlayers,
CfgKickVote.default.WarningColorHex,
String(CfgStartWaveKickProtection.default.Waves));
}
return;
}
if (KickedPlayers >= CfgKickVote.default.MaxKicks)
{
KFPC_Kicker.ReceiveLocalizedMessage(class'KFLocalMessage', LMT_KickVoteMaxKicksReached);
return;
}
if (CVC.PlayerIsKickProtected(PRI_Kickee))
{
CVC.WriteToChatLocalized(
KFPC_Kicker,
CVC_PlayerIsKickProtected,
CfgKickVote.default.WarningColorHex,
KickeeName);
if (CfgKickProtected.default.NotifyPlayerAboutKickAttempt)
{
CVC.WriteToChatLocalized(
KFPC_Kickee,
CVC_KickVoteNotStartedForPlayer,
CfgKickVote.default.WarningColorHex,
KickerName);
}
return;
}
if (KFGI.AccessControl != None && KFGI.AccessControl.IsAdmin(KFPC_Kickee))
{
KFPC_Kicker.ReceiveLocalizedMessage(class'KFLocalMessage', LMT_KickVoteAdmin);
return;
}
if (bIsFailedVoteTimerActive)
{
KFPC_Kicker.ReceiveLocalizedMessage(class'KFLocalMessage', LMT_KickVoteRejected);
return;
}
if (bIsSkipTraderVoteInProgress || bIsPauseGameVoteInProgress)
{
KFPC_Kicker.ReceiveLocalizedMessage(class'KFLocalMessage', LMT_OtherVoteInProgress);
return;
}
if (!bIsKickVoteInProgress)
{
PlayersThatHaveVoted.Length = 0;
CurrentKickVote.PlayerID = PRI_Kickee.UniqueId;
CurrentKickVote.PlayerPRI = PRI_Kickee;
CurrentKickVote.PlayerIPAddress = KFPC_Kickee.GetPlayerNetworkAddress();
bIsKickVoteInProgress = true;
GetKFPRIArray(KFPRIs);
foreach KFPRIs(KFPRI)
{
KFPRI.ShowKickVote(PRI_Kickee, VoteTime, !(KFPRI == PRI_Kicker || KFPRI == PRI_Kickee || !CVC.PlayerCanKickVote(KFPlayerController(KFPRI.Owner))));
}
if (CfgKickVote.default.bChatNotifications)
{
CVC.BroadcastChatLocalized(
CVC_KickVoteStarted,
CfgKickVote.default.PositiveColorHex,
KFPC_Kickee,
KickerName,
KickeeName);
CVC.WriteToChatLocalized(
KFPC_Kickee,
CVC_KickVoteStartedForPlayer,
CfgKickVote.default.NegativeColorHex,
KickerName,
KickeeName);
foreach KFPRIs(KFPRI)
{
if (KFPRI == PRI_Kickee)
{
continue;
}
KFPC = KFPlayerController(KFPRI.Owner);
if (!CVC.PlayerCanKickVote(KFPC))
{
CVC.WriteToChatLocalized(
KFPC,
CVC_PlayerCantVote,
CfgKickVote.default.WarningColorHex,
String(CfgStartWaveKickProtection.default.Waves));
}
}
}
else
{
KFGI.BroadcastLocalized(KFGI, class'KFLocalMessage', LMT_KickVoteStarted, CurrentKickVote.PlayerPRI);
}
if (CfgKickVote.default.bLogKickVote)
{
KickVotes.Length = 0;
KickVotes.AddItem(PlayerKickVote(PRI_Kickee, false));
}
if (CfgKickVote.default.bHudNotificationsOnlyOnTraderTime)
{
AllowHudNotification = bTraderIsOpen;
}
SetTimer(VoteTime, false, nameof(ConcludeVoteKick), Self);
RecieveVoteKick(PRI_Kicker, true);
}
else if (PRI_Kickee == CurrentKickVote.PlayerPRI)
{
RecieveVoteKick(PRI_Kicker, false); // WTF is that?!
`Log_Debug("WTF happens:" @ KickeeName);
}
else
{
KFPlayerController(PRI_Kicker.Owner).ReceiveLocalizedMessage(class'KFLocalMessage', LMT_KickVoteInProgress);
}
}
private function int VotingPlayers(optional PlayerReplicationInfo KickeePRI, optional Array<KFPlayerReplicationInfo> KFPRIs)
{
local KFPlayerReplicationInfo KFPRI;
local KFPlayerController KFPC;
local int VotingPlayersNum;
`Log_Trace();
if (KFPRIs.Length == 0)
{
GetKFPRIArray(KFPRIs);
}
if (KFPC_Kickee == None)
{
if (KickeePRI == None)
{
KickeePRI = CurrentKickVote.PlayerPRI;
}
if (KickeePRI != None)
{
KFPC_Kickee = KFPlayerController(KickeePRI.Owner);
}
}
VotingPlayersNum = 0;
foreach KFPRIs(KFPRI)
{
KFPC = KFPlayerController(KFPRI.Owner);
if (KFPC != None && CVC.PlayerCanKickVote(KFPC, KFPC_Kickee))
{
VotingPlayersNum++;
}
}
return VotingPlayersNum;
}
private function String DidntVotedPlayers()
{
local Array<KFPlayerReplicationInfo> KFPRIs;
local KFPlayerReplicationInfo KFPRI;
local String DidntVoted;
`Log_Trace();
GetKFPRIArray(KFPRIs);
foreach KFPRIs(KFPRI)
{
if (PlayersThatHaveVoted.Find(KFPRI) == INDEX_None)
{
DidntVoted $= (DidntVoted == "" ? KFPRI.PlayerName : ("," @ KFPRI.PlayerName));
}
}
return DidntVoted;
}
private function String VotedPlayers()
{
local PlayerReplicationInfo PRI;
local String Voted;
`Log_Trace();
foreach PlayersThatHaveVoted(PRI)
{
Voted $= (Voted == "" ? PRI.PlayerName : ("," @ PRI.PlayerName));
}
return Voted;
}
private function S_KickVote PlayerKickVote(PlayerReplicationInfo PRI, bool bKick)
{
local KFPlayerReplicationInfo KFPRI;
local PlayerController PC;
local OnlineSubsystem OS;
local S_KickVote KV;
`Log_Trace();
KV.Name = PRI.PlayerName;
KV.UniqueID = class'OnlineSubsystem'.static.UniqueNetIdToString(PRI.UniqueId);
PC = PlayerController(PRI.Owner);
if (PC != None && !PC.bIsEosPlayer)
{
OS = class'GameEngine'.static.GetOnlineSubsystem();
if (OS != None)
{
KV.SteamID = OS.UniqueNetIdToInt64(PRI.UniqueId);
}
}
KV.VoteYes = bKick;
KFPRI = KFPlayerReplicationInfo(PRI);
if (KFPRI != None)
{
KV.Perk = KFPRI.CurrentPerkClass;
KV.Level = KFPRI.GetActivePerkLevel();
}
return KV;
}
public reliable server function RecieveVoteKick(PlayerReplicationInfo PRI, bool bKick)
{
`Log_Trace();
// there is a bug somewhere in the TWI code:
// sometimes votes for skipping a trader or pausing come to this function
// this is an attempt to fix it without affecting other parts of the game
// probably this part can be changed when I understand when this bug occurs
if (bIsSkipTraderVoteInProgress)
{
`Log_Debug("Receive kick vote while skip trader vote is active");
RecieveVoteSkipTrader(PRI, bKick);
return;
}
if (bIsPauseGameVoteInProgress)
{
`Log_Debug("Receive kick vote while skip pause vote is active");
ReceiveVotePauseGame(PRI, bKick);
return;
}
if (!bIsKickVoteInProgress)
{
`Log_Debug("Receive kick vote while kick vote is not active");
return;
}
if (PlayersThatHaveVoted.Find(PRI) == INDEX_NONE)
{
if (bKick)
{
YesVotesPlayers = (YesVotesPlayers == "") ? PRI.PlayerName : YesVotesPlayers $ "," @ PRI.PlayerName;
}
else
{
NoVotesPlayers = (NoVotesPlayers == "") ? PRI.PlayerName : NoVotesPlayers $ "," @ PRI.PlayerName;
}
if (CfgKickVote.default.bLogKickVote)
{
KickVotes.AddItem(PlayerKickVote(PRI, bKick));
}
if (CfgKickVote.default.bChatNotifications)
{
CVC.BroadcastChatLocalized(
bKick ? CVC_KickVoteYesReceived : CVC_KickVoteNoReceived,
bKick ? CfgKickVote.default.PositiveColorHex : CfgKickVote.default.NegativeColorHex,
KFPC_Kickee,
PRI.PlayerName);
CVC.WriteToChatLocalized(
KFPC_Kickee,
bKick ? CVC_KickVoteYesReceived : CVC_KickVoteNoReceived,
bKick ? CfgKickVote.default.NegativeColorHex : CfgKickVote.default.PositiveColorHex,
PRI.PlayerName);
}
if (CfgKickVote.default.bHUDNotifications && AllowHudNotification)
{
if (NoVotesPlayers == "")
{
CVC.BroadcastHUDLocalized(
CVC_KickVoteStartedHUD,
float(VoteTime),
KickerName,
KickeeName,
YesVotesPlayers);
}
else
{
CVC.BroadcastHUDLocalized(
CVC_KickVoteReceivedHUD,
float(VoteTime),
YesVotesPlayers,
NoVotesPlayers);
}
}
}
Super.RecieveVoteKick(PRI, bKick);
}
public function bool ShouldConcludeKickVote()
{
local KFGameInfo KFGI;
local int NumPRIs;
local int KickVotesNeeded;
`Log_Trace();
if (CfgStartWaveKickProtection.default.Waves == 0)
{
return Super.ShouldConcludeKickVote();
}
KFGI = KFGameInfo(WorldInfo.Game);
NumPRIs = VotingPlayers();
if (YesVotes + NoVotes >= NumPRIs)
{
return true;
}
else if (KFGI != None)
{
KickVotesNeeded = FCeil(float(NumPRIs) * KFGI.KickVotePercentage);
KickVotesNeeded = Clamp(KickVotesNeeded, 1, NumPRIs);
if (YesVotes >= KickVotesNeeded)
{
return true;
}
else if (NoVotes > (NumPRIs - KickVotesNeeded))
{
return true;
}
}
return false;
}
public reliable server function ConcludeVoteKick()
{
local Array<KFPlayerReplicationInfo> KFPRIs;
local KFPlayerReplicationInfo KFPRI;
local PlayerReplicationInfo PRI;
local int NumPRIs;
local KFGameInfo KFGI;
local KFPlayerController KickedPC;
local int KickVotesNeeded;
local int PrevKickedPlayers;
`Log_Trace();
`Log_Debug("ConcludeVoteKick()" @ bIsKickVoteInProgress);
if (bIsKickVoteInProgress)
{
YesVotesPlayers = "";
NoVotesPlayers = "";
if (CfgKickVote.default.bHUDNotifications)
{
CVC.BroadcastClearMessageHUD(CfgKickVote.default.DefferedClearHUD);
}
}
PrevKickedPlayers = KickedPlayers;
if (CfgStartWaveKickProtection.default.Waves == 0)
{
Super.ConcludeVoteKick();
}
else if (bIsKickVoteInProgress)
{
KFGI = KFGameInfo(WorldInfo.Game);
GetKFPRIArray(KFPRIs);
foreach KFPRIs(KFPRI) KFPRI.HideKickVote();
NumPRIs = VotingPlayers(CurrentKickVote.PlayerPRI, KFPRIs);
KickVotesNeeded = FCeil(float(NumPRIs) * KFGI.KickVotePercentage);
KickVotesNeeded = Clamp(KickVotesNeeded, 1, NumPRIs);
if (YesVotes >= KickVotesNeeded)
{
if (CurrentKickVote.PlayerPRI == None || CurrentKickVote.PlayerPRI.bPendingDelete)
{
foreach WorldInfo.Game.InactivePRIArray(PRI)
{
if (PRI.UniqueId == CurrentKickVote.PlayerID)
{
CurrentKickVote.PlayerPRI = PRI;
break;
}
}
}
if (KFGI.AccessControl != None)
{
KickedPC = ((CurrentKickVote.PlayerPRI != None) && (CurrentKickVote.PlayerPRI.Owner != None)) ? KFPlayerController(CurrentKickVote.PlayerPRI.Owner) : None;
KFAccessControl(KFGI.AccessControl).KickSessionBanPlayer(KickedPC, CurrentKickVote.PlayerID, KFGI.AccessControl.KickedMsg);
}
KFGI.BroadcastLocalized(KFGI, class'KFLocalMessage', LMT_KickVoteSucceeded, CurrentKickVote.PlayerPRI);
KickedPlayers++;
}
else
{
bIsFailedVoteTimerActive = true;
SetTimer(KFGI.TimeBetweenFailedVotes, false, nameof(ClearFailedVoteFlag), Self);
KFGI.BroadcastLocalized(KFGI, class'KFLocalMessage', LMT_KickVoteFailed, CurrentKickVote.PlayerPRI);
}
bIsKickVoteInProgress = false;
CurrentKickVote.PlayerPRI = None;
CurrentKickVote.PlayerID = class'PlayerReplicationInfo'.default.UniqueId;
YesVotes = 0;
NoVotes = 0;
}
if (CfgKickVote.default.bLogKickVote && KickedPlayers > PrevKickedPlayers)
{
LogKickVotes();
}
}
private function LogKickVotes()
{
local S_KickVote KV;
local S_KickVote Kicker;
local S_KickVote Kickee;
local Array<S_KickVote> Yes;
local Array<S_KickVote> No;
local int Index;
`Log_Trace();
foreach KickVotes(KV, Index)
{
switch (Index)
{
case 0: Kickee = KV; break;
case 1: Kicker = KV; break;
default: if (KV.VoteYes) Yes.AddItem(KV); else No.AddItem(KV); break;
}
}
`Log_Kick("Kicker:" @ LogVotePlayer(Kicker));
`Log_Kick("Kicked:" @ LogVotePlayer(Kickee) @ String(Kickee.Perk) @ String(Kickee.Level));
`Log_Kick("Yes voters:");
foreach Yes(KV) `Log_Kick(LogVotePlayer(KV));
if (No.Length == 0) return;
`Log_Kick("No voters:");
foreach No(KV) `Log_Kick(LogVotePlayer(KV));
}
private function String LogVotePlayer(S_KickVote KV)
{
`Log_Trace();
return KV.Name @ "(UniqueID:" @ KV.UniqueID $ (KV.SteamID == "" ? "" : (", SteamID:" @ KV.SteamID $ ", Profile:" @ "https://steamcommunity.com/profiles/" $ KV.SteamID)) $ ")";
}
public reliable server function RecieveVoteSkipTrader(PlayerReplicationInfo PRI, bool bSkip)
{
local bool MustNotify;
`Log_Trace();
MustNotify = (PlayersThatHaveVoted.Find(PRI) == INDEX_NONE);
Super.RecieveVoteSkipTrader(PRI, bSkip);
if (MustNotify)
{
if (CfgSkipTraderVote.default.bChatNotifications)
{
CVC.BroadcastChatLocalized(
bSkip ? CVC_SkipVoteYesReceived : CVC_SkipVoteNoReceived,
bSkip ? CfgSkipTraderVote.default.PositiveColorHex : CfgSkipTraderVote.default.NegativeColorHex,
None,
PRI.PlayerName);
}
if (CfgSkipTraderVote.default.bHUDNotifications)
{
CVC.BroadcastHUDLocalized(
CVC_VoteProgressHUD,
float(VoteTime),
VotedPlayers(),
DidntVotedPlayers());
}
}
}
public reliable server function ConcludeVoteSkipTrader()
{
`Log_Trace();
`Log_Debug("ConcludeVoteSkipTrader()" @ bIsSkipTraderVoteInProgress);
if (bIsSkipTraderVoteInProgress)
{
YesVotesPlayers = "";
NoVotesPlayers = "";
if (CfgSkipTraderVote.default.bHUDNotifications)
{
CVC.BroadcastClearMessageHUD(CfgSkipTraderVote.default.DefferedClearHUD);
}
}
Super.ConcludeVoteSkipTrader();
}
public reliable server function ReceiveVotePauseGame(PlayerReplicationInfo PRI, bool bSkip)
{
local bool MustNotify;
`Log_Trace();
MustNotify = (PlayersThatHaveVoted.Find(PRI) == INDEX_NONE);
Super.ReceiveVotePauseGame(PRI, bSkip);
if (MustNotify)
{
if (CfgPauseVote.default.bChatNotifications)
{
CVC.BroadcastChatLocalized(
bSkip ? CVC_PauseVoteYesReceived : CVC_PauseVoteNoReceived,
bSkip ? CfgPauseVote.default.PositiveColorHex : CfgPauseVote.default.NegativeColorHex,
None,
PRI.PlayerName);
}
if (CfgPauseVote.default.bHUDNotifications)
{
CVC.BroadcastHUDLocalized(
CVC_VoteProgressHUD,
float(VoteTime),
VotedPlayers(),
DidntVotedPlayers());
}
}
}
public reliable server function ConcludeVotePauseGame()
{
`Log_Trace();
`Log_Debug("ConcludeVotePauseGame()" @ bIsPauseGameVoteInProgress);
if (bIsPauseGameVoteInProgress)
{
YesVotesPlayers = "";
NoVotesPlayers = "";
if (CfgPauseVote.default.bHUDNotifications)
{
CVC.BroadcastClearMessageHUD(CfgPauseVote.default.DefferedClearHUD);
}
}
Super.ConcludeVotePauseGame();
}
private function Array<String> ActiveMapCycle()
{
local KFGameInfo KFGI;
`Log_Trace();
if (WorldInfo.NetMode == NM_Standalone)
{
return Maplist;
}
KFGI = KFGameInfo(WorldInfo.Game);
if (KFGI != None)
{
return KFGI.GameMapCycles[KFGI.ActiveMapCycle].Maps;
}
}
private function Array<String> GetAviableMaps()
{
local String LowerDefaultNextMap;
local Array<String> MapCycle;
local Array<String> Maps;
local KFGameInfo KFGI;
local String Map;
local int Index;
`Log_Trace();
KFGI = KFGameInfo(WorldInfo.Game);
if (KFGI == None) return Maps;
MapCycle = ActiveMapCycle();
LowerDefaultNextMap = Locs(CfgMapVote.default.DefaultNextMap);
`Log_Debug("LowerDefaultNextMap:" @ LowerDefaultNextMap);
switch (LowerDefaultNextMap)
{
case "any":
`Log_Debug("any");
foreach MapCycle(Map)
{
if (KFGI.IsMapAllowedInCycle(Map))
{
Maps.AddItem(Map);
}
}
break;
case "official":
`Log_Debug("official");
foreach MapCycle(Map)
{
if (KFGI.IsMapAllowedInCycle(Map) && !IsCustomMap(Map))
{
Maps.AddItem(Map);
}
}
break;
case "custom":
`Log_Debug("custom");
foreach MapCycle(Map)
{
if (KFGI.IsMapAllowedInCycle(Map) && IsCustomMap(Map))
{
Maps.AddItem(Map);
}
}
break;
default:
`Log_Debug("kf-");
if (Left(LowerDefaultNextMap, 3) == "kf-")
{
Index = MapCycle.Find(LowerDefaultNextMap);
if (Index != INDEX_NONE)
{
Maps.AddItem(MapCycle[Index]);
}
}
break;
}
`Log_Debug("AviableMaps:"); foreach Maps(Map) `Log_Debug(Map);
return Maps;
}
private function bool IsCustomMap(String MapName)
{
local KFMapSummary MapData;
`Log_Trace();
MapData = class'KFUIDataStore_GameResource'.static.GetMapSummaryFromMapName(MapName);
if (MapData == None)
{
return true;
}
else
{
return (MapData.MapAssociation == EAI_Custom);
}
}
private function int DefaultNextMapIndex()
{
local KFGameInfo KFGI;
local Array<String> AviableMaps;
local Array<String> MapCycle;
local int CurrentMapIndex;
`Log_Trace();
KFGI = KFGameInfo(WorldInfo.Game);
if (KFGI == None) return INDEX_NONE;
MapCycle = ActiveMapCycle();
AviableMaps = GetAviableMaps();
if (MapCycle.Length > 0 && AviableMaps.Length > 0)
{
if (CfgMapVote.default.bRandomizeNextMap)
{
return MapCycle.Find(AviableMaps[Rand(AviableMaps.Length)]);
}
else
{
// I don't use KFGameInfo.GetNextMap() because
// it uses and changes global KFGameInfo.MapCycleIndex variable
CurrentMapIndex = MapCycle.Find(WorldInfo.GetMapName(true));
if (CurrentMapIndex != INDEX_NONE)
{
for (++CurrentMapIndex; CurrentMapIndex < MapCycle.Length; ++CurrentMapIndex)
{
if (AviableMaps.Find(MapCycle[CurrentMapIndex]) != INDEX_NONE)
{
return CurrentMapIndex;
}
}
}
for (CurrentMapIndex = 0; CurrentMapIndex < MapCycle.Length; ++CurrentMapIndex)
{
if (AviableMaps.Find(MapCycle[CurrentMapIndex]) != INDEX_NONE)
{
return CurrentMapIndex;
}
}
}
}
return INDEX_NONE;
}
private function String MapNameByIndex(int MapIndex)
{
local Array<String> MapCycle;
`Log_Trace();
if (MapIndex == INDEX_NONE) return "";
MapCycle = ActiveMapCycle();
if (MapIndex >= MapCycle.Length) return "";
return MapCycle[MapIndex];
}
public function int GetNextMap()
{
local int MapIndex;
local String MapName;
`Log_Trace();
if (CfgMapStat.default.bEnable)
{
if (WorldInfo.GRI != None)
{
CfgMapStats.static.IncMapStat(
WorldInfo.GetMapName(true),
WorldInfo.GRI.ElapsedTime / 60,
CfgMapStat.default.SortPolicy,
LogLevel);
}
else
{
`Log_Warn("WorldInfo.GRI is None, can't write map stats");
}
}
if (MapVoteList.Length > 0)
{
MapIndex = MapVoteList[0].MapIndex;
MapName = MapNameByIndex(MapIndex);
if (MapName != "")
{
`Log_Info("Next map (vote):" @ MapName);
}
else
{
`Log_Warn("Can't find next map (vote)");
}
}
else
{
MapIndex = DefaultNextMapIndex();
MapName = MapNameByIndex(MapIndex);
if (MapName != "")
{
`Log_Info("Next map (default):" @ MapName);
}
else
{
`Log_Warn("Can't find next map (default)");
}
}
return MapIndex;
}
defaultproperties
{
AllowHudNotification = true;
}

View File

@ -0,0 +1,82 @@
class KickProtected extends Object
config(CVC)
abstract;
var public config bool NotifyPlayerAboutKickAttempt;
var private config Array<String> PlayerID;
public static function InitConfig(int Version, int LatestVersion, E_LogLevel LogLevel)
{
`Log_TraceStatic();
switch (Version)
{
case `NO_CONFIG:
ApplyDefault(LogLevel);
default: break;
}
if (LatestVersion != Version)
{
StaticSaveConfig();
}
}
public static function Array<UniqueNetId> Load(E_LogLevel LogLevel)
{
local Array<UniqueNetId> UIDs;
local UniqueNetId UID;
local String ID;
`Log_TraceStatic();
foreach default.PlayerID(ID)
{
if (AnyToUID(ID, UID, LogLevel))
{
UIDs.AddItem(UID);
}
else
{
`Log_Warn("Can't load PlayerID:" @ ID);
}
}
return UIDs;
}
private static function ApplyDefault(E_LogLevel LogLevel)
{
`Log_TraceStatic();
default.NotifyPlayerAboutKickAttempt = true;
default.PlayerID.Length = 0;
default.PlayerID.AddItem("76561190000000000");
default.PlayerID.AddItem("0x0000000000000000");
}
private static function bool IsUID(String ID, E_LogLevel LogLevel)
{
`Log_TraceStatic();
return (Locs(Left(ID, 2)) ~= "0x");
}
private static function bool AnyToUID(String ID, out UniqueNetId UID, E_LogLevel LogLevel)
{
local OnlineSubsystem OS;
`Log_TraceStatic();
OS = class'GameEngine'.static.GetOnlineSubsystem();
if (OS == None) return false;
return IsUID(ID, LogLevel) ? OS.StringToUniqueNetId(ID, UID) : OS.Int64ToUniqueNetId(ID, UID);
}
defaultproperties
{
}

52
CVC/Classes/KickVote.uc Normal file
View File

@ -0,0 +1,52 @@
class KickVote extends BaseVote
config(CVC)
abstract;
var public config bool bHudNotificationsOnlyOnTraderTime;
var public config int MinVotingPlayersToStartKickVote;
var public config int MaxKicks;
var public config bool bLogKickVote;
var public config String WarningColorHex;
public static function Load(E_LogLevel LogLevel)
{
`Log_TraceStatic();
if (default.MinVotingPlayersToStartKickVote < 2)
{
`Log_Error("MinVotingPlayersToStartKickVote" @ "(" $ default.MinVotingPlayersToStartKickVote $ ")" @ "must be greater than 1");
default.MinVotingPlayersToStartKickVote = 2;
}
if (default.MaxKicks < 1)
{
`Log_Error("MaxKicks" @ "(" $ default.MaxKicks $ ")" @ "must be greater than 0");
default.MaxKicks = 2;
}
if (!IsValidHexColor(default.WarningColorHex, LogLevel))
{
`Log_Error("WarningColorHex" @ "(" $ default.WarningColorHex $ ")" @ "is not valid hex color");
default.WarningColorHex = class'KFLocalMessage'.default.PriorityColor;
}
Super.Load(LogLevel);
}
protected static function ApplyDefault(E_LogLevel LogLevel)
{
`Log_TraceStatic();
Super.ApplyDefault(LogLevel);
default.bHudNotificationsOnlyOnTraderTime = true;
default.MinVotingPlayersToStartKickVote = 2;
default.MaxKicks = 2;
default.DefferedClearHUD = 2.0f;
default.WarningColorHex = class'KFLocalMessage'.default.PriorityColor;
}
defaultproperties
{
}

57
CVC/Classes/MapStat.uc Normal file
View File

@ -0,0 +1,57 @@
class MapStat extends Object
config(CVC)
abstract;
var public config bool bEnable;
var public config String SortPolicy;
public static function InitConfig(int Version, int LatestVersion, E_LogLevel LogLevel)
{
`Log_TraceStatic();
switch (Version)
{
case `NO_CONFIG:
ApplyDefault(LogLevel);
default: break;
}
if (LatestVersion != Version)
{
StaticSaveConfig();
}
}
public static function Load(E_LogLevel LogLevel)
{
`Log_TraceStatic();
switch (Locs(default.SortPolicy))
{
case "counterasc": return;
case "counterdesc": return;
case "nameasc": return;
case "namedesc": return;
case "playtimetotalasc": return;
case "playtimetotaldesc": return;
case "playtimeavgasc": return;
case "playtimeavgdesc": return;
}
`Log_Error("Can't load SortPolicy (" $ default.SortPolicy $ "), must be one of: CounterAsc CounterDesc NameAsc NameDesc PlaytimeTotalAsc PlaytimeTotalDesc PlaytimeAvgAsc PlaytimeAvgDesc");
default.SortPolicy = "CounterDesc";
}
protected static function ApplyDefault(E_LogLevel LogLevel)
{
`Log_TraceStatic();
default.bEnable = false;
default.SortPolicy = "CounterDesc";
}
defaultproperties
{
}

70
CVC/Classes/MapStats.uc Normal file
View File

@ -0,0 +1,70 @@
class MapStats extends Object
config(MapStats);
struct MapStatEntry
{
var String Name; // map
var int Counter; // play count
var int PlayTimeTotal; // minutes total
var int PlayTimeAvg; // minutes per map
};
var config array<MapStatEntry> MapStat;
static delegate int CounterAsc (MapStatEntry A, MapStatEntry B) { return B.Counter < A.Counter ? -1 : 0; }
static delegate int CounterDesc (MapStatEntry A, MapStatEntry B) { return A.Counter < B.Counter ? -1 : 0; }
static delegate int NameAsc (MapStatEntry A, MapStatEntry B) { return B.Name < A.Name ? -1 : 0; }
static delegate int NameDesc (MapStatEntry A, MapStatEntry B) { return A.Name < B.Name ? -1 : 0; }
static delegate int PlayTimeTotalAsc (MapStatEntry A, MapStatEntry B) { return B.PlayTimeTotal < A.PlayTimeTotal ? -1 : 0; }
static delegate int PlayTimeTotalDesc (MapStatEntry A, MapStatEntry B) { return A.PlayTimeTotal < B.PlayTimeTotal ? -1 : 0; }
static delegate int PlayTimeAvgAsc (MapStatEntry A, MapStatEntry B) { return B.PlayTimeAvg < A.PlayTimeAvg ? -1 : 0; }
static delegate int PlayTimeAvgDesc (MapStatEntry A, MapStatEntry B) { return A.PlayTimeAvg < B.PlayTimeAvg ? -1 : 0; }
static function SortMapStat(String SortPolicy, E_LogLevel LogLevel)
{
`Log_TraceStatic();
switch (Locs(SortPolicy))
{
case "counterasc": default.MapStat.Sort(CounterAsc); break;
case "counterdesc": default.MapStat.Sort(CounterDesc); break;
case "nameasc": default.MapStat.Sort(NameAsc); break;
case "namedesc": default.MapStat.Sort(NameDesc); break;
case "playtimetotalasc": default.MapStat.Sort(PlayTimeTotalAsc); break;
case "playtimetotaldesc": default.MapStat.Sort(PlayTimeTotalDesc); break;
case "playtimeavgasc": default.MapStat.Sort(PlayTimeAvgAsc); break;
case "playtimeavgdesc": default.MapStat.Sort(PlayTimeAvgDesc); break;
}
}
static function IncMapStat(String Map, int PlayTime, String SortPolicy, E_LogLevel LogLevel)
{
local int MapStatEntryIndex;
local MapStatEntry NewEntry;
`Log_TraceStatic();
MapStatEntryIndex = default.MapStat.Find('Name', Map);
if (MapStatEntryIndex == INDEX_NONE)
{
NewEntry.Name = Map;
NewEntry.Counter = 1;
NewEntry.PlayTimeTotal = PlayTime;
NewEntry.PlayTimeAvg = PlayTime;
default.MapStat.AddItem(NewEntry);
}
else
{
default.MapStat[MapStatEntryIndex].Counter++;
default.MapStat[MapStatEntryIndex].PlayTimeTotal += PlayTime;
default.MapStat[MapStatEntryIndex].PlayTimeAvg = default.MapStat[MapStatEntryIndex].PlayTimeTotal / default.MapStat[MapStatEntryIndex].Counter;
}
SortMapStat(SortPolicy, LogLevel);
StaticSaveConfig();
}
DefaultProperties
{
}

57
CVC/Classes/MapVote.uc Normal file
View File

@ -0,0 +1,57 @@
class MapVote extends Object
config(CVC)
abstract;
var public config String DefaultNextMap; // Any, Official, Custom, KF-<MapName>
var public config bool bRandomizeNextMap;
public static function InitConfig(int Version, int LatestVersion, E_LogLevel LogLevel)
{
`Log_TraceStatic();
switch (Version)
{
case `NO_CONFIG:
ApplyDefault(LogLevel);
default: break;
}
if (LatestVersion != Version)
{
StaticSaveConfig();
}
}
public static function Load(E_LogLevel LogLevel)
{
local String LowerDefaultNextMap;
`Log_TraceStatic();
LowerDefaultNextMap = Locs(default.DefaultNextMap);
switch (LowerDefaultNextMap)
{
case "any": return;
case "official": return;
case "custom": return;
default: if (Left(LowerDefaultNextMap, 3) == "kf-") return;
}
`Log_Error("Can't load DefaultNextMap (" $ default.DefaultNextMap $ "), must be one of: Any Official Custom KF-<MapName>");
default.DefaultNextMap = "Any";
}
protected static function ApplyDefault(E_LogLevel LogLevel)
{
`Log_TraceStatic();
default.bRandomizeNextMap = true;
default.DefaultNextMap = "Any";
}
defaultproperties
{
}

19
CVC/Classes/PauseVote.uc Normal file
View File

@ -0,0 +1,19 @@
class PauseVote extends BaseVote
config(CVC)
abstract;
protected static function ApplyDefault(E_LogLevel LogLevel)
{
`Log_TraceStatic();
Super.ApplyDefault(LogLevel);
default.PositiveColorHex = class'KFLocalMessage'.default.GameColor;
default.bChatNotifications = false;
default.bHudNotifications = false;
}
defaultproperties
{
}

View File

@ -0,0 +1,19 @@
class SkipTraderVote extends BaseVote
config(CVC)
abstract;
protected static function ApplyDefault(E_LogLevel LogLevel)
{
`Log_TraceStatic();
Super.ApplyDefault(LogLevel);
default.PositiveColorHex = class'KFLocalMessage'.default.GameColor;
default.bChatNotifications = false;
default.bHudNotifications = false;
}
defaultproperties
{
}

View File

@ -0,0 +1,54 @@
class StartWaveKickProtection extends Object
config(CVC)
abstract;
var public config int Waves;
var public config int MinLevel;
public static function InitConfig(int Version, int LatestVersion, E_LogLevel LogLevel)
{
`Log_TraceStatic();
switch (Version)
{
case `NO_CONFIG:
ApplyDefault(LogLevel);
default: break;
}
if (LatestVersion != Version)
{
StaticSaveConfig();
}
}
public static function Load(E_LogLevel LogLevel)
{
`Log_TraceStatic();
if (default.Waves < 0)
{
`Log_Error("Waves" @ "(" $ default.Waves $ ")" @ "must be greater than or equal 0");
default.Waves = 0;
}
if (default.MinLevel < 0 || default.MinLevel > 25)
{
`Log_Error("MinLevel" @ "(" $ default.MinLevel $ ")" @ "must be in range 0-25");
default.MinLevel = 0;
}
}
protected static function ApplyDefault(E_LogLevel LogLevel)
{
`Log_TraceStatic();
default.Waves = 0;
default.MinLevel = 0;
}
defaultproperties
{
}

21
CVC/Classes/_Logger.uc Normal file
View File

@ -0,0 +1,21 @@
class _Logger extends Object
abstract;
enum E_LogLevel
{
LL_WrongLevel,
LL_None,
LL_Fatal,
LL_Error,
LL_Warning,
LL_Kick,
LL_Info,
LL_Debug,
LL_Trace,
LL_All
};
defaultproperties
{
}