From a156bd319753ef901b677049698905274870c2c3 Mon Sep 17 00:00:00 2001 From: Nikita Hoffman Date: Wed, 17 Aug 2022 21:11:56 +0300 Subject: [PATCH] init: initial commit --- Classes/KFTHPAdminHelpCommand.uc | 15 + Classes/KFTHPCommand.uc | 689 +++++++++++++++++++++++++ Classes/KFTHPCommandExecutionState.uc | 232 +++++++++ Classes/KFTHPCommandManager.uc | 334 ++++++++++++ Classes/KFTHPCommandPreservingState.uc | 131 +++++ Classes/KFTHPCommandValidator.uc | 172 ++++++ Classes/KFTHPFakesCommand.uc | 67 +++ Classes/KFTHPGameSettingsCommand.uc | 18 + Classes/KFTHPGameStateUtils.uc | 80 +++ Classes/KFTHPGodModeCommand.uc | 129 +++++ Classes/KFTHPHelpCommand.uc | 90 ++++ Classes/KFTHPKillZedsCommand.uc | 87 ++++ Classes/KFTHPMaxZedsCommand.uc | 62 +++ Classes/KFTHPSetNameCommand.uc | 57 ++ Classes/KFTHPSkipTradeCommand.uc | 22 + Classes/KFTHPSlotsCommand.uc | 59 +++ Classes/KFTHPSpawnRateCommand.uc | 58 +++ Classes/KFTHPSpectatorsCommand.uc | 59 +++ Classes/KFTHPStatusCommand.uc | 31 ++ Classes/KFTHPTargetCommand.uc | 46 ++ Classes/KFTHPTradeTimeCommand.uc | 67 +++ Classes/KFTHPZedHPConfigCommand.uc | 67 +++ Classes/KFTHPZedTimeCommand.uc | 49 ++ README.md | 7 + 24 files changed, 2628 insertions(+) create mode 100644 Classes/KFTHPAdminHelpCommand.uc create mode 100644 Classes/KFTHPCommand.uc create mode 100644 Classes/KFTHPCommandExecutionState.uc create mode 100644 Classes/KFTHPCommandManager.uc create mode 100644 Classes/KFTHPCommandPreservingState.uc create mode 100644 Classes/KFTHPCommandValidator.uc create mode 100644 Classes/KFTHPFakesCommand.uc create mode 100644 Classes/KFTHPGameSettingsCommand.uc create mode 100644 Classes/KFTHPGameStateUtils.uc create mode 100644 Classes/KFTHPGodModeCommand.uc create mode 100644 Classes/KFTHPHelpCommand.uc create mode 100644 Classes/KFTHPKillZedsCommand.uc create mode 100644 Classes/KFTHPMaxZedsCommand.uc create mode 100644 Classes/KFTHPSetNameCommand.uc create mode 100644 Classes/KFTHPSkipTradeCommand.uc create mode 100644 Classes/KFTHPSlotsCommand.uc create mode 100644 Classes/KFTHPSpawnRateCommand.uc create mode 100644 Classes/KFTHPSpectatorsCommand.uc create mode 100644 Classes/KFTHPStatusCommand.uc create mode 100644 Classes/KFTHPTargetCommand.uc create mode 100644 Classes/KFTHPTradeTimeCommand.uc create mode 100644 Classes/KFTHPZedHPConfigCommand.uc create mode 100644 Classes/KFTHPZedTimeCommand.uc create mode 100644 README.md diff --git a/Classes/KFTHPAdminHelpCommand.uc b/Classes/KFTHPAdminHelpCommand.uc new file mode 100644 index 0000000..7203497 --- /dev/null +++ b/Classes/KFTHPAdminHelpCommand.uc @@ -0,0 +1,15 @@ +class KFTHPAdminHelpCommand extends KFTHPHelpCommand; + +/** @Override */ +protected function bool ShouldDisplayHelpForCommand(KFTHPCommand Command) +{ + return Command.IsAdminOnly(); +} + +defaultproperties +{ + bAdminOnly=true + Aliases(0)="AHELP" + Aliases(1)="ADMINHELP" + Description="Show a list of commands available to admins only or display info about one specific command" +} diff --git a/Classes/KFTHPCommand.uc b/Classes/KFTHPCommand.uc new file mode 100644 index 0000000..fe4a5d9 --- /dev/null +++ b/Classes/KFTHPCommand.uc @@ -0,0 +1,689 @@ +class KFTHPCommand extends Core.Object within KFTHPCommandManager + abstract; + +const ERRNO_NONE = 0; +const ERRNO_NOGAMETYPE = 1; +const ERRNO_NOTADMIN = 2; +const ERRNO_ARGC = 3; +const ERRNO_ARGL = 4; +const ERRNO_ARGT = 5; +const ERRNO_INVALARGS = 6; +const ERRNO_INVALTARGET = 7; +const ERRNO_CUSTOM = 8; + +var protected const Class ValidatorClass; +var protected const Class CommandStateClass; + +var protected editconstarray Array ArgTypes; +var protected editconstarray Array Aliases; + +var protected const int MinArgsNum, MaxArgsNum; + +var protected const string Description; +var protected const string Signature; + +var protected const bool bNotifySenderOnSuccess; +var protected const bool bNotifyTargetsOnSuccess; +var protected const bool bNotifyGlobalOnSuccess; +var protected const bool bNotifyOnError; + +var protected config const bool bAdminOnly; +var protected config const bool bFirstMatchOnly; +var protected config const bool bDisableNotifications; + +/**************************** + * PUBLIC INTERFACE + ****************************/ + +public final function KFTHPCommandExecutionState Execute( + PlayerController Sender, + Array Args) +{ + local KFTHPCommandExecutionState ExecState; + + ExecState = new(Self) CommandStateClass; + ExecState.InitCommandState(Sender, Args); + + Validate(ExecState); + + if (CheckActionProcessing(ExecState)) + { + ProcessAction(ExecState); + FinishExecution(ExecState); + } + else + { + TerminateExecution(ExecState); + } + + return ExecState; +} + +public final function string GetHelpString() +{ + local int i; + local string HelpMessage; + + for (i = 0; i < Aliases.Length; i++) + { + HelpMessage $= Aliases[i]; + + if (i + 1 < Aliases.Length) + { + HelpMessage $= "/"; + } + } + + HelpMessage $= " "$Signature$" - "$Description; + + return HelpMessage; +} + +public final function bool IsAdminOnly() +{ + return bAdminOnly; +} + +public final function bool HasAlias(string Alias) +{ + local int i; + + for (i = 0; i < Aliases.Length; i++) + { + if (Alias ~= Aliases[i]) + { + return true; + } + } + + return false; +} + +/**************************** + * VALIDATION FLOW + ****************************/ + +protected final function Validate(KFTHPCommandExecutionState ExecState) +{ + ExecState.SetValidatingStatus(); + + StartValidationPipeline(ExecState); + + if (!CheckActionFailure(ExecState)) + { + ExecState.SetProcessingStatus(); + } +} + +protected final function StartValidationPipeline(KFTHPCommandExecutionState ExecState) +{ + ValidateGameType(ExecState); + ValidateAdmin(ExecState); + ValidateArgsNum(ExecState); + ValidateArgsLength(ExecState); + ValidateArgsTypes(ExecState); + ValidateArgs(ExecState); + ValidateTargets(ExecState); + ValidateCustom(ExecState); +} + +protected final function ValidateGameType(KFTHPCommandExecutionState ExecState) +{ + if (CheckActionFailure(ExecState)) + { + return; + } + + if (!CheckGameType()) + { + ExecState.SetErrorNoGameType(); + return; + } +} + +protected final function ValidateAdmin(KFTHPCommandExecutionState ExecState) +{ + if (CheckActionFailure(ExecState)) + { + return; + } + + if (IsAdminOnly() && !CheckAdminPermissions(ExecState)) + { + ExecState.SetErrorNotAdmin(); + return; + } +} + +protected final function ValidateArgsNum(KFTHPCommandExecutionState ExecState) +{ + if (CheckActionFailure(ExecState)) + { + return; + } + + if (!CheckArgsNum(ExecState)) + { + ExecState.SetErrorArgC(); + return; + } +} + +protected final function ValidateArgsLength(KFTHPCommandExecutionState ExecState) +{ + if (CheckActionFailure(ExecState)) + { + return; + } + + if (!CheckArgsLength(ExecState)) + { + ExecState.SetErrorArgL(); + return; + } +} + +protected final function ValidateArgsTypes(KFTHPCommandExecutionState ExecState) +{ + if (CheckActionFailure(ExecState)) + { + return; + } + + if (!CheckArgTypes(ExecState)) + { + ExecState.SetErrorArgT(); + return; + } +} + +protected final function ValidateArgs(KFTHPCommandExecutionState ExecState) +{ + if (CheckActionFailure(ExecState)) + { + return; + } + + if (!CheckArgs(ExecState)) + { + ExecState.SetErrorInvalArgs(); + return; + } +} + +protected final function ValidateTargets(KFTHPCommandExecutionState ExecState) +{ + if (CheckActionFailure(ExecState)) + { + return; + } + + if (!CheckTargets(ExecState)) + { + ExecState.SetErrorInvalTarget(); + return; + } +} + +protected final function ValidateCustom(KFTHPCommandExecutionState ExecState) +{ + if (CheckActionFailure(ExecState)) + { + return; + } + + if (!CheckCustom(ExecState)) + { + ExecState.SetErrorCustom(); + return; + } +} + +/**************************** + * CHECKER FUNCTIONS + ****************************/ + +protected final function bool CheckGameType() +{ + return KFGameType(Level.Game) != None && KFGameReplicationInfo(Level.Game.GameReplicationInfo) != None; +} + +protected final function bool CheckActionFailure(KFTHPCommandExecutionState ExecState) +{ + return ExecState.IsFailed(); +} + +protected final function bool CheckActionProcessing(KFTHPCommandExecutionState ExecState) +{ + return ExecState.IsProcessing(); +} + +protected final function bool CheckAdminPermissions(KFTHPCommandExecutionState ExecState) +{ + return ExecState.GetSender().Pawn.PlayerReplicationInfo.bAdmin || ExecState.GetSender().Pawn.PlayerReplicationInfo.bSilentAdmin; +} + +protected final function bool CheckArgsNum(KFTHPCommandExecutionState ExecState) +{ + return ExecState.GetArgC() >= MinArgsNum && ExecState.GetArgC() <= MaxArgsNum; +} + +protected final function bool CheckArgsLength(KFTHPCommandExecutionState ExecState) +{ + local int i; + + for (i = 0; i < ExecState.GetArgC(); i++) + { + if (IsEmptyString(ExecState.GetArg(i)) || !IsValidLengthString(ExecState.GetArg(i))) + { + return false; + } + } + + return true; +} + +protected final function bool CheckArgTypes(KFTHPCommandExecutionState ExecState) +{ + local int i; + local string CurrentArg; + + for (i = 0; i < ExecState.GetArgC(); i++) + { + CurrentArg = ExecState.GetArg(i); + + switch (Locs(ArgTypes[i])) + { + case "number": + if (!IsNumber(CurrentArg)) + { + return false; + } + break; + + case "word": + if (!IsWord(CurrentArg)) + { + return false; + } + break; + + case "switch": + if (!IsSwitchValue(CurrentArg)) + { + return false; + } + break; + + case "any": + break; + } + } + + return true; +} + +protected function bool CheckArgs(KFTHPCommandExecutionState ExecState) +{ + return true; +} + +protected function bool CheckTargets(KFTHPCommandExecutionState ExecState) +{ + return true; +} + +protected function bool CheckCustom(KFTHPCommandExecutionState ExecState) +{ + return true; +} + +/**************************** + * ACTION PROCESSING + ****************************/ + +protected final function ProcessAction(KFTHPCommandExecutionState ExecState) +{ + AddCommandActionTargets(ExecState); + DoAction(ExecState); + ExecState.SetSuccessStatus(); +} + +protected function DoAction(KFTHPCommandExecutionState ExecState); + +protected function DoActionForSingleTarget(KFTHPCommandExecutionState ExecState, PlayerController PC); + +protected final function TerminateExecution(KFTHPCommandExecutionState ExecState) +{ + if (!bDisableNotifications) + { + NotifyOnError(ExecState); + } +} + +protected final function FinishExecution(KFTHPCommandExecutionState ExecState) +{ + if (!bDisableNotifications) + { + NotifyOnSuccess(ExecState); + } +} + +/**************************** + * ACTION TARGET MANAGEMENT + ****************************/ + +protected final function AddCommandActionTargets(KFTHPCommandExecutionState ExecState) +{ + local Controller C; + local PlayerController PC; + + for (C = Level.ControllerList; C != None; C = C.NextController) + { + PC = PlayerController(C); + + if (PC != None && ShouldBeTarget(ExecState, PC)) + { + ExecState.AddTarget(PC); + + if (bFirstMatchOnly) + { + break; + } + } + } +} + +protected function bool ShouldBeTarget( + KFTHPCommandExecutionState ExecState, + PlayerController PC) +{ + return false; +} + +/**************************** + * NOTIFICATIONS + ****************************/ + +protected final function SendMessage(PlayerController PC, string Message) +{ + PC.ClientMessage(Message); +} + +protected final function NotifyOnSuccess(KFTHPCommandExecutionState ExecState) +{ + if (bNotifySenderOnSuccess) + { + NotifySenderOnSuccess(ExecState); + } + + if (bNotifyTargetsOnSuccess) + { + NotifyTargetsOnSuccess(ExecState); + } + + if (bNotifyGlobalOnSuccess) + { + NotifyGlobalOnSuccess(ExecState); + } +} + +protected final function NotifySenderOnSuccess(KFTHPCommandExecutionState ExecState) +{ + SendMessage(ExecState.GetSender(), GetSenderSuccessMessage(ExecState)); +} + +protected final function NotifyTargetsOnSuccess(KFTHPCommandExecutionState ExecState) +{ + local int i; + + for (i = 0; i < ExecState.GetTargetsNum(); i++) + { + if (bNotifySenderOnSuccess && ExecState.GetTarget(i) == ExecState.GetSender()) + { + continue; + } + + SendMessage(ExecState.GetTarget(i), GetTargetSuccessMessage(ExecState)); + } +} + +protected final function NotifyGlobalOnSuccess(KFTHPCommandExecutionState ExecState) +{ + local Controller C; + local PlayerController PC; + + for (C = Level.ControllerList; C != None; C = C.NextController) + { + PC = PlayerController(C); + + if (PC != None) + { + if ( + bNotifySenderOnSuccess && PC == ExecState.GetSender() || + bNotifyTargetsOnSuccess && ExecState.IsTarget(PC)) + { + continue; + } + + SendMessage(PC, GetGlobalSuccessMessage(ExecState)); + } + } +} + +protected final function NotifyOnError(KFTHPCommandExecutionState ExecState) +{ + if (bNotifyOnError) + { + SendMessage(ExecState.GetSender(), GetErrorMessage(ExecState)); + } +} + +protected function string GetSenderSuccessMessage(KFTHPCommandExecutionState ExecState); + +protected function string GetTargetSuccessMessage(KFTHPCommandExecutionState ExecState); + +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState); + +protected function string GetErrorMessage(KFTHPCommandExecutionState ExecState) +{ + switch (ExecState.GetErrNo()) + { + case ERRNO_NOTADMIN: + return Error(NotAdminMessage()); + + case ERRNO_ARGC: + return Error(InvalidArgsNumMessage(ExecState.GetArgC())); + + case ERRNO_ARGL: + return Error(InvalidArgsLengthMessage()); + + case ERRNO_ARGT: + return Error(InvalidArgTypesMessage()); + + case ERRNO_INVALARGS: + return Error(InvalidArgsMessage(ExecState)); + + case ERRNO_INVALTARGET: + return Error(InvalidTargetMessage(ExecState)); + + case ERRNO_CUSTOM: + return Error(CustomErrorMessage(ExecState)); + + default: + return Error(UnexpectedErrorMessage()); + } +} + +/**************************** + * ERROR MESSAGES + ****************************/ + +protected final function string Error(string Message) +{ + return "Error: "$Message; +} + +protected final function string NoGameTypeMessage() +{ + return "KFGameType/KFGameReplicationInfo not found"; +} + +protected final function string NotAdminMessage() +{ + return "Admin access required"; +} + +protected final function string InvalidArgsNumMessage(int ArgsActual) +{ + if (MinArgsNum == MaxArgsNum) + { + return "Expected "$MaxArgsNum$" arguments but got "$ArgsActual; + } + + return "Expected from "$MinArgsNum$" to "$MaxArgsNum$" arguments but got "$ArgsActual; +} + +protected final function string InvalidArgsLengthMessage() +{ + return "Arguments cannot be empty and must not exceed "$GetMaxAcceptableStringLength()$" characters in length"; +} + +protected final function string InvalidArgTypesMessage() +{ + return "Command arguments must match the signature "$Signature; +} + +protected function string InvalidArgsMessage(KFTHPCommandExecutionState ExecState) +{ + return "Invalid arguments"; +} + +protected function string InvalidTargetMessage(KFTHPCommandExecutionState ExecState) +{ + return "Invalid target"; +} + +protected function string CustomErrorMessage(KFTHPCommandExecutionState ExecState) +{ + return "Custom Error"; +} + +protected final function string UnexpectedErrorMessage() +{ + return "Unexpected Error"; +} + +/**************************** + * ARGS COERCION UTILS + ****************************/ + +protected final function int ToInt(string Arg) +{ + return int(Arg); +} + +protected final function float ToFloat(string Arg) +{ + return float(Arg); +} + +/**************************** + * VALIDATION UTILS + ****************************/ + +protected final function int GetMaxAcceptableStringLength() +{ + return ValidatorClass.static.GetMaxAcceptableStringLength(); +} + +protected final function bool IsEmptyString(string Str) +{ + return ValidatorClass.static.IsEmptyString(Str); +} + +protected final function bool IsValidLengthString(string Str) +{ + return ValidatorClass.static.IsValidLengthString(Str); +} + +protected final function bool IsStringPartOf(string SubStr, string SupStr) +{ + return ValidatorClass.static.IsStringPartOf(SubStr, SupStr); +} + +protected final function bool IsLetter(string Str) +{ + return ValidatorClass.static.IsLetter(Str); +} + +protected final function bool IsWord(string Str) +{ + return ValidatorClass.static.IsWord(Str); +} + +protected final function bool IsSwitchOnValue(string Value) +{ + return ValidatorClass.static.IsSwitchOnValue(Value); +} + +protected final function bool IsSwitchOffValue(string Value) +{ + return ValidatorClass.static.IsSwitchOffValue(Value); +} + +protected final function bool IsSwitchValue(string Value) +{ + return ValidatorClass.static.IsSwitchValue(Value); +} + +protected final function bool IsDigit(string Str) +{ + return ValidatorClass.static.IsDigit(Str); +} + +protected final function bool IsNumber(string Str) +{ + return ValidatorClass.static.IsNumber(Str); +} + +protected final function bool IsInRange( + coerce int Number, + int Start, + optional int End) +{ + return ValidatorClass.static.IsInRange(Number, Start, End); +} + +protected final function bool IsInRangeF( + coerce float Number, + float Start, + optional float End) +{ + return ValidatorClass.static.IsInRangeF(Number, Start, End); +} + +protected final function bool IsPlayer(Controller C) +{ + return ValidatorClass.static.IsPlayer(C); +} + +protected final function bool IsAlive(PlayerController PC) +{ + return ValidatorClass.static.IsAlive(PC); +} + +defaultproperties +{ + MinArgsNum=0 + MaxArgsNum=0 + bAdminOnly=false + bFirstMatchOnly=false + bNotifyOnError=true + bNotifySenderOnSuccess=true + bNotifyTargetsOnSuccess=false + bNotifyGlobalOnSuccess=false + bDisableNotifications=false + CommandStateClass=Class'KFTHPCommandExecutionState' + ValidatorClass=Class'KFTHPCommandValidator' +} diff --git a/Classes/KFTHPCommandExecutionState.uc b/Classes/KFTHPCommandExecutionState.uc new file mode 100644 index 0000000..31f7649 --- /dev/null +++ b/Classes/KFTHPCommandExecutionState.uc @@ -0,0 +1,232 @@ +class KFTHPCommandExecutionState extends Core.Object within KFTHPCommand; + +enum ECommandStatus +{ + CS_NONE, + CS_INITIALIZED, + CS_VALIDATING, + CS_PROCESSING, + CS_FAILURE, + CS_SUCCESS, +}; + +var protected ECommandStatus Status; + +var protected PlayerController Sender; +var protected Array Targets; +var protected Array Args; + +var protected int ErrNo; + +/**************************** + * PROP GETTERS + ****************************/ + +public final function PlayerController GetSender() +{ + return Sender; +} + +public final function Array GetArgs() +{ + return Args; +} + +public final function string GetArg(int i) +{ + if (i < Args.Length) + { + return Args[i]; + } + + return ""; +} + +public final function int GetArgC() +{ + return Args.Length; +} + +public final function Array GetTargets() +{ + return Targets; +} + +public final function PlayerController GetTarget(int i) +{ + if (i < Targets.Length) + { + return Targets[i]; + } + + return None; +} + +public final function int GetTargetsNum() +{ + return Targets.Length; +} + +public final function int GetErrNo() +{ + return ErrNo; +} + +/**************************** + * STATE INITIALIZATION + ****************************/ + +public final function InitCommandState + (PlayerController InitSender, Array InitArgs) +{ + local int i; + + for (i = 1; i < InitArgs.Length; i++) + { + Args[Args.Length] = InitArgs[i]; + } + + Sender = InitSender; + ErrNo = ERRNO_NONE; + + SetInitializedStatus(); +} + +/**************************** + * TARGET MANAGEMENT + ****************************/ + +public final function AddTarget(PlayerController Target) +{ + Targets[Targets.Length] = Target; +} + +public final function bool IsTarget(PlayerController PC) +{ + local int i; + + for (i = 0; i < Targets.Length; i++) + { + if (Targets[i] == PC) + { + return true; + } + } + + return false; +} + +/**************************** + * STATUS TRANSITIONING + ****************************/ + +public final function SetInitializedStatus() +{ + UpdateCommandStatus(CS_INITIALIZED); +} + +public final function SetValidatingStatus() +{ + UpdateCommandStatus(CS_VALIDATING); +} + +public final function SetProcessingStatus() +{ + UpdateCommandStatus(CS_PROCESSING); +} + +public final function SetSuccessStatus() +{ + UpdateCommandStatus(CS_SUCCESS); +} + +public final function SetErrorNoGameType() +{ + SetErrorStatus(ERRNO_NOGAMETYPE); +} + +public final function SetErrorNotAdmin() +{ + SetErrorStatus(ERRNO_NOTADMIN); +} + +public final function SetErrorArgC() +{ + SetErrorStatus(ERRNO_ARGC); +} + +public final function SetErrorArgL() +{ + SetErrorStatus(ERRNO_ARGL); +} + +public final function SetErrorArgT() +{ + SetErrorStatus(ERRNO_ARGT); +} + +public final function SetErrorInvalArgs() +{ + SetErrorStatus(ERRNO_INVALARGS); +} + +public final function SetErrorInvalTarget() +{ + SetErrorStatus(ERRNO_INVALTARGET); +} + +public final function SetErrorCustom() +{ + SetErrorStatus(ERRNO_CUSTOM); +} + +protected final function SetErrorStatus(int NextErrNo) +{ + UpdateCommandStatus(CS_FAILURE, NextErrNo); +} + +protected final function UpdateCommandStatus(ECommandStatus NextStatus, optional int NextErrNo) +{ + Status = NextStatus; + + if (IsFailed()) + { + ErrNo = NextErrNo; + } + else + { + ErrNo = ERRNO_NONE; + } +} + +/**************************** + * STATUS CHECK + ****************************/ + +public final function bool IsInitialized() +{ + return Status == CS_INITIALIZED; +} + +public final function bool IsValidating() +{ + return Status == CS_VALIDATING; +} + +public final function bool IsProcessing() +{ + return Status == CS_PROCESSING; +} + +public final function bool IsSuccess() +{ + return Status == CS_SUCCESS; +} + +public final function bool IsFailed() +{ + return Status == CS_FAILURE; +} + +defaultproperties +{} diff --git a/Classes/KFTHPCommandManager.uc b/Classes/KFTHPCommandManager.uc new file mode 100644 index 0000000..ffabcda --- /dev/null +++ b/Classes/KFTHPCommandManager.uc @@ -0,0 +1,334 @@ +class KFTHPCommandManager extends Engine.Mutator; + +var KFGameType KFGT; + +/** Class which contains some helper functions to work with game state */ +var const Class GSUClass; +var KFTHPGameStateUtils GSU; + +/********************************* + * HELPER COMMANDS + *********************************/ + +const CMD_HELP = 0; +var protected Class HelpCommandClass; + +const CMD_AHELP = 1; +var protected Class AdminHelpCommandClass; + +const CMD_STATUS = 2; +var protected Class StatusCommandClass; + +/********************************* + * GAME SETTINGS RELATED COMMANDS + *********************************/ + +const CMD_SLOTS = 3; +var protected Class SlotsCommandClass; + +const CMD_SPECS = 4; +var protected Class SpecsCommandClass; + +const CMD_FAKED = 5; +var protected Class FakesCommandClass; + +const CMD_HPCONFIG = 6; +var protected Class HPConfigCommandClass; + +const CMD_MAXZEDS = 7; +var protected Class MaxZedsCommandClass; + +const CMD_SPAWNRATE = 8; +var protected Class SpawnRateCommandClass; + +const CMD_SKIPTRADE = 9; +var protected Class SkipTradeCommandClass; + +const CMD_TRADE = 10; +var protected Class TradeTimeCommandClass; + +const CMD_ZEDTIME = 11; +var protected Class ZEDTimeCommandClass; + +const CMD_SETWAVE = 12; +// var protected Class SetWaveCommandClass; + +const CMD_RESTARTWAVE = 13; +// var protected Class RestartWaveCommandClass; + +/********************************* + * GAMEPLAY RELATED COMMANDS + *********************************/ + +const CMD_RESPAWN = 14; +// var protected Class RespawnCommandClass; + +const CMD_RESTORE = 15; +// var protected Class RestoreCommandClass; + +const CMD_KILLZEDS = 16; +var protected Class KillZedsCommandClass; + +const CMD_WELDDOORS = 17; +// var protected Class WeldDoorsCommandClass; + +const CMD_CLEARPIPES = 18; +// var protected Class ClearPipesCommandClass; + +/********************************* + * PLAYERS RELATED COMMANDS + *********************************/ + +const CMD_SETNAME = 19; +var protected Class SetNameCommandClass; + +const CMD_GOD = 20; +var protected Class GodModeCommandClass; + +const CMD_HEADSIZE = 21; +// var protected Class HeadSizeCommandClass; + +const CMD_BODYSIZE = 22; +// var protected Class BodySizeCommandClass; + +const CMD_HIT = 23; +// var protected Class HitPlayerCommandClass; + +/** Commands List */ +var protected Array Commands; + +/** THP Game settings */ +var protected config const bool bAllowMutate; +var protected bool bZedTimeDisabled; + +var protected int ZedHPConfig; +var protected const int ZedHPConfigThreshold; +var protected int FakedPlayersNum; + +/********************************* + * DATA ACCESSORS + *********************************/ + +public final function bool IsMutateAllowed() +{ + return bAllowMutate; +} + +public final function bool IsZedTimeDisabled() +{ + return bZedTimeDisabled; +} + +public final function SetZedTime(bool Flag) +{ + bZedTimeDisabled = Flag; +} + +public final function int GetZedHPConfig() +{ + return ZedHPConfig; +} + +public final function int GetZedHPConfigThreshold() +{ + return ZedHPConfigThreshold; +} + +public final function SetZedHPConfig(int NewZedHPConfig) +{ + ZedHPConfig = NewZedHPConfig; +} + +public final function int GetFakedPlayersNum() +{ + return FakedPlayersNum; +} + +public final function SetFakedPlayersNum(int NewFakedPlayersNum) +{ + FakedPlayersNum = NewFakedPlayersNum; +} + +/********************************* + * EVENTS + *********************************/ + +event PostBeginPlay() +{ + KFGT = KFTHPGameType(Level.Game); + + if (KFGT == None) + { + Destroy(); + } + + GSU = new(Self) GSUClass; + + InitHelperCommands(); + InitGameSettingsCommands(); + InitGameplayCommands(); + InitPlayerCommands(); + + SetTimer(5.0, true); +} + +event Timer() +{ + /** + * Constantly delaying next ZED-Time event + * so that it will never occur if bZedTimeDisabled is true + */ + if (IsZedTimeDisabled()) + { + KFGT.LastZedTimeEvent = Level.TimeSeconds; + } +} + +public function bool CheckReplacement(Actor Other, out byte bSuperRelevant) +{ + local KFMonster Zed; + + /** + * Changing Monsters HP modifiers depending on + * current HP config and number of players alive + */ + if (KFMonster(Other) != None) + { + Zed = KFMonster(Other); + + Zed.Health = GSU.GetZedModifiedHealth(Zed); + Zed.HealthMax = GSU.GetZedModifiedHealth(Zed); + Zed.HeadHealth = GSU.GetZedModifiedHeadHealth(Zed); + } + + return true; +} + +public function Mutate(string MutateString, PlayerController Sender) +{ + local int MutateCommandIndex; + local Array MutateArgs; + + if (bAllowMutate) + { + Split(MutateString, " ", MutateArgs); + + if (MutateArgs.Length > 0) + { + MutateCommandIndex = GetCommandIndex(MutateArgs[0]); + + if (MutateCommandIndex != -1) + { + Commands[MutateCommandIndex].Execute(Sender, MutateArgs); + } + } + } + + Super.Mutate(MutateString, Sender); +} + +/********************************* + * COMMANDS INITIALIZATION + *********************************/ + +protected function InitHelperCommands() +{ + Commands[CMD_HELP] = new(Self) HelpCommandClass; + Commands[CMD_AHELP] = new(Self) AdminHelpCommandClass; + Commands[CMD_STATUS] = new(Self) StatusCommandClass; +} + +protected function InitGameSettingsCommands() +{ + Commands[CMD_SLOTS] = new(Self) SlotsCommandClass; + Commands[CMD_SPECS] = new(Self) SpecsCommandClass; + Commands[CMD_FAKED] = new(Self) FakesCommandClass; + Commands[CMD_HPCONFIG] = new(Self) HPConfigCommandClass; + Commands[CMD_MAXZEDS] = new(Self) MaxZedsCommandClass; + Commands[CMD_SPAWNRATE] = new(Self) SpawnRateCommandClass; + Commands[CMD_SKIPTRADE] = new(Self) SkipTradeCommandClass; + Commands[CMD_TRADE] = new(Self) TradeTimeCommandClass; + Commands[CMD_ZEDTIME] = new(Self) ZEDTimeCommandClass; + // Commands[CMD_SETWAVE] = new(Self) SetWaveCommandClass; + // Commands[CMD_RESTARTWAVE] = new(Self) RestartWaveCommandClass; +} + +protected function InitGameplayCommands() +{ + // Commands[CMD_RESPAWN] = new(Self) RespawnCommandClass; + // Commands[CMD_RESTORE] = new(Self) RestoreCommandClass; + Commands[CMD_KILLZEDS] = new(Self) KillZedsCommandClass; + // Commands[CMD_WELDDOORS] = new(Self) WeldDoorsCommandClass; + // Commands[CMD_CLEARPIPES] = new(Self) ClearPipesCommandClass; +} + +protected function InitPlayerCommands() +{ + Commands[CMD_SETNAME] = new(Self) SetNameCommandClass; + Commands[CMD_GOD] = new(Self) GodModeCommandClass; + // Commands[CMD_HEADSIZE] = new(Self) HeadSizeCommandClass; + // Commands[CMD_BODYSIZE] = new(Self) BodySizeCommandClass; + // Commands[CMD_HIT] = new(Self) HitPlayerCommandClass; +} + +/********************************* + * LOW-LEVEL LOGIC IMPLEMENTATION + *********************************/ + +protected final function int GetCommandIndex(string Alias) +{ + local int i; + + for (i = 0; i < Commands.Length; i++) + { + if (Commands[i] != None && Commands[i].HasAlias(Alias)) + { + return i; + } + } + + return -1; +} + +defaultproperties +{ + GroupName="KF-THPCommandManager" + FriendlyName="N7 Three Hundred Pounds Command Manager" + Description="Commands API for KFTHPGameType" + + bAllowMutate=true + bZedTimeDisabled=false + ZedHPConfig=1 + ZedHPConfigThreshold=6 + FakedPlayersNum=0 + + GSUClass=Class'KFTHPGameStateUtils' + + HelpCommandClass=Class'KFTHPHelpCommand' + AdminHelpCommandClass=Class'KFTHPAdminHelpCommand' + StatusCommandClass=Class'KFTHPStatusCommand' + + SlotsCommandClass=Class'KFTHPSlotsCommand' + SpecsCommandClass=Class'KFTHPSpectatorsCommand' + FakesCommandClass=Class'KFTHPFakesCommand' + HPConfigCommandClass=Class'KFTHPZedHPConfigCommand' + MaxZedsCommandClass=Class'KFTHPMaxZedsCommand' + SpawnRateCommandClass=Class'KFTHPSpawnRateCommand' + SkipTradeCommandClass=Class'KFTHPSkipTradeCommand' + TradeTimeCommandClass=Class'KFTHPTradeTimeCommand' + ZEDTimeCommandClass=Class'KFTHPZedTimeCommand' + // SetWaveCommandClass=Class'KFTHPSetWaveCommand' + // RestartWaveCommandClass=Class'KFTHPRestartWaveCommand' + + // RespawnCommandClass=Class'KFTHPRespawnCommand' + // RestoreCommandClass=Class'KFTHPRestoreCommand' + KillZedsCommandClass=Class'KFTHPKillZedsCommand' + // WeldDoorsCommandClass=Class'KFTHPWeldDoorsCommand' + // ClearPipesCommandClass=Class'ClearPipesCommand' + + SetNameCommandClass=Class'KFTHPSetNameCommand' + GodModeCommandClass=Class'KFTHPGodModeCommand' + // HeadSizeCommandClass=Class'KFTHPHeadSizeCommand' + // BodySizeCommandClass=Class'KFTHPBodySizeCommand' + // HitPlayerCommandClass=Class'KFTHPHitPlayerCommand' +} diff --git a/Classes/KFTHPCommandPreservingState.uc b/Classes/KFTHPCommandPreservingState.uc new file mode 100644 index 0000000..dc05444 --- /dev/null +++ b/Classes/KFTHPCommandPreservingState.uc @@ -0,0 +1,131 @@ +/** + * This state has extended functionality in terms that it can + * preserve some pieces of intermediary state for later use + * so that there is no need to recalculate same parts again + */ +class KFTHPCommandPreservingState extends KFTHPCommandExecutionState; + +var protected string PreservedString; + +var protected int PreservedNumber; +var protected int PreservedMinLimit; +var protected int PreservedMaxLimit; + +var protected float PreservedNumberF; +var protected float PreservedMinLimitF; +var protected float PreservedMaxLimitF; + +var protected bool bPreservedFlag; + +public final function SaveString(string Value) +{ + PreservedString = Value; +} + +public final function SaveNumber(int Value) +{ + PreservedNumber = Value; +} + +public final function SaveMinLimit(int Value) +{ + PreservedMinLimit = Value; +} + +public final function SaveMaxLimit(int Value) +{ + PreservedMaxLimit = Value; +} + +public final function SaveNumberF(float Value) +{ + PreservedNumberF = Value; +} + +public final function SaveMinLimitF(float Value) +{ + PreservedMinLimitF = Value; +} + +public final function SaveMaxLimitF(float Value) +{ + PreservedMaxLimitF = Value; +} + +public final function SaveFlag(bool bValue) +{ + bPreservedFlag = bValue; +} + +public final function string LoadString() +{ + return PreservedString; +} + +public final function int LoadNumber() +{ + return PreservedNumber; +} + +public final function int LoadMinLimit() +{ + return PreservedMinLimit; +} + +public final function int LoadMaxLimit() +{ + return PreservedMaxLimit; +} + +public final function float LoadNumberF() +{ + return PreservedNumberF; +} + +public final function float LoadMinLimitF() +{ + return PreservedMinLimitF; +} + +public final function float LoadMaxLimitF() +{ + return PreservedMaxLimitF; +} + +public final function bool LoadFlag() +{ + return bPreservedFlag; +} + +public final function string LoadSwitch(optional bool bAllCaps) +{ + if (bPreservedFlag) + { + return EnsureUpperCase("On", bAllCaps); + } + + return EnsureUpperCase("Off", bAllCaps); +} + +public final function string LoadEnabled(optional bool bAllCaps) +{ + if (bPreservedFlag) + { + return EnsureUpperCase("enabled", bAllCaps); + } + + return EnsureUpperCase("disabled", bAllCaps); +} + +protected final function string EnsureUpperCase(string Value, bool bAllCaps) +{ + if (bAllCaps) + { + return Caps(Value); + } + + return Value; +} + +defaultproperties +{} diff --git a/Classes/KFTHPCommandValidator.uc b/Classes/KFTHPCommandValidator.uc new file mode 100644 index 0000000..3730381 --- /dev/null +++ b/Classes/KFTHPCommandValidator.uc @@ -0,0 +1,172 @@ +class KFTHPCommandValidator extends Core.Object; + +const CHR_MINUS = 45; +const CHR_DOT = 46; + +var protected const int MinDigitCode; +var protected const int MaxDigitCode; +var protected const int MinUppercaseLetterCode; +var protected const int MaxUppercaseLetterCode; +var protected const int MinLowercaseLetterCode; +var protected const int MaxLowercaseLetterCode; +var protected const int MaxAcceptableStringLength; + +/********************************* + * STRING VALIDATION + *********************************/ + +static function bool IsEmptyString(string Str) +{ + return Str == ""; +} + +static function int GetMaxAcceptableStringLength() +{ + return default.MaxAcceptableStringLength; +} + +static function bool IsValidLengthString(string Str) +{ + return Len(Str) <= default.MaxAcceptableStringLength; +} + +static function bool IsStringPartOf(string SubStr, string SupStr) +{ + return InStr(Caps(SupStr), Caps(SubStr)) >= 0; +} + +static function bool IsLetter(string Str) +{ + local string Ch; + Ch = Mid(Str, 0, 1); + + return IsInRange(Asc(Ch), default.MinUppercaseLetterCode, default.MaxUppercaseLetterCode) + || IsInRange(Asc(Ch), default.MinLowercaseLetterCode, default.MaxLowercaseLetterCode); +} + +static function bool IsWord(string Str) +{ + local int i; + + for (i = 0; i < Len(Str); i++) + { + if (!IsLetter(Mid(Str, i, 1))) + { + return false; + } + } + + return true; +} + +static function bool IsDigit(string Str) +{ + local string Ch; + Ch = Mid(Str, 0, 1); + + return IsInRange(Asc(Ch), default.MinDigitCode, default.MaxDigitCode); +} + +static function bool IsNumber(string Str) +{ + local int i; + local string Ch; + local bool bFloatingPointFound; + + for (i = 0; i < Len(Str); i++) + { + Ch = Mid(Str, i, 1); + + if (Asc(Ch) == CHR_DOT && !bFloatingPointFound) + { + bFloatingPointFound = true; + continue; + } + else if (Asc(Ch) == CHR_DOT) + { + return false; + } + + if (Asc(Ch) == CHR_MINUS && i == 0 && Len(Str) > 0) + { + return false; + } + + if (!IsDigit(Ch)) + { + return false; + } + } + + return true; +} + +static function bool IsSwitchOnValue(string Value) +{ + return Value ~= "ON" || Value ~= "1"; +} + +static function bool IsSwitchOffValue(string Value) +{ + return Value ~= "OFF" || Value ~= "0"; +} + +static function bool IsSwitchValue(string Value) +{ + return IsSwitchOnValue(Value) || IsSwitchOffValue(Value); +} + +/********************************* + * NUMBER VALIDATION + *********************************/ + +static function bool IsInRange( + coerce int Number, + int Start, + optional int End) +{ + if (End > 0) + { + return Number >= Start && Number <= End; + } + + return Number >= Start; +} + +static function bool IsInRangeF( + coerce float Number, + float Start, + optional float End) +{ + if (End > 0) + { + return Number >= Start && Number <= End; + } + + return Number >= Start; +} + +/********************************* + * GAME OBJECTS VALIDATION + *********************************/ + +static function bool IsPlayer(Controller C) +{ + return C.IsA('PlayerController') || C.IsA('xBot'); +} + +static function bool IsAlive(PlayerController PC) +{ + return PC.Pawn.Health > 0; +} + +defaultproperties +{ + MinDigitCode=48 + MaxDigitCode=57 + MinUppercaseLetterCode=65 + MaxUppercaseLetterCode=90 + MinLowercaseLetterCode=97 + MaxLowercaseLetterCode=122 + MaxAcceptableStringLength=25 +} diff --git a/Classes/KFTHPFakesCommand.uc b/Classes/KFTHPFakesCommand.uc new file mode 100644 index 0000000..467bfef --- /dev/null +++ b/Classes/KFTHPFakesCommand.uc @@ -0,0 +1,67 @@ +class KFTHPFakesCommand extends KFTHPGameSettingsCommand; + +enum ECmdArgs +{ + ARG_NEWFAKES, +}; + +var protected const int MinFakes; +var protected const int MaxFakes; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local int NewFakes; + + NewFakes = ToInt(ExecState.GetArg(ECmdArgs.ARG_NEWFAKES)); + SetFakedPlayersNum(NewFakes); + + KFGT.NumPlayers = GSU.GetRealPlayersNum() + GetFakedPlayersNum(); +} + +/** @Override */ +protected function bool CheckArgs(KFTHPCommandExecutionState ExecState) +{ + local int NewFakes, MaxFakesActual; + + NewFakes = ToInt(ExecState.GetArg(ECmdArgs.ARG_NEWFAKES)); + MaxFakesActual = Min(KFGT.MaxPlayers - (KFGT.NumPlayers - FakedPlayersNum), MaxFakes); + + if (!IsInRange(NewFakes, MinFakes, MaxFakesActual)) + { + KFTHPCommandPreservingState(ExecState).SaveMaxLimit(MaxFakesActual); + + return false; + } + + return true; +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + return "Faked Players set to "$(KFGT.NumPlayers - GSU.GetRealPlayersNum())$" by "$GetInstigatorName(ExecState); +} + +/** @Override */ +protected function string InvalidArgsMessage(KFTHPCommandExecutionState ExecState) +{ + return "Faked players number must be in range from "$MinFakes$" to "$KFTHPCommandPreservingState(ExecState).LoadMaxLimit(); +} + +defaultproperties +{ + MinFakes=0 + MaxFakes=10 + MinArgsNum=1 + MaxArgsNum=1 + Aliases(0)="FAKE" + Aliases(1)="FAKES" + Aliases(2)="FAKED" + Aliases(3)="SETFAKED" + Aliases(4)="SETFAKES" + ArgTypes(0)="number" + Signature="" + Description="Set number of faked players" + CommandStateClass=Class'KFTHPCommandPreservingState' +} diff --git a/Classes/KFTHPGameSettingsCommand.uc b/Classes/KFTHPGameSettingsCommand.uc new file mode 100644 index 0000000..5d13baf --- /dev/null +++ b/Classes/KFTHPGameSettingsCommand.uc @@ -0,0 +1,18 @@ +/** + * This class provides general abstraction for commands + * whose purpose is to change some game settings + */ +class KFTHPGameSettingsCommand extends KFTHPCommand + abstract; + +/** Will return the instigator of the setting change */ +protected function string GetInstigatorName(KFTHPCommandExecutionState ExecState) +{ + return ExecState.GetSender().PlayerReplicationInfo.PlayerName; +} + +defaultproperties +{ + bNotifySenderOnSuccess=false + bNotifyGlobalOnSuccess=true +} diff --git a/Classes/KFTHPGameStateUtils.uc b/Classes/KFTHPGameStateUtils.uc new file mode 100644 index 0000000..8d48534 --- /dev/null +++ b/Classes/KFTHPGameStateUtils.uc @@ -0,0 +1,80 @@ +class KFTHPGameStateUtils extends Core.Object within KFTHPCommandManager; + +public final function int GetAlivePlayersNum() +{ + local Controller C; + local int AlivePlayersNum; + + for (C = Level.ControllerList; C != None; C = C.NextController) + { + if ((C.IsA('PlayerController') || C.IsA('xBot')) && C.Pawn != None && C.Pawn.Health > 0) + { + AlivePlayersNum++; + } + } + return AlivePlayersNum; +} + +public final function int GetRealPlayersNum() +{ + local Controller C; + local int RealPlayersNum; + + for (C = Level.ControllerList; C != None; C = C.NextController) + { + if ((C.IsA('PlayerController') || C.IsA('xBot')) && MessagingSpectator(C) == None) + { + RealPlayersNum++; + } + } + return RealPlayersNum; +} + +public final function int GetFinalZedHPConfig() +{ + local int AlivePlayersNum; + + AlivePlayersNum = GetAlivePlayersNum(); + + if (AlivePlayersNum >= GetZedHPConfigThreshold()) + { + return GetZedHPConfig(); + } + + return Max(AlivePlayersNum, GetZedHPConfig()); +} + +public final function int GetZedModifiedHealth(KFMonster Zed) +{ + local float ZedRawHealth, ZedHealthModifiedByDifficulty, ZedModifiedHealth; + + ZedRawHealth = Zed.default.Health; + ZedHealthModifiedByDifficulty = ZedRawHealth * Zed.DifficultyHealthModifer(); + ZedModifiedHealth = ZedHealthModifiedByDifficulty * GetZedHPModifierByPlayers(Zed); + + return ZedModifiedHealth; +} + +public final function int GetZedModifiedHeadHealth(KFMonster Zed) +{ + local float ZedRawHeadHealth, ZedHeadHealthModifiedByDifficulty, ZedModifiedHeadHealth; + + ZedRawHeadHealth = Zed.default.HeadHealth; + ZedHeadHealthModifiedByDifficulty = ZedRawHeadHealth * Zed.DifficultyHeadHealthModifer(); + ZedModifiedHeadHealth = ZedHeadHealthModifiedByDifficulty * GetZedHeadHPModifierByPlayers(Zed); + + return ZedModifiedHeadHealth; +} + +protected final function float GetZedHPModifierByPlayers(KFMonster Zed) +{ + return 1.0 + Zed.PlayerCountHealthScale * FMax(GetFinalZedHPConfig() - 1.0, 0); +} + +protected final function float GetZedHeadHPModifierByPlayers(KFMonster Zed) +{ + return 1.0 + Zed.PlayerNumHeadHealthScale * FMax(GetFinalZedHPConfig() - 1.0, 0); +} + +defaultproperties +{} diff --git a/Classes/KFTHPGodModeCommand.uc b/Classes/KFTHPGodModeCommand.uc new file mode 100644 index 0000000..da1d3b2 --- /dev/null +++ b/Classes/KFTHPGodModeCommand.uc @@ -0,0 +1,129 @@ +class KFTHPGodModeCommand extends KFTHPTargetCommand; + +enum ECmdArgs +{ + ARG_FLAG, + ARG_TARGETNAME, +}; + +/** @Override */ +protected function DoActionForSingleTarget + (KFTHPCommandExecutionState ExecState, PlayerController PC) +{ + switch (ExecState.GetArgC()) + { + case 0: + PC.bGodMode = !PC.bGodMode; + break; + + case 1: + case 2: + if (IsSwitchOnValue(ExecState.GetArg(ECmdArgs.ARG_FLAG))) + { + PC.bGodMode = true; + } + else + { + PC.bGodMode = false; + } + break; + } + + KFTHPCommandPreservingState(ExecState).SaveFlag(PC.bGodMode); +} + +/** @Override */ +protected function bool CheckTargets(KFTHPCommandExecutionState ExecState) +{ + local string TargetName; + + switch (ExecState.GetArgC()) + { + case 0: + case 1: + return true; + + case 2: + TargetName = ExecState.GetArg(ECmdArgs.ARG_TARGETNAME); + KFTHPCommandPreservingState(ExecState).SaveString(TargetName); + + return TargetName ~= "all" || DoesTargetExist(TargetName); + } + + return false; +} + +/** @Override */ +protected function bool ShouldBeTarget( + KFTHPCommandExecutionState ExecState, + PlayerController PC) +{ + local string TargetName; + + switch (ExecState.GetArgC()) + { + case 0: + case 1: + return PC == ExecState.GetSender(); + + case 2: + TargetName = ExecState.GetArg(ECmdArgs.ARG_TARGETNAME); + + if (TargetName ~= "all") + { + return true; + } + return IsStringPartOf(TargetName, PC.PlayerReplicationInfo.PlayerName); + } + + return false; +} + +/** @Override */ +protected function string InvalidTargetMessage(KFTHPCommandExecutionState ExecState) +{ + return "Cannot find player with name "$KFTHPCommandPreservingState(ExecState).LoadString(); +} + +/** @Override */ +protected function string GetTargetSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + local string TargetName; + TargetName = KFTHPCommandPreservingState(ExecState).LoadString(); + + if (TargetName ~= "all") + { + return "All players' God Mode is "$KFTHPCommandPreservingState(ExecState).LoadEnabled(); + } + + return "God Mode is "$KFTHPCommandPreservingState(ExecState).LoadEnabled(); +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + local string TargetName; + TargetName = KFTHPCommandPreservingState(ExecState).LoadString(); + + if (TargetName ~= "all") + { + return "All players' God Mode is "$KFTHPCommandPreservingState(ExecState).LoadEnabled(); + } + + return TargetName$"'s God Mode is "$KFTHPCommandPreservingState(ExecState).LoadEnabled(); +} + +defaultproperties +{ + MinArgsNum=0 + MaxArgsNum=2 + Aliases(0)="GM" + Aliases(1)="GOD" + ArgTypes(0)="switch" + ArgTypes(1)="any" + Signature="" + Description="Toggle God Mode" + bAdminOnly=true + bNotifyGlobalOnSuccess=true + CommandStateClass=Class'KFTHPCommandPreservingState' +} diff --git a/Classes/KFTHPHelpCommand.uc b/Classes/KFTHPHelpCommand.uc new file mode 100644 index 0000000..31c7d00 --- /dev/null +++ b/Classes/KFTHPHelpCommand.uc @@ -0,0 +1,90 @@ +class KFTHPHelpCommand extends KFTHPCommand; + +enum ECmdArgs +{ + ARG_COMMAND, +}; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local string CommandString; + local KFTHPCommand RequestedCommand; + local int i; + + switch (ExecState.GetArgC()) + { + case 0: + for (i = 0; i < Commands.Length; i++) + { + if (Commands[i] != None && ShouldDisplayHelpForCommand(Commands[i])) + { + DisplayHelpForCommand(ExecState.GetSender(), Commands[i]); + } + } + break; + + case 1: + CommandString = ExecState.GetArg(ECmdArgs.ARG_COMMAND); + RequestedCommand = GetCommand(CommandString); + + DisplayHelpForCommand(ExecState.GetSender(), RequestedCommand); + break; + } +} + +/** @Override */ +protected function bool CheckArgs(KFTHPCommandExecutionState ExecState) +{ + local string CommandString; + + if (ExecState.GetArgC() == 1) + { + CommandString = ExecState.GetArg(ECmdArgs.ARG_COMMAND); + return GetCommand(CommandString) != None; + } + + return true; +} + +protected final function DisplayHelpForCommand(PlayerController Target, KFTHPCommand Command) +{ + SendMessage(Target, Command.GetHelpString()); +} + +protected function bool ShouldDisplayHelpForCommand(KFTHPCommand Command) +{ + return !Command.IsAdminOnly(); +} + +protected function KFTHPCommand GetCommand(string CommandString) +{ + local int i; + + for (i = 0; i < Commands.Length; i++) + { + if (Commands[i] != None && Commands[i].HasAlias(Caps(CommandString))) + { + return Commands[i]; + } + } + + return None; +} + +/** @Override */ +protected function string InvalidArgsMessage(KFTHPCommandExecutionState ExecState) +{ + return "Unrecognized command"; +} + +defaultproperties +{ + MinArgsNum=0 + MaxArgsNum=1 + Aliases(0)="HELP" + ArgTypes(0)="word" + Signature="" + Description="Show a list of commands available to players or display info about one specific command" + bNotifySenderOnSuccess=false +} diff --git a/Classes/KFTHPKillZedsCommand.uc b/Classes/KFTHPKillZedsCommand.uc new file mode 100644 index 0000000..873428e --- /dev/null +++ b/Classes/KFTHPKillZedsCommand.uc @@ -0,0 +1,87 @@ +class KFTHPKillZedsCommand extends KFTHPGameSettingsCommand; + +enum ECmdArgs +{ + ARG_TARGET, +}; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local KFGameReplicationInfo KFGRI; + KFGRI = KFGameReplicationInfo(Level.Game.GameReplicationInfo); + + KillLivingZeds(); + + if (ExecState.GetArgC() == 1) + { + KFGT.NumMonsters = 0; + KFGT.TotalMaxMonsters = 0; + KFGT.MaxMonsters = 0; + KFGRI.MaxMonsters = 0; + } +} + +protected function KillLivingZeds() +{ + local KFMonster TargetMonster; + + foreach DynamicActors(Class'KFMonster', TargetMonster) + { + if (TargetMonster.Health > 0 && !TargetMonster.bDeleteMe) + { + KillZed(TargetMonster); + } + } +} + +protected function KillZed(KFMonster TargetMonster) +{ + TargetMonster.Died(TargetMonster.Controller, Class'DamageType', TargetMonster.Location); +} + +/** @Override */ +protected function bool CheckArgs(KFTHPCommandExecutionState ExecState) +{ + local string TargetString; + + if (ExecState.GetArgC() == 1) + { + TargetString = Locs(ExecState.GetArg(ECmdArgs.ARG_TARGET)); + + if (TargetString != "all") + { + return false; + } + } + + return true; +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + if (ExecState.GetArgC() == 1) + { + return "Wave cleared by "$GetInstigatorName(ExecState); + } + + return "ZED Squads killed by "$GetInstigatorName(ExecState); +} + +/** @Override */ +protected function string InvalidArgsMessage(KFTHPCommandExecutionState ExecState) +{ + return "Command only accepts 'all' as an argument"; +} + +defaultproperties +{ + MinArgsNum=0 + MaxArgsNum=1 + Aliases(0)="KZ" + Aliases(1)="KILLZEDS" + ArgTypes(0)="word" + Signature="" + Description="Kill ZEDs" +} diff --git a/Classes/KFTHPMaxZedsCommand.uc b/Classes/KFTHPMaxZedsCommand.uc new file mode 100644 index 0000000..1e2726e --- /dev/null +++ b/Classes/KFTHPMaxZedsCommand.uc @@ -0,0 +1,62 @@ +class KFTHPMaxZedsCommand extends KFTHPGameSettingsCommand; + +enum ECmdArgs +{ + ARG_MAXZEDS, +}; + +var protected const int MinLimit; +var protected const int MaxLimit; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local int NewMaxZeds; + + NewMaxZeds = ToInt(ExecState.GetArg(ECmdArgs.ARG_MAXZEDS)); + + KFGT.MaxZombiesOnce = NewMaxZeds; + KFGT.StandardMaxZombiesOnce = NewMaxZeds; + KFGT.MaxMonsters = NewMaxZeds; +} + +/** @Override */ +protected function bool CheckArgs(KFTHPCommandExecutionState ExecState) +{ + local int NewMaxZeds; + + NewMaxZeds = ToInt(ExecState.GetArg(ECmdArgs.ARG_MAXZEDS)); + + if (!IsInRange(NewMaxZeds, MinLimit, MaxLimit)) + { + return false; + } + + return true; +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + return "Max Zeds set to "$KFGT.MaxZombiesOnce$" by "$GetInstigatorName(ExecState); +} + +/** @Override */ +protected function string InvalidArgsMessage(KFTHPCommandExecutionState ExecState) +{ + return "Max Zeds number must be in range from "$MinLimit$" to "$MaxLimit; +} + +defaultproperties +{ + MinLimit=4 + MaxLimit=96 + MinArgsNum=1 + MaxArgsNum=1 + Aliases(0)="MZ" + Aliases(1)="SETMZ" + Aliases(2)="MAXZEDS" + ArgTypes(0)="number" + Signature="" + Description="Set max number of ZEDs present at a time" +} diff --git a/Classes/KFTHPSetNameCommand.uc b/Classes/KFTHPSetNameCommand.uc new file mode 100644 index 0000000..b51efef --- /dev/null +++ b/Classes/KFTHPSetNameCommand.uc @@ -0,0 +1,57 @@ +class KFTHPSetNameCommand extends KFTHPCommand; + +enum ECmdArgs +{ + ARG_NAME, +}; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local string OldName, NewName; + + OldName = ExecState.GetSender().PlayerReplicationInfo.PlayerName; + NewName = ExecState.GetArg(ECmdArgs.ARG_NAME); + + KFTHPCommandPreservingState(ExecState).SaveString(OldName); + ExecState.GetSender().PlayerReplicationInfo.PlayerName = NewName; +} + +/** @Override */ +protected function bool ShouldNotifyGlobalOnSuccess() +{ + return true; +} + +/** @Override */ +protected function string GetSenderSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + local string NewName; + + NewName = ExecState.GetArg(ECmdArgs.ARG_NAME); + + return "Your name has been changed to "$NewName; +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + local string NewName; + + NewName = ExecState.GetArg(ECmdArgs.ARG_NAME); + + return KFTHPCommandPreservingState(ExecState).LoadString()$"'s name has been changed to "$NewName; +} + +defaultproperties +{ + MinArgsNum=1 + MaxArgsNum=1 + Aliases(0)="CN" + Aliases(1)="SN" + Aliases(2)="SETNAME" + ArgTypes(0)="any" + Signature="" + Description="Set new name" + CommandStateClass=Class'KFTHPCommandPreservingState' +} diff --git a/Classes/KFTHPSkipTradeCommand.uc b/Classes/KFTHPSkipTradeCommand.uc new file mode 100644 index 0000000..bd1c94b --- /dev/null +++ b/Classes/KFTHPSkipTradeCommand.uc @@ -0,0 +1,22 @@ +class KFTHPSkipTradeCommand extends KFTHPTradeTimeCommand; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + KFGT.WaveCountDown = MinTradeTime; +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + return "Trader skipped by "$GetInstigatorName(ExecState); +} + +defaultproperties +{ + MinArgsNum=0 + MaxArgsNum=0 + Aliases(0)="SKIP" + Signature="<>" + Description="Skip trader time" +} diff --git a/Classes/KFTHPSlotsCommand.uc b/Classes/KFTHPSlotsCommand.uc new file mode 100644 index 0000000..53dc3b7 --- /dev/null +++ b/Classes/KFTHPSlotsCommand.uc @@ -0,0 +1,59 @@ +class KFTHPSlotsCommand extends KFTHPGameSettingsCommand; + +enum ECmdArgs +{ + ARG_NEWSLOTS, +}; + +var protected const int MinSlots; +var protected const int MaxSlots; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local int NewSlots; + + NewSlots = ToInt(ExecState.GetArg(ECmdArgs.ARG_NEWSLOTS)); + KFGT.MaxPlayers = NewSlots; +} + +/** @Override */ +protected function bool CheckArgs(KFTHPCommandExecutionState ExecState) +{ + local int NewSlots, MinSlotsActual; + + NewSlots = ToInt(ExecState.GetArg(ECmdArgs.ARG_NEWSLOTS)); + MinSlotsActual = Max(KFGT.NumPlayers, MinSlots); + + if (!IsInRange(NewSlots, MinSlotsActual, MaxSlots)) + { + return false; + } + + return true; +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + return "Max Players set to "$KFGT.MaxPlayers$" by "$GetInstigatorName(ExecState); +} + +/** @Override */ +protected function string InvalidArgsMessage(KFTHPCommandExecutionState ExecState) +{ + return "New slots number must be in range from "$Max(KFGT.NumPlayers, MinSlots)$" to "$MaxSlots; +} + +defaultproperties +{ + MinSlots=1 + MaxSlots=10 + MinArgsNum=1 + MaxArgsNum=1 + Aliases(0)="SLOT" + Aliases(1)="SLOTS" + ArgTypes(0)="number" + Signature="" + Description="Set maximum number of players allowed" +} diff --git a/Classes/KFTHPSpawnRateCommand.uc b/Classes/KFTHPSpawnRateCommand.uc new file mode 100644 index 0000000..354ffae --- /dev/null +++ b/Classes/KFTHPSpawnRateCommand.uc @@ -0,0 +1,58 @@ +class KFTHPSpawnRateCommand extends KFTHPGameSettingsCommand; + +enum ECmdArgs +{ + ARG_SPAWNRATE, +}; + +var protected const float MinRate; +var protected const float MaxRate; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local float NewSpawnRate; + + NewSpawnRate = ToFloat(ExecState.GetArg(ECmdArgs.ARG_SPAWNRATE)); + KFGT.KFLRules.WaveSpawnPeriod = NewSpawnRate; +} + +/** @Override */ +protected function bool CheckArgs(KFTHPCommandExecutionState ExecState) +{ + local float NewSpawnRate; + + NewSpawnRate = ToFloat(ExecState.GetArg(ECmdArgs.ARG_SPAWNRATE)); + + if (!IsInRangeF(NewSpawnRate, MinRate, MaxRate)) + { + return false; + } + + return true; +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + return "Spawn Rate set to "$KFGT.KFLRules.WaveSpawnPeriod$" by "$GetInstigatorName(ExecState); +} + +/** @Override */ +protected function string InvalidArgsMessage(KFTHPCommandExecutionState ExecState) +{ + return "Spawn Rate number must be in range from "$MinRate$" to "$MaxRate; +} + +defaultproperties +{ + MinRate=0.0 + MaxRate=10.0 + MinArgsNum=1 + MaxArgsNum=1 + Aliases(0)="SR" + Aliases(1)="SETSR" + ArgTypes(0)="number" + Signature="" + Description="Set interval between ZED squads spawn" +} diff --git a/Classes/KFTHPSpectatorsCommand.uc b/Classes/KFTHPSpectatorsCommand.uc new file mode 100644 index 0000000..cb29f01 --- /dev/null +++ b/Classes/KFTHPSpectatorsCommand.uc @@ -0,0 +1,59 @@ +class KFTHPSpectatorsCommand extends KFTHPGameSettingsCommand; + +enum ECmdArgs +{ + ARG_NEWSPECS, +}; + +var protected const int MinSpectators; +var protected const int MaxSpectators; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local int NewSpectators; + + NewSpectators = ToInt(ExecState.GetArg(ECmdArgs.ARG_NEWSPECS)); + KFGT.MaxSpectators = NewSpectators; +} + +/** @Override */ +protected function bool CheckArgs(KFTHPCommandExecutionState ExecState) +{ + local int NewSpectators, MinSpectatorsActual; + + NewSpectators = ToInt(ExecState.GetArg(ECmdArgs.ARG_NEWSPECS)); + MinSpectatorsActual = Max(KFGT.NumSpectators, MinSpectators); + + if (!IsInRange(NewSpectators, MinSpectatorsActual, MaxSpectators)) + { + return false; + } + + return true; +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + return "Max Spectators set to "$KFGT.MaxSpectators$" by "$GetInstigatorName(ExecState); +} + +/** @Override */ +protected function string InvalidArgsMessage(KFTHPCommandExecutionState ExecState) +{ + return "New Spectators number must be in range from "$Max(KFGT.NumSpectators, MinSpectators)$" to "$MaxSpectators; +} + +defaultproperties +{ + MinSpectators=0 + MaxSpectators=10 + MinArgsNum=1 + MaxArgsNum=1 + Aliases(0)="SPEC" + Aliases(1)="SPECS" + ArgTypes(0)="number" + Signature="" + Description="Set maximum of spectators allowed" +} diff --git a/Classes/KFTHPStatusCommand.uc b/Classes/KFTHPStatusCommand.uc new file mode 100644 index 0000000..02179e5 --- /dev/null +++ b/Classes/KFTHPStatusCommand.uc @@ -0,0 +1,31 @@ +class KFTHPStatusCommand extends KFTHPCommand; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + SendMessage( + ExecState.GetSender(), + "MZ - "$KFGT.MaxZombiesOnce$" | "$"FAKES - "$FakedPlayersNum$" | "$"HP - "$GSU.GetFinalZedHPConfig()$" | "$"SR - "$KFGT.KFLRules.WaveSpawnPeriod + ); + SendMessage( + ExecState.GetSender(), + "SLOTS - "$KFGT.MaxPlayers$" | "$"SPECS - "$KFGT.MaxSpectators + ); + + if (IsZedTimeDisabled()) + { + SendMessage(ExecState.GetSender(), "ZED-Time is disabled"); + } + else + { + SendMessage(ExecState.GetSender(), "ZED-Time is enabled"); + } +} + +defaultproperties +{ + Aliases(0)="STATUS" + Signature="<>" + Description="Display current game settings" + bNotifySenderOnSuccess=false +} diff --git a/Classes/KFTHPTargetCommand.uc b/Classes/KFTHPTargetCommand.uc new file mode 100644 index 0000000..58bbf5f --- /dev/null +++ b/Classes/KFTHPTargetCommand.uc @@ -0,0 +1,46 @@ +/** + * This class provides common logic for commands + * that are supposed to affect a selection of players + */ +class KFTHPTargetCommand extends KFTHPCommand + abstract; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local int i; + + for (i = 0; i < ExecState.GetTargetsNum(); i++) + { + DoActionForSingleTarget(ExecState, ExecState.GetTarget(i)); + } +} + +/** @Override */ +protected function bool ShouldBeTarget( + KFTHPCommandExecutionState ExecState, + PlayerController PC) +{ + return true; +} + +protected function bool DoesTargetExist(string TargetName) +{ + local Controller C; + + for (C = Level.ControllerList; C != None; C = C.NextController) + { + if (IsPlayer(C) && IsStringPartOf(TargetName, PlayerController(C).PlayerReplicationInfo.PlayerName)) + { + return true; + } + } + + return false; +} + +defaultproperties +{ + bNotifySenderOnSuccess=false + bNotifyTargetsOnSuccess=true +} diff --git a/Classes/KFTHPTradeTimeCommand.uc b/Classes/KFTHPTradeTimeCommand.uc new file mode 100644 index 0000000..61c422b --- /dev/null +++ b/Classes/KFTHPTradeTimeCommand.uc @@ -0,0 +1,67 @@ +class KFTHPTradeTimeCommand extends KFTHPGameSettingsCommand; + +enum ECmdArgs +{ + ARG_NEWTIME, +}; + +var protected const int MinTradeTime; +var protected const int MaxTradeTime; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local int NewTradeTime; + + NewTradeTime = ToInt(ExecState.GetArg(ECmdArgs.ARG_NEWTIME)); + + if (IsInRange(NewTradeTime, MinTradeTime, MaxTradeTime)) + { + KFGT.WaveCountDown = NewTradeTime; + } + else if (IsInRange(NewTradeTime, MinTradeTime)) + { + KFGT.WaveCountDown = MaxTradeTime; + } + else + { + KFGT.WaveCountDown = MinTradeTime; + } +} + +/** @Override */ +protected function bool CheckCustom(KFTHPCommandExecutionState ExecState) +{ + if (!KFGT.bTradingDoorsOpen) + { + return false; + } + + return true; +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + return "Trade Time set to "$KFGT.WaveCountDown$"s by "$GetInstigatorName(ExecState); +} + +/** @Override */ +protected function string CustomErrorMessage(KFTHPCommandExecutionState ExecState) +{ + return "Trader Time cannot be set during the wave"; +} + +defaultproperties +{ + MinArgsNum=1 + MaxArgsNum=1 + MinTradeTime=6 + MaxTradeTime=600 + Aliases(0)="TT" + Aliases(1)="TRADE" + Aliases(2)="SETTRADE" + ArgTypes(0)="number" + Signature="" + Description="Set trader time in seconds" +} diff --git a/Classes/KFTHPZedHPConfigCommand.uc b/Classes/KFTHPZedHPConfigCommand.uc new file mode 100644 index 0000000..7c8766e --- /dev/null +++ b/Classes/KFTHPZedHPConfigCommand.uc @@ -0,0 +1,67 @@ +class KFTHPZedHPConfigCommand extends KFTHPGameSettingsCommand; + +enum ECmdArgs +{ + ARG_HPCONFIG, +}; + +var protected const int MinLimit; +var protected const int MaxLimit; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local int NewHPConfig; + + NewHPConfig = ToInt(ExecState.GetArg(ECmdArgs.ARG_HPCONFIG)); + SetZedHPConfig(NewHPConfig); +} + +/** @Override */ +protected function bool CheckArgs(KFTHPCommandExecutionState ExecState) +{ + local int NewHPConfig, MinLimitActual; + + NewHPConfig = ToInt(ExecState.GetArg(ECmdArgs.ARG_HPCONFIG)); + + MinLimitActual = Min( + ZedHPConfigThreshold, + Max(MinLimit, GSU.GetAlivePlayersNum()) + ); + + if (!IsInRange(NewHPConfig, MinLimitActual, MaxLimit)) + { + KFTHPCommandPreservingState(ExecState).SaveMinLimit(MinLimitActual); + + return false; + } + + + return true; +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + return "HP Config set to "$GSU.GetFinalZedHPConfig()$" by "$GetInstigatorName(ExecState); +} + +/** @Override */ +protected function string InvalidArgsMessage(KFTHPCommandExecutionState ExecState) +{ + return "HP config must be in range from "$KFTHPCommandPreservingState(ExecState).LoadMinLimit()$" to "$MaxLimit; +} + +defaultproperties +{ + MinLimit=1 + MaxLimit=10 + MinArgsNum=1 + MaxArgsNum=1 + Aliases(0)="HP" + Aliases(1)="SETHP" + ArgTypes(0)="number" + Signature="" + Description="Set HP multiplier for ZEDs" + CommandStateClass=Class'KFTHPCommandPreservingState' +} diff --git a/Classes/KFTHPZedTimeCommand.uc b/Classes/KFTHPZedTimeCommand.uc new file mode 100644 index 0000000..2c6bfd6 --- /dev/null +++ b/Classes/KFTHPZedTimeCommand.uc @@ -0,0 +1,49 @@ +class KFTHPZedTimeCommand extends KFTHPGameSettingsCommand; + +enum ECmdArgs +{ + ARG_FLAG, +}; + +/** @Override */ +protected function DoAction(KFTHPCommandExecutionState ExecState) +{ + local string Flag; + + if (ExecState.GetArgC() == 1) + { + Flag = ExecState.GetArg(ECmdArgs.ARG_FLAG); + + if (IsSwitchOnValue(Flag)) + { + SetZedTime(false); + } + else if (IsSwitchOffValue(Flag)) + { + SetZedTime(true); + } + } + else + { + SetZedTime(!IsZedTimeDisabled()); + } + + KFTHPCommandPreservingState(ExecState).SaveFlag(!IsZedTimeDisabled()); +} + +/** @Override */ +protected function string GetGlobalSuccessMessage(KFTHPCommandExecutionState ExecState) +{ + return "ZED-Time is "$KFTHPCommandPreservingState(ExecState).LoadEnabled()$" by "$GetInstigatorName(ExecState); +} + +defaultproperties +{ + MinArgsNum=0 + MaxArgsNum=1 + Aliases(0)="ZT" + ArgTypes(0)="switch" + Signature="" + Description="Toggle ZED-Time" + CommandStateClass=Class'KFTHPCommandPreservingState' +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..a59de8d --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# Three Hundred Pounds Command Manager + +## Description +Mutate API for Killing Floor Three Hundred Pounds server. +Provides various commands for both players and admins to change game settings and trigger gameplay actions/events. + +*Work in Progress*