Compare commits

...

6 Commits

  1. 55
      config/FutilityChat.ini
  2. 142
      config/FutilityNicknames.ini
  3. 120
      sources/Commands/ACommandDB.uc
  4. 35
      sources/Commands/ACommandDosh.uc
  5. 465
      sources/Commands/ACommandFeature.uc
  6. 150
      sources/Commands/ACommandFeature_Announcer.uc
  7. 30
      sources/Commands/ACommandGod.uc
  8. 66
      sources/Commands/ACommandInventory.uc
  9. 38
      sources/Commands/ACommandNick.uc
  10. 102
      sources/Commands/ACommandSpawn.uc
  11. 104
      sources/Commands/ACommandTrader.uc
  12. 142
      sources/Commands/ACommandUserData.uc
  13. 112
      sources/Features/FutileChat/FutilityChat.uc
  14. 114
      sources/Features/FutileChat/FutilityChat_Feature.uc
  15. 164
      sources/Features/FutileNickames/FutilityNicknames.uc
  16. 368
      sources/Features/FutileNickames/FutilityNicknames_Feature.uc
  17. 12
      sources/Futility.uc
  18. 6
      sources/Futility_Feature.uc
  19. 10
      sources/Tools/PendingConfigsTool.uc

55
config/FutilityChat.ini

@ -1,31 +1,34 @@
[default FutilityChat]
; This feature allows to configure color of text chat messages.
autoEnable=true
; How to color text chat messages?
; 1. `CCS_DoNothing` - do not change color in any way;
; 2. `CCS_TeamColorForced` - force players' team colors for
; their messages;
; 3. `CCS_ConfigColorForced` - force `configuredColor` value for
; players' messages;
; 4. `CCS_TeamColorCustom` - use players' team colors for
; their messages by default, but allow to change color with formatted
; tags (e.g. "Stop right there, {$crimson criminal} scum!");
; 5. `CCS_ConfigColorCustom` - use `configuredColor` value for
; messages by default, but allow to change color with formatted
; tags (e.g. "Stop right there, {$crimson criminal} scum!");
; Default is `CCS_DoNothing`, corresponding to vanilla behaviour.
;= How to color text chat messages?
;=
;= 1. `CCS_DoNothing` - do not change color in any way;
;= 2. `CCS_TeamColorForced` - force players' team colors for
;= their messages;
;= 3. `CCS_ConfigColorForced` - force `configuredColor` value for
;= players' messages;
;= 4. `CCS_TeamColorCustom` - use players' team colors for
;= their messages by default, but allow to change color with formatted tags
;= (e.g. "Stop right there, {$crimson criminal} scum!");
;= 5. `CCS_ConfigColorCustom` - use `configuredColor` value for
;= messages by default, but allow to change color with formatted
;= tags (e.g. "Stop right there, {$crimson criminal} scum!");
;=
;= Default is `CCS_DoNothing`, corresponding to vanilla behaviour.
colorSetting=CCS_DoNothing
; Color that will be used if either of `CCS_ConfigColorForced` or
; `CCS_ConfigColorCustom` options were used in `colorSetting`.
; Default value is white: (R=255,G=255,B=255,A=255),
; has no vanilla equivalent.
;= Color that will be used if either of `CCS_ConfigColorForced` or
;= `CCS_ConfigColorCustom` options were used in `colorSetting`.
;= Default value is white: (R=255,G=255,B=255,A=255), has no vanilla
;= equivalent.
configuredColor=(R=255,G=255,B=255,A=255)
; Allows to modify team color's value for the chat messages
; (if either of `CCS_TeamColorForced` or `CCS_TeamColorCustom` options
; were used) to be lighter or darker.
; This value is clamped between -1 and 1.
; * `0` means using the same color;
; * range (0; 1) - gives you lighter colors (`1` being white);
; * range (-1; 0) - gives you darker colors (`-1` being black);
; Default value is `0.6`, has no vanilla equivalent.
;= Allows to modify team color's value for the chat messages
;= (if either of `CCS_TeamColorForced` or `CCS_TeamColorCustom` options
;= were used) to be lighter or darker.
;= This value is clamped between -1 and 1:
;=
;= * `0` means using the same color;
;= * range (0; 1) - gives you lighter colors (`1` being white);
;= * range (-1; 0) - gives you darker colors (`-1` being black);
;=
;= Default value is `0.6`, has no vanilla equivalent.
teamColorModifier=0.6

142
config/FutilityNicknames.ini

@ -1,64 +1,67 @@
[default FutilityNicknames]
; This feature allows to configure nickname limitations for the server.
; It allows you to customize vanilla limitations for nickname length and
; color with those of your own design. Enabling this feature overwrites
; default behaviour.
;= This feature allows to configure nickname limitations for the server.
;= It allows you to customize vanilla limitations for nickname length and
;= color with those of your own design. Enabling this feature overwrites
;= default behaviour.
autoEnable=true
; How to treat whitespace characters inside players' nicknames.
; * `NSA_DoNothing` - does nothing, leaving whitespaces as they are;
; * `NSA_Trim` - removes leading and trailing whitespaces for nicknames;
; * `NSA_Simplify` - removes leading and trailing whitespaces
; for nicknames, also reducing a sequence of whitespaces inside
; nickname to a single space, e.g. "my nick" becomes "my nick".
; Default is `NSA_DoNothing`, same as on vanilla.
;= How to treat whitespace characters inside players' nicknames.
;=
;= * `NSA_DoNothing` - does nothing, leaving whitespaces as they are;=
;= * `NSA_Trim` - removes leading and trailing whitespaces for nicknames;=
;= * `NSA_Simplify` - removes leading and trailing whitespaces
;= for nicknames, also reducing a sequence of whitespaces inside
;= nickname to a single space, e.g. "my nick" becomes "my nick".
;=
;= Default is `NSA_DoNothing`, same as on vanilla.
spacesAction=NSA_DoNothing
; How to treat colored nicknames.
; * `NCP_ForbidColor` - completely strips down any color from nicknames;
; * `NCP_ForceTeamColor` - forces all nicknames to have player's current
; team's color;
; * `NCP_ForceSingleColor` - allows nickname to be painted with a single
; color (sets nickname's color to that of the first character);
; * `NCP_AllowAnyColor` - allows nickname to be colored in any way player
; wants.
; Default is `NCP_ForbidColor`, same as on vanilla.
;= How to treat colored nicknames.
;= * `NCP_ForbidColor` - completely strips down any color from nicknames;=
;= * `NCP_ForceTeamColor` - forces all nicknames to have player's current
;= team's color;=
;= * `NCP_ForceSingleColor` - allows nickname to be painted with a single
;= color (sets nickname's color to that of the first character);=
;= * `NCP_AllowAnyColor` - allows nickname to be colored in any way player
;= wants.
;= Default is `NCP_ForbidColor`, same as on vanilla.
colorPermissions=NCP_ForbidColor
; Set this to `true` if you wish to replace all whitespace characters with
; underscores and `false` to leave them as is.
; Default is `true`, same as on vanilla. However there is one difference:
; Futility replaces all whitespace characters (including tabulations,
; non-breaking spaces, etc.) instead of only ' '.
;= Set this to `true` if you wish to replace all whitespace characters with
;= underscores and `false` to leave them as is.
;= Default is `true`, same as on vanilla. However there is one difference:
;= Futility replaces all whitespace characters (including tabulations,
;= non-breaking spaces, etc.) instead of only ' '.
replaceSpacesWithUnderscores=true
; Set this to `true` to remove single 'quotation marks' and `false` to
; leave them. Default is `false`, same as on vanilla.
;= Set this to `true` to remove single 'quotation marks' and `false` to
;= leave them. Default is `false`, same as on vanilla.
removeSingleQuotationMarks=false
; Set this to `true` to remove dobule 'quotation marks' and `false` to
; leave them. Default is `true`, same as on vanilla.
;= Set this to `true` to remove dobule 'quotation marks' and `false` to
;= leave them. Default is `true`, same as on vanilla.
removeDoubleQuotationMarks=true
; Max allowed nickname length. Negative values disable any length limits.
;
; NOTE #1: `0` resets all nicknames to be empty and,
; if `correctEmptyNicknames` is set to `true`, they will be replaced with
; one of the fallback nicknames
; (see `correctEmptyNicknames` and `fallbackNickname`).
; NOTE #2: Because of how color swapping in vanilla Killing Floor works,
; every color swap makes text count as being about 4 characters longer.
; So if one uses too many colors in the nickname, for drawing functions
; it will appear to be longer than it actually is and it *will* mess up
; UI. Unless you are using custom HUD it is recommended to keep this value
; at default `20` and forbid colored nicknames
; (by setting `colorPermissions=NCP_ForbidColor`). Or to allow only one
; color (by setting `colorPermissions=NCP_ForceSingleColor` or
; `colorPermissions=NCP_ForceTeamColor`) and reducing `maxNicknameLength`
; to `16` (20 characters - 4 for color swap).
; If you want to increase the limit above that, you can also do your
; own research by testing nicknames of various length on
; screen resolutions you care about.
;= Max allowed nickname length. Negative values disable any length limits.
;=
;= NOTE #1: `0` resets all nicknames to be empty and,
;= if `correctEmptyNicknames` is set to `true`, they will be replaced with
;= one of the fallback nicknames
;= (see `correctEmptyNicknames` and `fallbackNickname`).
;=
;= NOTE #2: Because of how color swapping in vanilla Killing Floor works,
;= every color swap makes text count as being about 4 characters longer.
;= So if one uses too many colors in the nickname, for drawing functions
;= it will appear to be longer than it actually is and it *will* mess up
;= UI. Unless you are using custom HUD it is recommended to keep this value
;= at default `20` and forbid colored nicknames
;= (by setting `colorPermissions=NCP_ForbidColor`). Or to allow only one
;= color (by setting `colorPermissions=NCP_ForceSingleColor` or
;= `colorPermissions=NCP_ForceTeamColor`) and reducing `maxNicknameLength`
;= to `16` (20 characters - 4 for color swap).
;= If you want to increase the limit above that, you can also do your
;= own research by testing nicknames of various length on
;= screen resolutions you care about.
maxNicknameLength=20
; Should we replace empty player nicknames with a random fallback nickname
; (defined in `fallbackNickname` array)?
;= Should we replace empty player nicknames with a random fallback nickname
;= (defined in `fallbackNickname` array)?
correctEmptyNicknames=true
; Array of fallback nicknames that will be used to replace any empty nicknames
; if `correctEmptyNicknames` is set to `true`.
;= Array of fallback nicknames that will be used to replace any empty nicknames
;= if `correctEmptyNicknames` is set to `true`.
fallbackNickname="Fresh Meat"
fallbackNickname="Rotten Meat"
fallbackNickname="Troll Meat"
@ -70,20 +73,21 @@ fallbackNickname="Boar Meat"
fallbackNickname="Walrus Meat"
fallbackNickname="Bug Meat"
fallbackNickname="Horse Meat"
; Guaranteed order of applying changes (only chosen ones) is as following:
; 1. Trim/simplify spaces;
; 2. Remove single and double quotation marks;
; 3. Enforce max limit of nickname's length;
; 4. Replace empty nickname with fallback nickname (no further changes
; will be applied to fallback nickname in that case);
; 5. Enforce color limitation;
; 6. Replace remaining whitespaces with underscores.
;
; NOTE #1: as follows from the instruction described above, no changes will
; ever be applied to fallback nicknames (unless player's nickname
; coincides with one by pure accident).
; NOTE #2: whitespaces inside steam nicknames are converted into underscores
; before they are passed into the game and this is a change Futility
; cannot currently abort.
; Therefore all changes relevant to whitespaces inside nicknames will only
; be applied to in-game changes.
;= Guaranteed order of applying changes (only chosen ones) is as following:
;= 1. Trim/simplify spaces;=
;= 2. Remove single and double quotation marks;=
;= 3. Enforce max limit of nickname's length;=
;= 4. Replace empty nickname with fallback nickname (no further changes
;= will be applied to fallback nickname in that case);=
;= 5. Enforce color limitation;=
;= 6. Replace remaining whitespaces with underscores.
;=
;= NOTE #1: as follows from the instruction described above, no changes will
;= ever be applied to fallback nicknames (unless player's nickname
;= coincides with one by pure accident).
;=
;= NOTE #2: whitespaces inside steam nicknames are converted into underscores
;= before they are passed into the game and this is a change Futility
;= cannot currently abort.
;= Therefore all changes relevant to whitespaces inside nicknames will only
;= be applied to in-game changes.

120
sources/Commands/ACommandDB.uc

@ -1,6 +1,6 @@
/**
* Command for working with databases.
* Copyright 2021-2022 Anton Tarasenko
* Copyright 2021-2023 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@ -74,47 +74,54 @@ var protected const int TDB_CANNOT_BE_CREATED, TNO_DEFAULT_COMMAND, TBAD_DBLINK;
protected function BuildData(CommandDataBuilder builder)
{
builder.Name(P("db")).Group(P("admin"))
.Summary(P("Read and edit data in your databases."
builder.Group(P("admin"));
builder.Summary(P("Read and edit data in your databases."
@ "Databases' values are addressed with links:"
@ "\"<db_name>:<json_path>\""));
builder.SubCommand(T(TCREATE))
.ParamText(T(TDATABASE_NAME))
.Describe(P("Creates new database with a specified name."));
builder.SubCommand(T(TDELETE))
.ParamText(T(TDATABASE_NAME))
.Describe(P("Completely deletes specified database."));
builder.SubCommand(T(TLIST))
.Describe(P("Lists available databases."));
builder.SubCommand(T(TREAD))
.ParamText(T(TDATABASE_LINK))
.Describe(P("Reads data from location given by the `databaseLink`."));
builder.SubCommand(T(TSIZE))
.ParamText(T(TDATABASE_LINK))
.Describe(P("Gets amount of elements inside JSON array or object at"
builder.SubCommand(T(TCREATE));
builder.ParamText(T(TDATABASE_NAME));
builder.Describe(P("Creates new database with a specified name."));
builder.SubCommand(T(TDELETE));
builder.ParamText(T(TDATABASE_NAME));
builder.Describe(P("Completely deletes specified database."));
builder.SubCommand(T(TLIST));
builder.Describe(P("Lists available databases."));
builder.SubCommand(T(TREAD));
builder.ParamText(T(TDATABASE_LINK));
builder.Describe(P("Reads data from location given by the `databaseLink`."));
builder.SubCommand(T(TSIZE));
builder.ParamText(T(TDATABASE_LINK));
builder.Describe(P("Gets amount of elements inside JSON array or object at"
@ "location given by the `databaseLink`."));
builder.SubCommand(T(TKEYS))
.ParamText(T(TDATABASE_LINK))
.Describe(P("Lists keys of JSON object at location given by"
builder.SubCommand(T(TKEYS));
builder.ParamText(T(TDATABASE_LINK));
builder.Describe(P("Lists keys of JSON object at location given by"
@ "the `databaseLink`."));
builder.SubCommand(T(TREMOVE))
.ParamText(T(TDATABASE_LINK))
.Describe(P("Removes data from location given by the `databaseLink`."));
builder.SubCommand(T(TWRITE))
.ParamText(T(TDATABASE_LINK))
.ParamJSON(T(TJSON_VALUE))
.Describe(P("Writes specified JSON value into location given by"
builder.SubCommand(T(TREMOVE));
builder.ParamText(T(TDATABASE_LINK));
builder.Describe(P("Removes data from location given by the `databaseLink`."));
builder.SubCommand(T(TWRITE));
builder.ParamText(T(TDATABASE_LINK));
builder.ParamJSON(T(TJSON_VALUE));
builder.Describe(P("Writes specified JSON value into location given by"
@ "the `databaseLink`."));
builder.Option(T(TINCREMENT))
.Describe(F("Specifying this option for any of the"
builder.Option(T(TINCREMENT));
builder.Describe(F("Specifying this option for any of the"
@ "{$TextEmphasis 'write'} subcommands will cause them to append"
@ "data to the old one, instead of rewriting it."));
}
protected function PushPlayer(EPlayer nextPlayer, Database callDatabase)
{
queueWaitingListPlayers[queueWaitingListPlayers.length] =
EPlayer(nextPlayer.Copy());
local EPlayer playerCopy;
if (nextPlayer != none) {
playerCopy = EPlayer(nextPlayer.Copy());
}
if (callDatabase != none) {
callDatabase.NewRef();
}
queueWaitingListPlayers[queueWaitingListPlayers.length] = playerCopy;
queueWaitingListDatabases[queueWaitingListDatabases.length] = callDatabase;
}
@ -128,9 +135,10 @@ protected function EPlayer PopPlayer(Database relevantDatabase)
while (i < queueWaitingListDatabases.length)
{
if (queueWaitingListDatabases[i] != relevantDatabase)
if (queueWaitingListDatabases[i].IsEqual(relevantDatabase))
{
result = queueWaitingListPlayers[i];
queueWaitingListDatabases[i].FreeSelf();
queueWaitingListPlayers.Remove(i, 1);
queueWaitingListDatabases.Remove(i, 1);
break;
@ -144,8 +152,11 @@ protected function EPlayer PopPlayer(Database relevantDatabase)
return none;
}
protected function Executed(CallData arguments, EPlayer instigator)
{
protected function Executed(
CallData arguments,
EPlayer instigator,
CommandPermissions permissions
) {
local AcediaObject valueToWrite;
local DBPointerPair pair;
local Text subCommand;
@ -235,11 +246,11 @@ private function DBPointerPair TryLoadingDB(BaseText databaseLink)
if (databaseLink == none) {
return result;
}
result.database = _.db.Load(databaseLink);
result.database = _server.db.Load(databaseLink);
if (result.database == none) {
return result;
}
result.pointer = _.db.GetPointer(databaseLink);
result.pointer = _server.db.GetPointer(databaseLink);
return result;
}
@ -248,12 +259,12 @@ protected function CreateDatabase(EPlayer instigator, Text databaseName)
if (instigator == none) {
return;
}
if (_.db.ExistsLocal(databaseName))
if (_server.db.ExistsLocal(databaseName))
{
callerConsole.WriteLine(T(TDB_ALREADY_EXISTS));
return;
}
if (_.db.NewLocal(databaseName) != none) {
if (_server.db.NewLocal(databaseName) != none) {
callerConsole.WriteLine(T(TDB_CREATED));
}
else {
@ -266,7 +277,7 @@ protected function DeleteDatabase(EPlayer instigator, Text databaseName)
if (instigator == none) {
return;
}
if (_.db.DeleteLocal(databaseName)) {
if (_server.db.DeleteLocal(databaseName)) {
callerConsole.WriteLine(T(TDA_DELETED));
}
else {
@ -283,7 +294,7 @@ protected function ListDatabases(EPlayer instigator)
if (instigator == none) {
return;
}
availableDatabases = _.db.ListLocal();
availableDatabases = _server.db.ListLocal();
console = callerConsole;
console.Write(T(TAVAILABLE_DATABASES));
for (i = 0; i < availableDatabases.length; i += 1)
@ -321,7 +332,8 @@ protected function OutputStatus(
protected function DisplayData(
Database.DBQueryResult result,
AcediaObject data,
Database source)
Database source,
int requestID)
{
local Text printedJSON;
local EPlayer instigator;
@ -342,7 +354,8 @@ protected function DisplayData(
protected function DisplaySize(
Database.DBQueryResult result,
int size,
Database source)
Database source,
int requestID)
{
local Text sizeAsText;
local EPlayer instigator;
@ -365,7 +378,8 @@ protected function DisplaySize(
protected function DisplayKeys(
Database.DBQueryResult result,
ArrayList keys,
Database source)
Database source,
int requestID)
{
local int i;
local Text nextKey;
@ -399,7 +413,8 @@ protected function DisplayKeys(
protected function DisplayResponse(
Database.DBQueryResult result,
Database source)
Database source,
int requestID)
{
local EPlayer instigator;
@ -410,6 +425,7 @@ protected function DisplayResponse(
defaultproperties
{
preferredName = "db"
TCREATE = 0
stringConstants(0) = "create"
TDELETE = 1
@ -441,25 +457,25 @@ defaultproperties
TQUERY_COMPLETED = 14
stringConstants(14) = "{$TextPositive Database query was completed!}"
TQUERY_INVALID_POINTER = 15
stringConstants(15) = "{$TextNegative Query was provided with an invalid JSON pointer.}"
stringConstants(15) = "{$TextFailure Query was provided with an invalid JSON pointer.}"
TQUERY_INVALID_DB = 16
stringConstants(16) = "{$TextNegative Operation could not finish because database is damaged and unusable.}"
stringConstants(16) = "{$TextFailure Operation could not finish because database is damaged and unusable.}"
TQUERY_INVALID_DATA = 17
stringConstants(17) = "{$TextNegative Query data is invalid.}"
stringConstants(17) = "{$TextFailure Query data is invalid.}"
TAVAILABLE_DATABASES = 18
stringConstants(18) = "{$TextEmphasis Available databases:} "
TDA_DELETED = 19
stringConstants(19) = "{$TextPositive Database was deleted.}"
TDB_DOESNT_EXIST = 20
stringConstants(20) = "{$TextNegative Database with specified name does not exist.}"
stringConstants(20) = "{$TextFailure Database with specified name does not exist.}"
TDB_ALREADY_EXISTS = 21
stringConstants(21) = "{$TextNegative Database with specified name already exists.}"
stringConstants(21) = "{$TextFailure Database with specified name already exists.}"
TDB_CREATED = 22
stringConstants(22) = "{$TextPositive Database was created.}"
TDB_CANNOT_BE_CREATED = 23
stringConstants(23) = "{$TextNegative Database cannot be created.}"
stringConstants(23) = "{$TextFailure Database cannot be created.}"
TNO_DEFAULT_COMMAND = 24
stringConstants(24) = "{$TextNegative Default command does nothing. Use on of the sub-commands.}"
stringConstants(24) = "{$TextFailure Default command does nothing. Use on of the sub-commands.}"
TBAD_DBLINK = 25
stringConstants(25) = "{$TextNegative Database could not be read for the specified link.}"
stringConstants(25) = "{$TextFailure Database could not be read for the specified link.}"
}

35
sources/Commands/ACommandDosh.uc

@ -29,23 +29,23 @@ protected function Finalizer()
protected function BuildData(CommandDataBuilder builder)
{
builder.Name(P("dosh")).Group(P("gameplay"))
.Summary(P("Changes amount of money."));
builder.Group(P("gameplay"));
builder.Summary(P("Changes amount of money."));
builder.RequireTarget();
builder.ParamInteger(P("amount"))
.Describe(P("Gives (or takes if negative) players a specified <amount>"
builder.ParamInteger(P("amount"));
builder.Describe(P("Gives (or takes if negative) players a specified <amount>"
@ "of money."));
builder.SubCommand(P("set"))
.ParamInteger(P("amount"))
.Describe(P("Sets player's money to a specified <amount>."));
builder.Option(P("min"))
.ParamInteger(P("minValue"))
.Describe(F("Players will retain at least this amount of dosh after"
builder.SubCommand(P("set"));
builder.ParamInteger(P("amount"));
builder.Describe(P("Sets player's money to a specified <amount>."));
builder.Option(P("min"));
builder.ParamInteger(P("minValue"));
builder.Describe(F("Players will retain at least this amount of dosh after"
@ "the command's execution. In case of conflict, overrides"
@ "'{$TextEmphasis --max}' option. `0` is assumed by default."));
builder.Option(P("max"), P("M"))
.ParamInteger(P("maxValue"))
.Describe(F("Players will have at most this amount of dosh after"
builder.Option(P("max"), P("M"));
builder.ParamInteger(P("maxValue"));
builder.Describe(F("Players will have at most this amount of dosh after"
@ "the command's execution. In case of conflict, it is overridden"
@ "by '{$TextEmphasis --min}' option."));
announcer = ACommandDosh_Announcer(
@ -55,8 +55,9 @@ protected function BuildData(CommandDataBuilder builder)
protected function ExecutedFor(
EPlayer target,
CallData arguments,
EPlayer instigator)
{
EPlayer instigator,
CommandPermissions permissions
) {
local int oldAmount, newAmount;
local int amount, minValue, maxValue;
@ -88,6 +89,6 @@ protected function ExecutedFor(
}
}
defaultproperties
{
defaultproperties {
preferredName = "dosh"
}

465
sources/Commands/ACommandFeature.uc

@ -20,211 +20,194 @@
class ACommandFeature extends Command
dependson(PendingConfigsTool);
var private class<Feature> selectedFeatureClass;
var private Text selectedConfigName;
var private class<Feature> selectedFeatureClass;
var private Text selectedFeatureName;
var private Text selectedConfigName;
var private PendingConfigsTool pendingConfigs;
var private ACommandFeature_Announcer announcer;
var private PendingConfigsTool pendingConfigs;
var private ACommandFeature_Announcer announcer;
protected function Constructor()
{
pendingConfigs =
PendingConfigsTool(_.memory.Allocate(class'PendingConfigsTool'));
protected function Constructor() {
pendingConfigs = PendingConfigsTool(_.memory.Allocate(class'PendingConfigsTool'));
super.Constructor();
}
protected function Finalizer()
{
protected function Finalizer() {
_.memory.Free(announcer);
_.memory.Free(pendingConfigs);
super.Finalizer();
}
protected function BuildData(CommandDataBuilder builder)
{
builder.Name(P("feature")).Group(P("admin"))
.Summary(P("Managing features."))
.Describe(P("Command for managing features and their configs."));
builder.SubCommand(P("enable"))
.ParamText(P("feature"),, P("feature"))
.OptionalParams()
.ParamText(P("config"))
.Describe(P("Enables specified <feature>. If <config> isn't specified -"
protected function BuildData(CommandDataBuilder builder) {
builder.Group(P("admin"));
builder.Summary(P("Managing features."));
builder.Describe(P("Command for managing features and their configs."));
builder.SubCommand(P("enable"));
builder.ParamText(P("feature"),, P("feature"));
builder.OptionalParams();
builder.ParamText(P("config"));
builder.Describe(P("Enables specified <feature>. If <config> isn't specified -"
@ "choses the \"default\" one, making new config with default"
@ "settings if it doesn't exist."));
builder.SubCommand(P("disable"))
.ParamText(P("feature"),, P("feature"))
.Describe(P("Disables specified <feature>."));
builder.SubCommand(P("showconf"))
.ParamText(P("feature"),, P("feature"))
.OptionalParams()
.ParamText(P("config"))
.Describe(P("Show given <config> for the given <feature>."));
builder.SubCommand(P("editconf"))
.ParamText(P("feature"),, P("feature"))
.ParamText(P("config"))
.ParamText(P("variable_path"))
.ParamJSON(P("value"))
.Describe(P("Changes a value inside given <config> of the given"
builder.SubCommand(P("disable"));
builder.ParamText(P("feature"),, P("feature"));
builder.Describe(P("Disables specified <feature>."));
builder.SubCommand(P("showconf"));
builder.ParamText(P("feature"),, P("feature"));
builder.OptionalParams();
builder.ParamText(P("config"));
builder.Describe(P("Show given <config> for the given <feature>."));
builder.SubCommand(P("editconf"));
builder.ParamText(P("feature"),, P("feature"));
builder.ParamText(P("config"));
builder.ParamText(P("variable_path"));
builder.ParamJSON(P("value"));
builder.Describe(P("Changes a value inside given <config> of the given"
@ "<feature> by setting value at JSON path <variable_path> to"
@ "the JSON value <value>. Changes won't be immediately applied to"
@ "the game and kept as pending."));
builder.SubCommand(P("saveconf"))
.ParamText(P("feature"),, P("feature"))
.ParamText(P("config"))
.Describe(P("Saves pending changes for the given <config> of the given"
builder.SubCommand(P("saveconf"));
builder.ParamText(P("feature"),, P("feature"));
builder.ParamText(P("config"));
builder.Describe(P("Saves pending changes for the given <config> of the given"
@ "<feature>."));
builder.SubCommand(P("newconf"))
.ParamText(P("feature"),, P("feature"))
.ParamText(P("config"))
.Describe(P("Creates new config for the given <feature>."));
builder.SubCommand(P("removeconf"))
.ParamText(P("feature"),, P("feature"))
.ParamText(P("config"))
.Describe(P("Removes specified <config> of the specified <feature>."));
builder.SubCommand(P("autoconf"))
.ParamText(P("feature"),, P("feature"))
.OptionalParams()
.ParamText(P("config"))
.Describe(P("Changes current auto config config of the specified"
builder.SubCommand(P("newconf"));
builder.ParamText(P("feature"),, P("feature"));
builder.ParamText(P("config"));
builder.Describe(P("Creates new config for the given <feature>."));
builder.SubCommand(P("removeconf"));
builder.ParamText(P("feature"),, P("feature"));
builder.ParamText(P("config"));
builder.Describe(P("Removes specified <config> of the specified <feature>."));
builder.SubCommand(P("autoconf"));
builder.ParamText(P("feature"),, P("feature"));
builder.OptionalParams();
builder.ParamText(P("config"));
builder.Describe(P("Changes current auto config config of the specified"
@ "<feature>. Auto config is a config that is supposed to be"
@ "automatically enabled at the start of the Acedia, unless"
@ "otherwise specified for the loader."));
builder.Option(P("all"))
.Describe(F("Affects subcommand {$TextEmphasis showconf} by making it"
builder.Option(P("all"));
builder.Describe(F("Affects subcommand {$TextEmphasis showconf} by making it"
@ "show all available configs."));
builder.Option(P("save"))
.Describe(F("Affects subcommand {$TextEmphasis editconf} by making it"
builder.Option(P("save"));
builder.Describe(F("Affects subcommand {$TextEmphasis editconf} by making it"
@ "also save all pending changes."));
announcer = ACommandFeature_Announcer(
_.memory.Allocate(class'ACommandFeature_Announcer'));
}
protected function Executed(CallData arguments, EPlayer instigator)
{
protected function Executed(
CallData arguments,
EPlayer instigator,
CommandPermissions permissions
) {
local bool saveFlag, allFlag;
announcer.Setup(none, instigator, othersConsole);
saveFlag = arguments.options.HasKey(P("save"));
allFlag = arguments.options.HasKey(P("all"));
saveFlag = arguments.options.HasKey(P("save"));
allFlag = arguments.options.HasKey(P("all"));
SelectFeatureAndConfig(arguments);
if (arguments.subCommandName.IsEmpty()) {
ShowAllFeatures();
}
else if (arguments.subCommandName.Compare(P("enable"))) {
} else if (arguments.subCommandName.Compare(P("enable"))) {
EnableFeature();
}
else if (arguments.subCommandName.Compare(P("disable"))) {
} else if (arguments.subCommandName.Compare(P("disable"))) {
DisableFeature();
}
else if (arguments.subCommandName.Compare(P("showconf"))) {
} else if (arguments.subCommandName.Compare(P("showconf"))) {
ShowSelectedConfigs(allFlag);
}
else if (arguments.subCommandName.Compare(P("editconf")))
{
} else if (arguments.subCommandName.Compare(P("editconf"))) {
EditFeatureConfig(
arguments.parameters.GetText(P("variable_path")),
arguments.parameters.GetItem(P("value")),
saveFlag);
}
else if (arguments.subCommandName.Compare(P("saveconf"))) {
} else if (arguments.subCommandName.Compare(P("saveconf"))) {
SaveFeatureConfig();
}
else if (arguments.subCommandName.Compare(P("newconf"))) {
} else if (arguments.subCommandName.Compare(P("newconf"))) {
NewFeatureConfig();
}
else if (arguments.subCommandName.Compare(P("removeconf"))) {
} else if (arguments.subCommandName.Compare(P("removeconf"))) {
RemoveFeatureConfig();
}
else if (arguments.subCommandName.Compare(P("autoconf"))) {
} else if (arguments.subCommandName.Compare(P("autoconf"))) {
SetAutoFeatureConfig();
}
_.memory.Free(selectedConfigName);
_.memory.Free2(selectedConfigName, selectedFeatureName);
selectedConfigName = none;
selectedFeatureName = none;
}
protected function SelectFeatureAndConfig(CallData arguments)
{
protected function SelectFeatureAndConfig(CallData arguments) {
local Text featureClassName, userGivenConfigName;
featureClassName = arguments.parameters.GetText(P("feature"));
selectedFeatureName = arguments.parameters.GetTextBy(P("/feature/alias"));
featureClassName = arguments.parameters.GetTextBy(P("/feature/value"));
selectedFeatureClass = LoadFeatureClass(featureClassName);
if (selectedFeatureClass == none && !arguments.subCommandName.IsEmpty()) {
_.memory.Free(selectedFeatureName);
selectedFeatureName = none;
return;
}
_.memory.Free(featureClassName);
userGivenConfigName = arguments.parameters.GetText(P("config"));
if (userGivenConfigName != none)
{
if (userGivenConfigName != none) {
selectedConfigName = userGivenConfigName.LowerCopy();
userGivenConfigName.FreeSelf();
}
pendingConfigs.SelectConfig(selectedFeatureClass, selectedConfigName);
}
protected function EnableFeature()
{
local bool wasEnabled;
local Text oldConfig, newConfig;
local Feature instance;
protected function EnableFeature() {
local bool wasEnabled;
local Text oldConfig, newConfig;
local Feature instance;
wasEnabled = selectedFeatureClass.static.IsEnabled();
oldConfig = selectedFeatureClass.static.GetCurrentConfig();
newConfig = PickConfigBasedOnParameter();
wasEnabled = selectedFeatureClass.static.IsEnabled();
oldConfig = selectedFeatureClass.static.GetCurrentConfig();
newConfig = PickConfigBasedOnParameter();
// Already enabled with the same config!
if (oldConfig != none && oldConfig.Compare(newConfig, SCASE_INSENSITIVE))
{
announcer.AnnounceFailedAlreadyEnabled(selectedFeatureClass, newConfig);
if (oldConfig != none && oldConfig.Compare(newConfig, SCASE_INSENSITIVE)) {
announcer.AnnounceFailedAlreadyEnabled(selectedFeatureName, newConfig);
_.memory.Free(newConfig);
_.memory.Free(oldConfig);
return;
}
// Try enabling and report the result
instance = selectedFeatureClass.static.EnableMe(newConfig);
if (instance == none)
{
if (instance == none) {
announcer.AnnounceFailedCannotEnableFeature(
selectedFeatureClass,
selectedFeatureName,
newConfig);
}
else if (wasEnabled)
{
} else if (wasEnabled) {
announcer.AnnounceSwappedConfig(
selectedFeatureClass,
selectedFeatureName,
oldConfig,
newConfig);
}
else {
announcer.AnnounceEnabledFeature(selectedFeatureClass, newConfig);
} else {
announcer.AnnounceEnabledFeature(selectedFeatureName, newConfig);
}
_.memory.Free(newConfig);
_.memory.Free(oldConfig);
}
protected function DisableFeature()
{
if (!selectedFeatureClass.static.IsEnabled())
{
announcer.AnnounceFailedAlreadyDisabled(selectedFeatureClass);
protected function DisableFeature() {
if (!selectedFeatureClass.static.IsEnabled()) {
announcer.AnnounceFailedAlreadyDisabled(selectedFeatureName);
return;
}
selectedFeatureClass.static.DisableMe();
// It is possible that this command itself is destroyed after above command
// so do the check just in case
if (IsAllocated()) {
announcer.AnnounceDisabledFeature(selectedFeatureClass);
announcer.AnnounceDisabledFeature(selectedFeatureName);
}
}
protected function ShowSelectedConfigs(bool showAllFeatures)
{
local int i;
local array<Text> availableConfigs;
local MutableText configList;
local class<FeatureConfig> configClass;
protected function ShowSelectedConfigs(bool showAllFeatures) {
local int i;
local array<Text> availableConfigs;
local MutableText configList;
local class<FeatureConfig> configClass;
if (showAllFeatures)
{
if (showAllFeatures) {
configClass = selectedFeatureClass.default.configClass;
if (configClass != none) {
availableConfigs = configClass.static.AvailableConfigs();
@ -235,8 +218,7 @@ protected function ShowSelectedConfigs(bool showAllFeatures)
_.memory.FreeMany(availableConfigs);
return;
}
if (selectedConfigName != none)
{
if (selectedConfigName != none) {
ShowFeatureConfig(selectedConfigName);
return;
}
@ -248,35 +230,32 @@ protected function ShowSelectedConfigs(bool showAllFeatures)
_.memory.Free(configList);
}
protected function ShowFeatureConfig(BaseText configName)
{
local MutableText dataAsJSON;
local HashTable currentData, pendingData;
protected function ShowFeatureConfig(BaseText configName) {
local MutableText dataAsJSON;
local HashTable currentData, pendingData;
if (configName == none) {
return;
}
currentData = GetCurrentConfigData(configName);
if (currentData == none)
{
if (currentData == none) {
announcer.AnnounceFailedNoDataForConfig(
selectedFeatureClass,
selectedFeatureName,
configName);
return;
}
// Display current data
dataAsJSON = _.json.PrettyPrint(currentData);
announcer.AnnounceCurrentConfig(selectedFeatureClass, configName);
announcer.AnnounceCurrentConfig(selectedFeatureName, configName);
callerConsole.Flush().WriteLine(dataAsJSON);
_.memory.Free(dataAsJSON);
// Display pending data
pendingConfigs.SelectConfig(selectedFeatureClass, configName);
pendingData = pendingConfigs.GetPendingConfigData();
if (pendingData != none)
{
if (pendingData != none) {
dataAsJSON = _.json.PrettyPrint(pendingData);
announcer.AnnouncePendingConfig(
selectedFeatureClass,
selectedFeatureName,
configName);
callerConsole.Flush().WriteLine(dataAsJSON);
_.memory.Free(dataAsJSON);
@ -285,20 +264,17 @@ protected function ShowFeatureConfig(BaseText configName)
_.memory.Free(currentData);
}
protected function Text PickConfigBasedOnParameter()
{
local Text resolvedConfig;
local class<FeatureConfig> configClass;
protected function Text PickConfigBasedOnParameter() {
local Text resolvedConfig;
local class<FeatureConfig> configClass;
configClass = selectedFeatureClass.default.configClass;
if (configClass == none)
{
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
if (configClass == none) {
announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
return none;
}
// If config was specified - simply check that it exists
if (selectedConfigName != none)
{
if (selectedConfigName != none) {
if (configClass.static.Exists(selectedConfigName)) {
return selectedConfigName.Copy();
}
@ -308,13 +284,12 @@ protected function Text PickConfigBasedOnParameter()
// If it wasn't specified - try auto config instead
resolvedConfig = configClass.static.GetAutoEnabledConfig();
if (resolvedConfig == none) {
announcer.AnnounceFailedNoConfigProvided(selectedFeatureClass);
announcer.AnnounceFailedNoConfigProvided(selectedFeatureName);
}
return resolvedConfig;
}
protected function class<Feature> LoadFeatureClass(BaseText featureClassName)
{
protected function class<Feature> LoadFeatureClass(BaseText featureClassName) {
local class<Feature> featureClass;
if (featureClassName == none) {
@ -327,10 +302,10 @@ protected function class<Feature> LoadFeatureClass(BaseText featureClassName)
return featureClass;
}
protected function ShowAllFeatures()
{
local int i;
local array< class<Feature> > availableFeatures;
protected function ShowAllFeatures() {
local int i;
local array< class<Feature> > availableFeatures;
availableFeatures = _.environment.GetAvailableFeatures();
for (i = 0; i < availableFeatures.length; i ++) {
ShowFeature(availableFeatures[i]);
@ -363,45 +338,47 @@ protected function ShowFeature(class<Feature> featureClass)
_.memory.Free(configList);
}
protected function MutableText PrintConfigList(class<Feature> featureClass)
{
local int i;
local Text autoConfig;
local ListBuilder configList;
local MutableText result, nextConfig;
local array<Text> availableConfigs;
local class<FeatureConfig> configClass;
protected function MutableText PrintConfigList(class<Feature> featureClass) {
local int i;
local Text autoConfig, enabledConfig;
local ListBuilder configList;
local MutableText result, nextConfig;
local array<Text> availableConfigs;
local class<FeatureConfig> configClass;
if (featureClass == none) return none;
configClass = featureClass.default.configClass;
if (configClass == none) return none;
availableConfigs = configClass.static.AvailableConfigs();
autoConfig = configClass.static.GetAutoEnabledConfig();
configList = ListBuilder(_.memory.Allocate(class'ListBuilder'));
for (i = 0; i < availableConfigs.length; i += 1)
{
availableConfigs = configClass.static.AvailableConfigs();
enabledConfig = featureClass.static.GetCurrentConfig();
autoConfig = configClass.static.GetAutoEnabledConfig();
configList = ListBuilder(_.memory.Allocate(class'ListBuilder'));
for (i = 0; i < availableConfigs.length; i += 1) {
nextConfig = availableConfigs[i].MutableCopy();
if (enabledConfig != none && enabledConfig.Compare(nextConfig, SCASE_INSENSITIVE)) {
nextConfig.ChangeDefaultColor(_.color.TextPositive);
}
if (pendingConfigs.HasPendingConfigFor(featureClass, nextConfig)) {
nextConfig.Append(F("{$TextEmphasis *}"));
}
configList.Item(nextConfig);
_.memory.Free(nextConfig);
if ( autoConfig != none
&& autoConfig.Compare(availableConfigs[i], SCASE_INSENSITIVE))
{
configList.Comment(F("{$TextPositive auto enabled}"));
if (autoConfig != none && autoConfig.Compare(availableConfigs[i], SCASE_INSENSITIVE)) {
if (autoConfig.Compare(enabledConfig, SCASE_INSENSITIVE)) {
configList.Comment(F("{$TextPositive auto enabled}"));
} else {
configList.Comment(F("{$TextNegative auto enabled}"));
}
}
}
result = configList.GetMutable();
_.memory.Free(configList);
_.memory.Free(autoConfig);
_.memory.Free3(configList, autoConfig, enabledConfig);
_.memory.FreeMany(availableConfigs);
return result;
}
protected function MutableText PrettyPrintValueAt(BaseText pathToValue)
{
protected function MutableText PrettyPrintValueAt(BaseText pathToValue) {
local MutableText printedValue;
local AcediaObject value;
local HashTable relevantData;
@ -413,8 +390,7 @@ protected function MutableText PrettyPrintValueAt(BaseText pathToValue)
if (relevantData != none) {
value = relevantData.GetItemBy(pathToValue);
}
if (value != none)
{
if (value != none) {
printedValue = _.json.PrettyPrint(value);
_.memory.Free(value);
}
@ -422,11 +398,7 @@ protected function MutableText PrettyPrintValueAt(BaseText pathToValue)
return printedValue;
}
protected function EditFeatureConfig(
BaseText pathToValue,
AcediaObject newValue,
bool saveConfig)
{
protected function EditFeatureConfig(BaseText pathToValue, AcediaObject newValue, bool saveConfig) {
local MutableText printedOldValue;
local MutableText printedNewValue;
local PendingConfigsTool.PendingConfigToolResult error;
@ -444,26 +416,20 @@ protected function EditFeatureConfig(
}
else if (error == PCTE_ExpectedObject) {
announcer.AnnounceFailedExpectedObject();
}
else if (error == PCTE_BadPointer)
{
} else if (error == PCTE_BadPointer) {
announcer.AnnounceFailedBadPointer(
selectedFeatureClass,
selectedFeatureName,
selectedConfigName,
pathToValue);
}
else if (printedOldValue == none)
{
} else if (printedOldValue == none) {
announcer.AnnounceConfigNewValue(
selectedFeatureClass,
selectedFeatureName,
selectedConfigName,
pathToValue,
printedNewValue);
}
else
{
} else {
announcer.AnnounceConfigEdited(
selectedFeatureClass,
selectedFeatureName,
selectedConfigName,
pathToValue,
printedOldValue,
@ -478,21 +444,18 @@ protected function EditFeatureConfig(
_.memory.Free(newValue);
}
protected function SaveFeatureConfig()
{
local BaseText enabledConfigName;
local HashTable pendingData;
local class<FeatureConfig> configClass;
protected function SaveFeatureConfig() {
local BaseText enabledConfigName;
local HashTable pendingData;
local class<FeatureConfig> configClass;
configClass = selectedFeatureClass.default.configClass;
if (configClass == none)
{
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
if (configClass == none) {
announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
return;
}
pendingData = pendingConfigs.GetPendingConfigData();
if (pendingData == none)
{
if (pendingData == none) {
announcer.AnnounceFailedPendingConfigMissing(selectedConfigName);
return;
}
@ -501,13 +464,11 @@ protected function SaveFeatureConfig()
configClass.static.SaveData(selectedConfigName, pendingData);
// Re-apply config if it is active?
enabledConfigName = selectedFeatureClass.static.GetCurrentConfig();
if (selectedConfigName.Compare(enabledConfigName, SCASE_INSENSITIVE))
{
if (selectedConfigName.Compare(enabledConfigName, SCASE_INSENSITIVE)) {
selectedFeatureClass.static.EnableMe(selectedConfigName);
announcer.AnnouncePublicPendingConfigSaved(selectedFeatureClass);
}
else {
announcer.AnnouncePrivatePendingConfigSaved(selectedFeatureClass);
announcer.AnnouncePublicPendingConfigSaved(selectedFeatureName);
} else {
announcer.AnnouncePrivatePendingConfigSaved(selectedFeatureName, selectedConfigName);
}
_.memory.Free(enabledConfigName);
pendingData.FreeSelf();
@ -515,128 +476,104 @@ protected function SaveFeatureConfig()
return;
}
protected function NewFeatureConfig()
{
local BaseText enabledConfigName;
local class<FeatureConfig> configClass;
protected function NewFeatureConfig() {
local BaseText enabledConfigName;
local class<FeatureConfig> configClass;
configClass = selectedFeatureClass.default.configClass;
if (configClass == none)
{
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
if (configClass == none) {
announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
return;
}
if (configClass.static.Exists(selectedConfigName))
{
announcer.AnnounceFailedConfigAlreadyExists(
selectedFeatureClass,
selectedConfigName);
if (configClass.static.Exists(selectedConfigName)) {
announcer.AnnounceFailedConfigAlreadyExists(selectedFeatureName, selectedConfigName);
return;
}
if (!configClass.static.NewConfig(selectedConfigName))
{
if (!configClass.static.NewConfig(selectedConfigName)) {
announcer.AnnounceFailedBadConfigName(selectedConfigName);
return;
}
enabledConfigName = selectedFeatureClass.static.GetCurrentConfig();
if (selectedConfigName.Compare(enabledConfigName, SCASE_INSENSITIVE))
{
if (selectedConfigName.Compare(enabledConfigName, SCASE_INSENSITIVE)) {
selectedFeatureClass.static.EnableMe(selectedConfigName);
announcer.AnnouncePublicPendingConfigSaved(selectedFeatureClass);
announcer.AnnouncePublicPendingConfigSaved(selectedFeatureName);
}
_.memory.Free(enabledConfigName);
announcer.AnnounceConfigCreated(selectedFeatureClass, selectedConfigName);
announcer.AnnounceConfigCreated(selectedFeatureName, selectedConfigName);
}
protected function RemoveFeatureConfig()
{
protected function RemoveFeatureConfig() {
local class<FeatureConfig> configClass;
configClass = selectedFeatureClass.default.configClass;
if (configClass == none)
{
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
if (configClass == none) {
announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
return;
}
if (!configClass.static.Exists(selectedConfigName))
{
if (!configClass.static.Exists(selectedConfigName)) {
announcer.AnnounceFailedConfigDoesNotExist(
selectedFeatureClass,
selectedFeatureName,
selectedConfigName);
return;
}
pendingConfigs.RemoveConfig();
configClass.static.DeleteConfig(selectedConfigName);
announcer.AnnounceConfigRemoved(selectedFeatureClass, selectedConfigName);
announcer.AnnounceConfigRemoved(selectedFeatureName, selectedConfigName);
}
protected function SetAutoFeatureConfig()
{
local Text currentAutoEnabledConfig;
local class<FeatureConfig> configClass;
protected function SetAutoFeatureConfig() {
local Text currentAutoEnabledConfig;
local class<FeatureConfig> configClass;
configClass = selectedFeatureClass.default.configClass;
if (configClass == none)
{
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
if (configClass == none) {
announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
return;
}
if ( selectedConfigName != none
&& !configClass.static.Exists(selectedConfigName))
{
announcer.AnnounceFailedConfigDoesNotExist(
selectedFeatureClass,
selectedConfigName);
if (selectedConfigName != none && !configClass.static.Exists(selectedConfigName)) {
announcer.AnnounceFailedConfigDoesNotExist(selectedFeatureName, selectedConfigName);
return;
}
currentAutoEnabledConfig = configClass.static.GetAutoEnabledConfig();
if (selectedConfigName == none && currentAutoEnabledConfig == none) {
announcer.AnnounceFailedAlreadyNoAutoEnabled(selectedFeatureClass);
announcer.AnnounceFailedAlreadyNoAutoEnabled(selectedFeatureName);
}
else if (selectedConfigName != none &&
selectedConfigName.Compare(currentAutoEnabledConfig, SCASE_INSENSITIVE))
{
selectedConfigName.Compare(currentAutoEnabledConfig, SCASE_INSENSITIVE)) {
announcer.AnnounceFailedAlreadySameAutoEnabled(
selectedFeatureClass,
selectedFeatureName,
selectedConfigName);
}
else
{
} else {
configClass.static.SetAutoEnabledConfig(selectedConfigName);
if (selectedConfigName != none)
{
if (selectedConfigName != none) {
announcer.AnnounceAutoEnabledConfig(
selectedFeatureClass,
selectedFeatureName,
selectedConfigName);
}
else {
announcer.AnnounceRemovedAutoEnabledConfig(selectedFeatureClass);
} else {
announcer.AnnounceRemovedAutoEnabledConfig(selectedFeatureName);
}
}
_.memory.Free(currentAutoEnabledConfig);
}
private function HashTable GetCurrentConfigData(BaseText configName)
{
private function HashTable GetCurrentConfigData(BaseText configName) {
local class<FeatureConfig> configClass;
if (configName == none) {
return none;
}
configClass = selectedFeatureClass.default.configClass;
if (configClass == none)
{
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
if (configClass == none) {
announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
return none;
}
return configClass.static.LoadData(configName);
}
private function HashTable GetCurrentSelectedConfigData()
{
private function HashTable GetCurrentSelectedConfigData() {
return GetCurrentConfigData(selectedConfigName);
}
defaultproperties
{
defaultproperties {
preferredName = "feature"
}

150
sources/Commands/ACommandFeature_Announcer.uc

@ -75,8 +75,8 @@ protected function Finalizer()
}
public final function AnnounceEnabledFeature(
class<Feature> featureClass,
BaseText configName)
BaseText featureName,
BaseText configName)
{
local int i;
local array<TextTemplate> templates;
@ -93,12 +93,12 @@ public final function AnnounceEnabledFeature(
}
templates = MakeArray(enabledFeature);
for (i = 0; i < templates.length; i += 1) {
templates[i].Reset().ArgClass(featureClass).Arg(configName);
templates[i].Reset().Arg(featureName).Arg(configName);
}
MakeAnnouncement(enabledFeature);
}
public final function AnnounceDisabledFeature(class<Feature> featureClass)
public final function AnnounceDisabledFeature(BaseText featureName)
{
local int i;
local array<TextTemplate> templates;
@ -114,15 +114,15 @@ public final function AnnounceDisabledFeature(class<Feature> featureClass)
}
templates = MakeArray(disabledFeature);
for (i = 0; i < templates.length; i += 1) {
templates[i].Reset().ArgClass(featureClass);
templates[i].Reset().Arg(featureName);
}
MakeAnnouncement(disabledFeature);
}
public final function AnnounceSwappedConfig(
class<Feature> featureClass,
BaseText oldConfig,
BaseText newConfig)
BaseText featureName,
BaseText oldConfig,
BaseText newConfig)
{
local int i;
local array<TextTemplate> templates;
@ -142,7 +142,7 @@ public final function AnnounceSwappedConfig(
{
templates[i]
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(oldConfig)
.Arg(newConfig);
}
@ -150,7 +150,7 @@ public final function AnnounceSwappedConfig(
}
public final function AnnouncePublicPendingConfigSaved(
class<Feature> featureClass)
BaseText featureName)
{
local int i;
local array<TextTemplate> templates;
@ -167,30 +167,30 @@ public final function AnnouncePublicPendingConfigSaved(
}
templates = MakeArray(pendingConfigSavedPublic);
for (i = 0; i < templates.length; i += 1) {
templates[i].Reset().ArgClass(featureClass);
templates[i].Reset().Arg(featureName);
}
MakeAnnouncement(pendingConfigSavedPublic);
}
public final function AnnouncePrivatePendingConfigSaved(
class<Feature> featureClass)
public final function AnnouncePrivatePendingConfigSaved(BaseText featureName, BaseText configName)
{
if (!pendingConfigSavedPrivate.initialized)
{
pendingConfigSavedPrivate.initialized = true;
pendingConfigSavedPrivate.toSelfReport = _.text.MakeTemplate_S(
"Active config for feature {$TextEmphasis `%1`} was"
"Config \"%2\" for feature {$TextEmphasis `%1`} was"
@ "{$TextNeutral modified}");
}
pendingConfigSavedPrivate.toSelfReport
.Reset()
.ArgClass(featureClass);
.Arg(featureName)
.Arg(configName);
MakeAnnouncement(pendingConfigSavedPrivate);
}
public final function AnnounceCurrentConfig(
class<Feature> featureClass,
BaseText config)
BaseText featureName,
BaseText config)
{
if (!showCurrentConfig.initialized)
{
@ -200,14 +200,14 @@ public final function AnnounceCurrentConfig(
}
showCurrentConfig.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(showCurrentConfig);
}
public final function AnnouncePendingConfig(
class<Feature> featureClass,
BaseText config)
BaseText featureName,
BaseText config)
{
if (!showPendingConfig.initialized)
{
@ -217,14 +217,14 @@ public final function AnnouncePendingConfig(
}
showPendingConfig.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(showPendingConfig);
}
public final function AnnounceConfigCreated(
class<Feature> featureClass,
BaseText config)
BaseText featureName,
BaseText config)
{
if (!configCreated.initialized)
{
@ -235,14 +235,14 @@ public final function AnnounceConfigCreated(
}
configCreated.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(configCreated);
}
public final function AnnounceConfigRemoved(
class<Feature> featureClass,
BaseText config)
BaseText featureName,
BaseText config)
{
if (!configRemoved.initialized)
{
@ -253,17 +253,17 @@ public final function AnnounceConfigRemoved(
}
configRemoved.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(configRemoved);
}
public final function AnnounceConfigEdited(
class<Feature> featureClass,
BaseText config,
BaseText pathToValue,
BaseText oldValue,
BaseText newValue)
BaseText featureName,
BaseText config,
BaseText pathToValue,
BaseText oldValue,
BaseText newValue)
{
if (!configEdited.initialized)
{
@ -275,7 +275,7 @@ public final function AnnounceConfigEdited(
}
configEdited.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config)
.Arg(pathToValue)
.Arg(oldValue)
@ -284,10 +284,10 @@ public final function AnnounceConfigEdited(
}
public final function AnnounceConfigNewValue(
class<Feature> featureClass,
BaseText config,
BaseText pathToValue,
BaseText newValue)
BaseText featureName,
BaseText config,
BaseText pathToValue,
BaseText newValue)
{
if (!configEditedNew.initialized)
{
@ -298,7 +298,7 @@ public final function AnnounceConfigNewValue(
}
configEditedNew.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config)
.Arg(pathToValue)
.Arg(newValue);
@ -306,8 +306,8 @@ public final function AnnounceConfigNewValue(
}
public final function AnnounceAutoEnabledConfig(
class<Feature> featureClass,
BaseText config)
BaseText featureName,
BaseText config)
{
if (!autoEnabled.initialized)
{
@ -318,13 +318,13 @@ public final function AnnounceAutoEnabledConfig(
}
autoEnabled.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(autoEnabled);
}
public final function AnnounceRemovedAutoEnabledConfig(
class<Feature> featureClass)
BaseText featureName)
{
if (!removedAutoEnabled.initialized)
{
@ -335,12 +335,12 @@ public final function AnnounceRemovedAutoEnabledConfig(
}
removedAutoEnabled.toSelfReport
.Reset()
.ArgClass(featureClass);
.Arg(featureName);
MakeAnnouncement(removedAutoEnabled);
}
public final function AnnounceFailedAlreadyNoAutoEnabled(
class<Feature> featureClass)
BaseText featureName)
{
if (!failedAlreadyNoAutoEnabled.initialized)
{
@ -352,13 +352,13 @@ public final function AnnounceFailedAlreadyNoAutoEnabled(
}
failedAlreadyNoAutoEnabled.toSelfReport
.Reset()
.ArgClass(featureClass);
.Arg(featureName);
MakeAnnouncement(failedAlreadyNoAutoEnabled);
}
public final function AnnounceFailedAlreadySameAutoEnabled(
class<Feature> featureClass,
BaseText config)
BaseText featureName,
BaseText config)
{
if (!failedAlreadySameAutoEnabled.initialized)
{
@ -370,14 +370,14 @@ public final function AnnounceFailedAlreadySameAutoEnabled(
}
failedAlreadySameAutoEnabled.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(failedAlreadySameAutoEnabled);
}
public final function AnnounceFailedConfigAlreadyExists(
class<Feature> featureClass,
BaseText config)
BaseText featureName,
BaseText config)
{
if (!failedConfigAlreadyExists.initialized)
{
@ -388,14 +388,14 @@ public final function AnnounceFailedConfigAlreadyExists(
}
failedConfigAlreadyExists.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(failedConfigAlreadyExists);
}
public final function AnnounceFailedConfigDoesNotExist(
class<Feature> featureClass,
BaseText config)
public final function AnnounceFailedConfigDoesNotExist(
BaseText featureName,
BaseText config)
{
if (!failedConfigDoesNotExists.initialized)
{
@ -406,7 +406,7 @@ public final function AnnounceFailedConfigAlreadyExists(
}
failedConfigDoesNotExists.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(failedConfigDoesNotExists);
}
@ -424,7 +424,7 @@ public final function AnnounceFailedToLoadFeatureClass(BaseText failedClassName)
}
public final function AnnounceFailedNoConfigProvided(
class<Feature> featureClass)
BaseText featureName)
{
if (!failedNoConfigProvided.initialized)
{
@ -433,7 +433,7 @@ public final function AnnounceFailedNoConfigProvided(
"{$TextFailure No config specified} and {$TextFailure no"
@ "auto-enabled config} exists for feature {$TextEmphasis `%1`}");
}
failedNoConfigProvided.toSelfReport.Reset().ArgClass(featureClass);
failedNoConfigProvided.toSelfReport.Reset().Arg(featureName);
MakeAnnouncement(failedNoConfigProvided);
}
@ -463,8 +463,8 @@ public final function AnnounceFailedPendingConfigMissing(BaseText config)
}
public final function AnnounceFailedCannotEnableFeature(
class<Feature> featureClass,
BaseText config)
BaseText featureName,
BaseText config)
{
if (!failedCannotEnableFeature.initialized)
{
@ -475,13 +475,13 @@ public final function AnnounceFailedCannotEnableFeature(
}
failedCannotEnableFeature.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(failedCannotEnableFeature);
}
public final function AnnounceFailedNoConfigClass(
class<Feature> featureClass)
BaseText featureName)
{
if (!failedNoConfigClass.initialized)
{
@ -491,7 +491,7 @@ public final function AnnounceFailedNoConfigClass(
@ "class! This is most likely caused by its faulty"
@ "implementation");
}
failedNoConfigClass.toSelfReport.Reset().ArgClass(featureClass);
failedNoConfigClass.toSelfReport.Reset().Arg(featureName);
MakeAnnouncement(failedNoConfigClass);
}
@ -508,7 +508,7 @@ public final function AnnounceFailedBadConfigName(BaseText configName)
}
public final function AnnounceFailedAlreadyDisabled(
class<Feature> featureClass)
BaseText featureName)
{
if (!failedAlreadyDisabled.initialized)
{
@ -517,13 +517,13 @@ public final function AnnounceFailedAlreadyDisabled(
"{$TextFailure Cannot disable} feature {$TextEmphasis `%1`}: it is"
@ "already {$TextNegative disabled}");
}
failedAlreadyDisabled.toSelfReport.Reset().ArgClass(featureClass);
failedAlreadyDisabled.toSelfReport.Reset().Arg(featureName);
MakeAnnouncement(failedAlreadyDisabled);
}
public final function AnnounceFailedAlreadyEnabled(
class<Feature> featureClass,
BaseText config)
BaseText featureName,
BaseText config)
{
if (!failedAlreadyEnabled.initialized)
{
@ -534,14 +534,14 @@ public final function AnnounceFailedAlreadyEnabled(
}
failedAlreadyEnabled.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(failedAlreadyEnabled);
}
public final function AnnounceFailedNoDataForConfig(
class<Feature> featureClass,
BaseText config)
BaseText featureName,
BaseText config)
{
if (!failedNoDataForConfig.initialized)
{
@ -552,7 +552,7 @@ public final function AnnounceFailedNoDataForConfig(
}
failedNoDataForConfig.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config);
MakeAnnouncement(failedNoDataForConfig);
}
@ -570,9 +570,9 @@ public final function AnnounceFailedExpectedObject()
}
public final function AnnounceFailedBadPointer(
class<Feature> featureClass,
BaseText config,
BaseText pointer)
BaseText featureName,
BaseText config,
BaseText pointer)
{
if (!failedBadPointer.initialized)
{
@ -583,7 +583,7 @@ public final function AnnounceFailedBadPointer(
}
failedBadPointer.toSelfReport
.Reset()
.ArgClass(featureClass)
.Arg(featureName)
.Arg(config)
.Arg(pointer);
MakeAnnouncement(failedBadPointer);

30
sources/Commands/ACommandGod.uc

@ -46,21 +46,21 @@ protected function Finalizer()
protected function BuildData(CommandDataBuilder builder)
{
builder.Name(P("god")).Group(P("gameplay"))
.Summary(P("Command for making player immortal."));
builder.RequireTarget()
.Describe(P("Gives targeted players god status, making them"
builder.Group(P("gameplay"));
builder.Summary(P("Command for making player immortal."));
builder.RequireTarget();
builder.Describe(P("Gives targeted players god status, making them"
@ "invincible."));
builder.SubCommand(P("list"))
.Describe(P("Reports godhood status of targeted players."));
builder.SubCommand(P("strip"))
.Describe(P("Strips targeted players from the godhood status."));
builder.Option(P("demi"))
.Describe(P("This flag makes targeted players \"demigods\" instead -"
builder.SubCommand(P("list"));
builder.Describe(P("Reports godhood status of targeted players."));
builder.SubCommand(P("strip"));
builder.Describe(P("Strips targeted players from the godhood status."));
builder.Option(P("demi"));
builder.Describe(P("This flag makes targeted players \"demigods\" instead -"
@ "they still cannot die, but they can take any non-lethal"
@ "damage."));
builder.Option(P("unmovable"))
.Describe(P("This flag also prevents targeted players from being"
builder.Option(P("unmovable"));
builder.Describe(P("This flag also prevents targeted players from being"
@ "affected by the momentum trasnferred from damaging attacks."));
announcer = ACommandGod_Announcer(
_.memory.Allocate(class'ACommandGod_Announcer'));
@ -69,8 +69,9 @@ protected function BuildData(CommandDataBuilder builder)
protected function ExecutedFor(
EPlayer target,
CallData arguments,
EPlayer instigator)
{
EPlayer instigator,
CommandPermissions permissions
) {
local GodStatus newGodStatus;
announcer.Setup(target, instigator, othersConsole);
@ -215,6 +216,7 @@ private final function UpdateHealthSignalConnection()
defaultproperties
{
preferredName = "god"
TDAMAGE = 0
stringConstants(0) = "damage"
TMOMENTUM = 1

66
sources/Commands/ACommandInventory.uc

@ -34,57 +34,57 @@ var protected const int TLISTS_SKIPPED;
protected function BuildData(CommandDataBuilder builder)
{
builder.Name(T(TINVENTORY)).Group(P("gameplay"))
.Summary(P("Manages player's inventory."))
.Describe(P("Command for displaying and editing players' inventories."
builder.Group(P("gameplay"));
builder.Summary(P("Manages player's inventory."));
builder.Describe(P("Command for displaying and editing players' inventories."
@ "If called without specifying subcommand - simply displays"
@ "targeted player's inventory."));
builder.RequireTarget();
builder.SubCommand(T(TADD))
.OptionalParams()
.ParamTextList(T(TITEMS))
.Describe(P("This command adds items (based on listed templates) to"
builder.SubCommand(T(TADD));
builder.OptionalParams();
builder.ParamTextList(T(TITEMS));
builder.Describe(P("This command adds items (based on listed templates) to"
@ "the targeted player's inventory."
@ "Instead of templates item aliases can be specified."));
builder.SubCommand(T(TREMOVE))
.OptionalParams()
.ParamTextList(T(TITEMS))
.Describe(P("This command removes items (based on listed templates)"
builder.SubCommand(T(TREMOVE));
builder.OptionalParams();
builder.ParamTextList(T(TITEMS));
builder.Describe(P("This command removes items (based on listed templates)"
@ "from the targeted player's inventory."
@ "Instead of templates item aliases can be specified."));
builder.SubCommand(T(TSET))
.OptionalParams()
.ParamTextList(T(TITEMS))
.Describe(P("This command acts like combination of two commands -"
builder.SubCommand(T(TSET));
builder.OptionalParams();
builder.ParamTextList(T(TITEMS));
builder.Describe(P("This command acts like combination of two commands -"
@ "first removing all items from the player's current inventory and"
@ "then adding specified items. first clears inventory"
@ "(based on specified options) and then "));
builder.Option(T(TEQUIP))
.Describe(F("Affect items currently equipped by the targeted player."
builder.Option(T(TEQUIP));
builder.Describe(F("Affect items currently equipped by the targeted player."
@ "Releveant for a {$TextEmphasis remove} subcommand."));
builder.Option(T(TLIST))
.Describe(P("Include weapons from specified group into the list."))
.ParamTextList(T(TLISTS_NAMES));
builder.Option(T(TAMMO))
.Describe(P("When adding weapons - signals that their"
builder.Option(T(TLIST));
builder.Describe(P("Include weapons from specified group into the list."));
builder.ParamTextList(T(TLISTS_NAMES));
builder.Option(T(TAMMO));
builder.Describe(P("When adding weapons - signals that their"
@ "ammo / charge / whatever has to be filled after addition."));
builder.Option(T(TKEEP))
.Describe(F("Removing items by default means simply destroying them."
builder.Option(T(TKEEP));
builder.Describe(F("Removing items by default means simply destroying them."
@ "This flag makes command to try and keep them in some form."
@ "Success for all items is not guaranteed."));
builder.Option(T(THIDDEN))
.Describe(F("Some of the items in the inventory are"
builder.Option(T(THIDDEN));
builder.Describe(F("Some of the items in the inventory are"
@ "{$TextEmphasis hidden} and are not supposed to be seem by"
@ "the player. To avoid weird behavior, {$TextEmphasis inventory}"
@ "command by default ignores them when affecting groups of items"
@ "(like when removing all items) unless they're directly"
@ "specified. This flag tells it to also affect hidden items."));
builder.Option(T(TFORCE))
.Describe(P("Sometimes adding and removing items is impossible due to"
builder.Option(T(TFORCE));
builder.Describe(P("Sometimes adding and removing items is impossible due to"
@ "the limitations imposed by the game. This option allows to"
@ "ignore some of those limitation."));
builder.Option(T(TALL), P("A"))
.Describe(F("This flag is used when removing items. If user has"
builder.Option(T(TALL), P("A"));
builder.Describe(F("This flag is used when removing items. If user has"
@ "specified any weapon templates - it means"
@ "\"remove all items with these tempaltes from inventory\","
@ "but if user has not specified any templated it simply means"
@ -94,8 +94,9 @@ protected function BuildData(CommandDataBuilder builder)
protected function ExecutedFor(
EPlayer target,
CallData arguments,
EPlayer instigator)
{
EPlayer instigator,
CommandPermissions permissions
) {
local InventoryTool tool;
local ArrayList itemsArray, specifiedLists;
LoadUserFlags(arguments.options);
@ -286,6 +287,7 @@ protected function array<Text> LoadItemsList(
defaultproperties
{
preferredName = "inventory"
TINVENTORY = 0
stringConstants(0) = "inventory"
TADD = 1

38
sources/Commands/ACommandNick.uc

@ -34,30 +34,31 @@ protected function Finalizer()
protected function BuildData(CommandDataBuilder builder)
{
builder.Name(P("nick")).Group(P("gameplay"))
.Summary(P("Changes nickname."));
builder.Group(P("gameplay"));
builder.Summary(P("Changes nickname."));
builder.RequireTarget();
builder.ParamRemainder(P("nick"))
.Describe(P("Changes nickname of targeted players to <nick>."));
builder.Option(P("plain"))
.Describe(P("Take nickname exactly as typed, without attempting to"
builder.ParamRemainder(P("nick"));
builder.Describe(P("Changes nickname of targeted players to <nick>."));
builder.Option(P("plain"));
builder.Describe(P("Take nickname exactly as typed, without attempting to"
@ "treat it like formatted string."));
builder.Option(P("fix"), P("f"))
.Describe(P("In case of a nickname with erroroneous formatting or"
builder.Option(P("fix"), P("f"));
builder.Describe(P("In case of a nickname with erroroneous formatting or"
@ "invalid default color (specified with `--color`),"
@ "try to fix/ignore it instead of simply rejecting it."));
builder.Option(P("color"))
.Describe(P("Color to use for the nickname. In case nickname is already"
@ "colored, this flag will only affects uncolored parts."))
.ParamText(P("default_color"));
builder.Option(P("color"));
builder.Describe(P("Color to use for the nickname. In case nickname is already"
@ "colored, this flag will only affects uncolored parts."));
builder.ParamText(P("default_color"));
announcer = ACommandNick_Announcer(
_.memory.Allocate(class'ACommandNick_Announcer'));
}
protected function Executed(
CallData arguments,
EPlayer callerPlayer)
{
EPlayer callerPlayer,
CommandPermissions permissions
) {
local Text givenName;
local array<FormattingErrorsReport.FormattedStringError> errors;
@ -89,8 +90,9 @@ protected function Executed(
protected function ExecutedFor(
EPlayer target,
CallData arguments,
EPlayer instigator)
{
EPlayer instigator,
CommandPermissions permissions
) {
local Text alteredVersion;
if (!foundErrors || arguments.options.HasKey(P("fix")))
@ -126,6 +128,6 @@ protected function bool TryChangeDefaultColor(BaseText specifiedColor)
return false;
}
defaultproperties
{
defaultproperties {
preferredName = "nick"
}

102
sources/Commands/ACommandSpawn.uc

@ -30,72 +30,76 @@ protected function Finalizer()
protected function BuildData(CommandDataBuilder builder)
{
builder.Name(P("spawn")).Group(P("debug"))
.Summary(P("Spawns new entity on the map."));
builder.ParamText(P("template"),, P("entity"))
.Describe(P("Spawns new entity based on the given template at the point"
builder.Group(P("debug"));
builder.Summary(P("Spawns new entity on the map."));
builder.ParamText(P("template"),, P("entity"));
builder.Describe(P("Spawns new entity based on the given template at the point"
@ "player is currently looking at."));
builder.SubCommand(P("at"))
.ParamText(P("template"),, P("entity"))
.ParamNumber(P("x"))
.ParamNumber(P("y"))
.ParamNumber(P("z"))
.Describe(P("Spawns new entity based on the given template at"
builder.SubCommand(P("at"));
builder.ParamText(P("template"),, P("entity"));
builder.ParamNumber(P("x"));
builder.ParamNumber(P("y"));
builder.ParamNumber(P("z"));
builder.Describe(P("Spawns new entity based on the given template at"
@ "the point, given by the coordinates"));
announcer = ACommandSpawn_Announcer(
_.memory.Allocate(class'ACommandSpawn_Announcer'));
}
protected function Executed(
CallData arguments,
EPlayer instigator)
{
local Text template;
local Vector spawnLocation;
CallData arguments,
EPlayer instigator,
CommandPermissions permissions
) {
local HashTable value;
local Vector spawnLocation;
announcer.Setup(none, instigator, othersConsole);
template = arguments.parameters.GetText(P("template"));
value = arguments.parameters.GetHashTable(P("template"));
if (arguments.subCommandName.IsEmpty()) {
SpawnInInstigatorSight(instigator, template);
}
else if (arguments.subCommandName.Compare(P("at"), SCASE_INSENSITIVE))
{
SpawnInInstigatorSight(instigator, value);
} else if (arguments.subCommandName.Compare(P("at"), SCASE_INSENSITIVE)) {
spawnLocation.x = arguments.parameters.GetFloat(P("x"));
spawnLocation.y = arguments.parameters.GetFloat(P("y"));
spawnLocation.z = arguments.parameters.GetFloat(P("z"));
SpawnAt(instigator, template, spawnLocation);
SpawnAt(instigator, value, spawnLocation);
}
_.memory.Free(template);
_.memory.Free(value);
}
private final function SpawnAt(
EPlayer instigator,
BaseText template,
Vector spawnLocation)
{
EPlayer instigator,
HashTable value,
Vector spawnLocation
) {
local Text humanReadable, template;
local EPlaceable result;
humanReadable = value.GetText(P("alias"));
template = value.GetText(P("value"));
result = _server.kf.world.Spawn(template, spawnLocation);
if (result != none) {
announcer.AnnounceSpawned(template);
}
else {
announcer.AnnounceSpawningFailed(template);
announcer.AnnounceSpawned(humanReadable);
} else {
announcer.AnnounceSpawningFailed(humanReadable);
}
_.memory.Free(result);
_.memory.Free2(humanReadable, result);
}
private final function SpawnInInstigatorSight(
EPlayer instigator,
BaseText template)
{
local EPlaceable result;
local Vector spawnLocation;
local TracingIterator iter;
EPlayer instigator,
HashTable value
) {
local EPlaceable result;
local Vector spawnLocation;
local TracingIterator iter;
local Text humanReadable, template;
humanReadable = value.GetText(P("alias"));
template = value.GetText(P("value"));
iter = _server.kf.world.TracePlayerSight(instigator);
if (iter.LeaveOnlyVisible().HasFinished())
{
iter.LeaveOnlyVisible();
if (iter.HasFinished()) {
announcer.AnnounceFailedTrace();
return;
}
@ -103,22 +107,18 @@ private final function SpawnInInstigatorSight(
result = _server.kf.world.Spawn(template, spawnLocation);
// Shift position back a little and try again;
// this should fix a ton of spawning failures
if (result == none)
{
spawnLocation = spawnLocation +
Normal(iter.GetTracingStart() - spawnLocation) * 100;
if (result == none) {
spawnLocation = spawnLocation + Normal(iter.GetTracingStart() - spawnLocation) * 100;
result = _server.kf.world.Spawn(template, spawnLocation);
}
if (result != none) {
announcer.AnnounceSpawned(template);
announcer.AnnounceSpawned(humanReadable);
} else {
announcer.AnnounceSpawningFailed(humanReadable);
}
else {
announcer.AnnounceSpawningFailed(template);
}
_.memory.Free(result);
_.memory.Free(iter);
_.memory.Free4(result, iter, humanReadable, result);
}
defaultproperties
{
defaultproperties {
preferredName = "spawn"
}

104
sources/Commands/ACommandTrader.uc

@ -38,60 +38,60 @@ protected function Finalizer()
protected function BuildData(CommandDataBuilder builder)
{
builder.Name(T(TTRADER)).Group(P("gameplay"))
.Summary(P("Manages trader time and available traders."))
.Describe(P("Enables of disables trading."))
.ParamBoolean(T(TENABLE));
builder.SubCommand(T(TTIME))
.Describe(F("Changes current trader time if numeric value is specified."
builder.Group(P("gameplay"));
builder.Summary(P("Manages trader time and available traders."));
builder.Describe(P("Enables of disables trading."));
builder.ParamBoolean(T(TENABLE));
builder.SubCommand(T(TTIME));
builder.Describe(F("Changes current trader time if numeric value is specified."
@ "You can also pause trader countdown by specifying"
@ "{$TextEmphasis pause} or turn it back on with"
@ "{$TextEmphasis unpause}."))
.ParamText(T(TTRADER_TIME));
builder.SubCommand(T(TLIST))
.Describe(P("Lists names of all available traders and"
@ "{$TextEmphasis unpause}."));
builder.ParamText(T(TTRADER_TIME));
builder.SubCommand(T(TLIST));
builder.Describe(P("Lists names of all available traders and"
@ "marks closest one to the caller."));
builder.SubCommand(T(TOPEN))
.Describe(P("Opens specified traders."))
.OptionalParams()
.ParamTextList(T(TTRADERS));
builder.SubCommand(T(TCLOSE))
.Describe(P("Closes specified traders."))
.OptionalParams()
.ParamTextList(T(TTRADERS));
builder.SubCommand(T(TAUTO_OPEN))
.Describe(P("Sets whether specified traders are open automatically."))
.ParamBoolean(T(TAUTO_OPEN_QUESTION))
.OptionalParams()
.ParamTextList(T(TTRADERS));
builder.SubCommand(T(TSELECT))
.Describe(P("Selects specified trader."))
.OptionalParams()
.ParamText(T(TTRADER));
builder.SubCommand(T(TBOOT))
.Describe(P("Boots all players from specified traders. If no traders"
@ "were specified - assumes that all of them should be affected."))
.OptionalParams()
.ParamTextList(T(TTRADERS));
builder.SubCommand(T(TENABLE))
.Describe(P("Enables specified traders."))
.OptionalParams()
.ParamTextList(T(TTRADERS));
builder.SubCommand(T(TDISABLE))
.Describe(P("Disables specified traders."))
.OptionalParams()
.ParamTextList(T(TTRADERS));
builder.Option(T(TALL))
.Describe(P("If sub-command targets shops, this flag will make it"
builder.SubCommand(T(TOPEN));
builder.Describe(P("Opens specified traders."));
builder.OptionalParams();
builder.ParamTextList(T(TTRADERS));
builder.SubCommand(T(TCLOSE));
builder.Describe(P("Closes specified traders."));
builder.OptionalParams();
builder.ParamTextList(T(TTRADERS));
builder.SubCommand(T(TAUTO_OPEN));
builder.Describe(P("Sets whether specified traders are open automatically."));
builder.ParamBoolean(T(TAUTO_OPEN_QUESTION));
builder.OptionalParams();
builder.ParamTextList(T(TTRADERS));
builder.SubCommand(T(TSELECT));
builder.Describe(P("Selects specified trader."));
builder.OptionalParams();
builder.ParamText(T(TTRADER));
builder.SubCommand(T(TBOOT));
builder.Describe(P("Boots all players from specified traders. If no traders"
@ "were specified - assumes that all of them should be affected."));
builder.OptionalParams();
builder.ParamTextList(T(TTRADERS));
builder.SubCommand(T(TENABLE));
builder.Describe(P("Enables specified traders."));
builder.OptionalParams();
builder.ParamTextList(T(TTRADERS));
builder.SubCommand(T(TDISABLE));
builder.Describe(P("Disables specified traders."));
builder.OptionalParams();
builder.ParamTextList(T(TTRADERS));
builder.Option(T(TALL));
builder.Describe(P("If sub-command targets shops, this flag will make it"
@ "target all the available shops."));
builder.Option(T(TCLOSEST))
.Describe(P("If sub-command targets shops, this flag will make it also"
builder.Option(T(TCLOSEST));
builder.Describe(P("If sub-command targets shops, this flag will make it also"
@ "target closest shop to the caller."));
builder.Option(T(TIGNORE_DOORS))
.Describe(F("When used with {$TextEmphasis select} sub-command, it will"
builder.Option(T(TIGNORE_DOORS));
builder.Describe(F("When used with {$TextEmphasis select} sub-command, it will"
@ "neither open or close doors."));
builder.Option(T(TIGNORE_PLAYERS), P("I"))
.Describe(P("Normally commands that close doors will automatically boot"
builder.Option(T(TIGNORE_PLAYERS), P("I"));
builder.Describe(P("Normally commands that close doors will automatically boot"
@ "players from inside to prevent locking them in. This flag forces"
@ "this command to leave players inside. However they can still be"
@ "booted out at the end of trading time. Also it is impossible to"
@ -100,8 +100,11 @@ protected function BuildData(CommandDataBuilder builder)
_.memory.Allocate(class'ACommandTrader_Announcer'));
}
protected function Executed(CallData arguments, EPlayer instigator)
{
protected function Executed(
CallData arguments,
EPlayer instigator,
CommandPermissions permissions
) {
local bool newTradingStatus;
announcer.Setup(none, instigator, othersConsole);
@ -632,6 +635,7 @@ protected function WriteTraderTags(ETrader traderToWrite, bool isClosest)
defaultproperties
{
preferredName = "trader"
TLIST = 0
stringConstants(0) = "list"
TOPEN = 1

142
sources/Commands/ACommandUserData.uc

@ -1,6 +1,6 @@
/**
* Command for changing amount of money players have.
* Copyright 2022 Anton Tarasenko
* Copyright 2022-2023 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@ -21,24 +21,23 @@ class ACommandUserData extends Command;
var private array<EPlayer> playerQueue;
// TODO: Finish this command after JSON parameters are added
protected function BuildData(CommandDataBuilder builder)
{
builder.Name(P("userdata")).Group(P("admin"))
.Summary(P("Allows to read and write custom user data for players."));
builder.Group(P("admin"));
builder.Summary(P("Allows to read and write custom user data for players."));
builder.RequireTarget();
builder.ParamText(P("groupName"))
.OptionalParams()
.ParamText(P("dataName"))
.Describe(P("Reads user data stored for targeted user under group"
builder.ParamText(P("groupName"));
builder.OptionalParams();
builder.ParamText(P("dataName"));
builder.Describe(F("Reads user data stored for targeted user under group"
@ "{$TextEmphasis `groupName`} and name"
@ "{$TextEmphasis `dataName`}. If {$TextEmphasis `dataName`} is"
@ "omitted, the data inside the whjole group will be read."));
builder.SubCommand(P("write"))
.ParamText(P("groupName"))
.ParamText(P("dataName"))
.ParamObject(P("newData"))
.Describe(P("Stores new user data {$TextEmphasis `newData`} for"
@ "omitted, the data inside the whole group will be read."));
builder.SubCommand(P("write"));
builder.ParamText(P("groupName"));
builder.ParamText(P("dataName"));
builder.ParamJSON(P("newData"));
builder.Describe(F("Stores new user data {$TextEmphasis `newData`} for"
@ "targeted user under group {$TextEmphasis `groupName`} and name"
@ "{$TextEmphasis `dataName`}."));
}
@ -46,74 +45,85 @@ protected function BuildData(CommandDataBuilder builder)
protected function ExecutedFor(
EPlayer target,
CallData arguments,
EPlayer instigator)
{
EPlayer instigator,
CommandPermissions permissions
) {
local AcediaObject userData;
local Text groupName, dataName;
if (arguments.subCommandName.IsEmpty())
{
target
.GetIdentity()
.ReadPersistentData(
arguments.parameters.GetText(P("groupName")),
arguments.parameters.GetText(P("dataName")))
.connect = UserDataRead;
groupName = arguments.parameters.GetText(P("groupName"));
dataName = arguments.parameters.GetText(P("dataName"));
userData = arguments.parameters.GetItem(P("newData"));
if (arguments.subCommandName.IsEmpty()) {
ReadUserData(target, groupName, dataName);
}
else
{
userData = arguments.parameters.GetHashTable(P("newData"));
target
.GetIdentity()
.WritePersistentData(
arguments.parameters.GetText(P("groupName")),
arguments.parameters.GetText(P("dataName")),
userData);
else {
WriteUserData(target, groupName, dataName, userData);
}
playerQueue[playerQueue.length] = target;
target.NewRef();
_.memory.Free(dataName);
_.memory.Free(groupName);
}
private final function UserDataRead(
Database.DBQueryResult result,
AcediaObject userData,
Database source)
private final function ReadUserData(
EPlayer targetPlayer,
BaseText groupName,
BaseText dataName)
{
local User identity;
local AcediaObject rawData;
local MutableText dataAsJSON;
local Text targetPlayerName;
local EPlayer targetPlayer;
local MutableText asJSON;
if (playerQueue.length <= 0)
{
targetPlayer
.BorrowConsole()
.UseColorOnce(_.color.TextFailure)
.Write(F("There was an internal error with `userdata` command. "))
.Write(P("Please report it!"));
return;
}
targetPlayer = playerQueue[0];
playerQueue.Remove(0, 1);
if (result != DBR_Success)
{
targetPlayer
.BorrowConsole()
.UseColorOnce(_.color.TextFailure)
.Write(F("There was an error reading user data, error code: "))
.WriteLine(_.text.FromInt(int(result)));
identity = targetPlayer.GetIdentity();
if (identity == none) {
return;
}
targetPlayerName = targetPlayer.GetName();
asJSON = _.json.PrettyPrint(userData);
rawData = identity.GetPersistentData(groupName, dataName);
dataAsJSON = _.json.PrettyPrint(rawData);
targetPlayer.BorrowConsole()
.Write(F("{$TextPositive User data for player}"))
.Write(F("User data for player "))
.Write(targetPlayerName)
.Write(P(":"))
.WriteLine(asJSON);
_.memory.Free(targetPlayer);
_.memory.Free(asJSON);
.Write(P(": "))
.WriteLine(dataAsJSON);
_.memory.Free(dataAsJSON);
_.memory.Free(rawData);
_.memory.Free(targetPlayerName);
_.memory.Free(identity);
}
defaultproperties
private final function WriteUserData(
EPlayer targetPlayer,
BaseText groupName,
BaseText dataName,
AcediaObject rawData)
{
local User identity;
local Text targetPlayerName;
identity = targetPlayer.GetIdentity();
if (identity == none) {
return;
}
targetPlayerName = targetPlayer.GetName();
if (identity.SetPersistentData(groupName, dataName, rawData))
{
targetPlayer.BorrowConsole()
.Write(P("User data for player "))
.Write(targetPlayerName)
.WriteLine(F(" was {$TextPositive successfully} changed!"));
}
else
{
targetPlayer.BorrowConsole()
.Write(P("User data for player "))
.Write(targetPlayerName)
.WriteLine(F(" has {$TextPositive failed} to change!"));
}
_.memory.Free(targetPlayerName);
_.memory.Free(identity);
}
defaultproperties {
preferredName = "userdata"
}

112
sources/Features/FutileChat/FutilityChat.uc

@ -1,6 +1,6 @@
/**
* Config object for `FutilityChat_Feature`.
* Copyright 2022 Anton Tarasenko
* Config class for storing map lists.
* Copyright 2022-2023 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@ -21,8 +21,7 @@ class FutilityChat extends FeatureConfig
perobjectconfig
config(FutilityChat);
enum ChatColorSetting
{
enum ChatColorSetting {
CCS_DoNothing,
CCS_TeamColorForced,
CCS_ConfigColorForced,
@ -30,16 +29,45 @@ enum ChatColorSetting
CCS_ConfigColorCustom
};
var public config ChatColorSetting colorSetting;
var public config Color configuredColor;
var public config float teamColorModifier;
/// How to color text chat messages?
///
/// 1. `CCS_DoNothing` - do not change color in any way;
/// 2. `CCS_TeamColorForced` - force players' team colors for
/// their messages;
/// 3. `CCS_ConfigColorForced` - force `configuredColor` value for
/// players' messages;
/// 4. `CCS_TeamColorCustom` - use players' team colors for
/// their messages by default, but allow to change color with formatted tags
/// (e.g. "Stop right there, {$crimson criminal} scum!");
/// 5. `CCS_ConfigColorCustom` - use `configuredColor` value for
/// messages by default, but allow to change color with formatted
/// tags (e.g. "Stop right there, {$crimson criminal} scum!");
///
/// Default is `CCS_DoNothing`, corresponding to vanilla behaviour.
var public config ChatColorSetting colorSetting;
/// Color that will be used if either of `CCS_ConfigColorForced` or
/// `CCS_ConfigColorCustom` options were used in `colorSetting`.
/// Default value is white: (R=255,G=255,B=255,A=255), has no vanilla
/// equivalent.
var public config Color configuredColor;
/// Allows to modify team color's value for the chat messages
/// (if either of `CCS_TeamColorForced` or `CCS_TeamColorCustom` options
/// were used) to be lighter or darker.
/// This value is clamped between -1 and 1:
///
/// * `0` means using the same color;
/// * range (0; 1) - gives you lighter colors (`1` being white);
/// * range (-1; 0) - gives you darker colors (`-1` being black);
///
/// Default value is `0.6`, has no vanilla equivalent.
var public config float teamColorModifier;
protected function HashTable ToData()
{
protected function HashTable ToData() {
local HashTable data;
local Text colorAsText;
local Text colorAsText;
data = __().collections.EmptyHashTable();
data.SetString(P("colorSetting"), string(colorSetting));
data.SetString(P("colorSetting"), StringFromColorSetting(colorSetting));
colorAsText = _.color.ToText(configuredColor);
data.SetItem(P("configuredColor"), colorAsText);
_.memory.Free(colorAsText);
@ -47,24 +75,21 @@ protected function HashTable ToData()
return data;
}
protected function FromData(HashTable source)
{
protected function FromData(HashTable source) {
local Text storedText;
if (source == none) {
return;
if (source != none) {
storedText = source.GetText(P("colorSetting"));
colorSetting = ColorSettingFromText(storedText);
_.memory.Free(storedText);
storedText = source.GetText(P("configuredColor"));
_.color.Parse(storedText, configuredColor);
_.memory.Free(storedText);
teamColorModifier = source.GetFloat(P("teamColorModifier"), 0.5);
}
storedText = source.GetText(P("colorSetting"));
colorSetting = ColorSettingFromText(storedText);
_.memory.Free(storedText);
storedText = source.GetText(P("configuredColor"));
_.color.Parse(storedText, configuredColor);
_.memory.Free(storedText);
teamColorModifier = source.GetFloat(P("teamColorModifier"), 0.5);
}
private function ChatColorSetting ColorSettingFromText(
BaseText permissions)
{
private function ChatColorSetting ColorSettingFromText(BaseText permissions) {
if (permissions == none) {
return CCS_DoNothing;
}
@ -83,17 +108,34 @@ private function ChatColorSetting ColorSettingFromText(
return CCS_DoNothing;
}
protected function DefaultIt()
{
colorSetting = CCS_DoNothing;
configuredColor = _.color.RGB(255, 255, 255);
teamColorModifier = 0.6;
private function string StringFromColorSetting(ChatColorSetting permissions) {
if (permissions == CCS_DoNothing) {
return "DoNothing";
}
if (permissions == CCS_TeamColorForced) {
return "TeamColorForced";
}
if (permissions == CCS_ConfigColorForced) {
return "ConfigColorForced";
}
if (permissions == CCS_TeamColorCustom) {
return "TeamColorCustom";
}
if (permissions == CCS_ConfigColorCustom) {
return "ConfigColorCustom";
}
return "DoNothing";
}
protected function DefaultIt() {
colorSetting = CCS_DoNothing;
configuredColor = _.color.RGB(255, 255, 255);
teamColorModifier = 0.6;
}
defaultproperties
{
defaultproperties {
configName = "FutilityChat"
colorSetting = CCS_DoNothing
configuredColor = (R=255,G=255,B=255,A=255)
teamColorModifier = 0.6
colorSetting = CCS_DoNothing
configuredColor = (R=255,G=255,B=255,A=255)
teamColorModifier = 0.6
}

114
sources/Features/FutileChat/FutilityChat_Feature.uc

@ -1,6 +1,6 @@
/**
* This feature allows to configure color of text chat messages.
* Copyright 2022 Anton Tarasenko
* Config class for storing map lists.
* Copyright 2022-2023 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@ -20,123 +20,82 @@
class FutilityChat_Feature extends Feature
dependson(FutilityChat);
// How to color text chat messages?
// 1. `CCS_DoNothing` - do not change color in any way;
// 2. `CCS_TeamColorForced` - force players' team colors for
// their messages;
// 3. `CCS_ConfigColorForced` - force `configuredColor` value for
// players' messages;
// 4. `CCS_TeamColorCustom` - use players' team colors for
// their messages by default, but allow to change color with formatted
// tags (e.g. "Stop right there, {$crimson criminal} scum!");
// 5. `CCS_ConfigColorCustom` - use `configuredColor` value for
// messages by default, but allow to change color with formatted
// tags (e.g. "Stop right there, {$crimson criminal} scum!");
// Default is `CCS_DoNothing`, corresponding to vanilla behaviour.
var private /*config*/ FutilityChat.ChatColorSetting colorSetting;
// Color that will be used if either of `CCS_ConfigColorForced` or
// `CCS_ConfigColorCustom` options were used in `colorSetting`.
// Default value is white: (R=255,G=255,B=255,A=255),
// has no vanilla equivalent.
var private /*config*/ Color configuredColor;
// Allows to modify team color's value for the chat messages
// (if either of `CCS_TeamColorForced` or `CCS_TeamColorCustom` options
// were used) to be lighter or darker.
// This value is clamped between -1 and 1.
// * `0` means using the same color;
// * range (0; 1) - gives you lighter colors (`1` being white);
// * range (-1; 0) - gives you darker colors (`-1` being black);
// Default value is `0.6`, has no vanilla equivalent.
var private /*config*/ float teamColorModifier;
var private /*config*/ FutilityChat.ChatColorSetting colorSetting;
var private /*config*/ Color configuredColor;
var private /*config*/ float teamColorModifier;
// Keep track of whether we connected to necessary signals, so that we can
// connect to them or disconnect from them once setting get updated
/// Keep track of whether we connected to necessary signals, so that we can
/// connect to them or disconnect from them once setting get updated
var private bool connectedToSignal;
protected function OnDisabled()
{
if (connectedToSignal)
{
connectedToSignal = false;
protected function OnEnabled() {
if (colorSetting != CCS_DoNothing) {
_.chat.OnMessage(self).connect = ReformatChatMessage;
}
}
protected function OnDisabled() {
if (colorSetting != CCS_DoNothing) {
_.chat.OnMessage(self).Disconnect();
}
}
protected function SwapConfig(FeatureConfig config)
{
local bool configRequiresSignal;
local FutilityChat newConfig;
local bool configRequiresSignal;
local FutilityChat newConfig;
newConfig = FutilityChat(config);
if (newConfig == none) {
return;
}
colorSetting = newConfig.colorSetting;
configuredColor = newConfig.configuredColor;
teamColorModifier = newConfig.teamColorModifier;
configRequiresSignal = (colorSetting != CCS_DoNothing);
// Enable or disable censoring if `IsAnyCensoringEnabled()`'s response
// has changed.
if (!connectedToSignal && configRequiresSignal)
{
connectedToSignal = true;
_.chat.OnMessage(self).connect = ReformatChatMessage;
}
if (connectedToSignal && !configRequiresSignal)
{
connectedToSignal = false;
_.chat.OnMessage(self).Disconnect();
}
colorSetting = newConfig.colorSetting;
configuredColor = newConfig.configuredColor;
teamColorModifier = newConfig.teamColorModifier;
configRequiresSignal = (colorSetting != CCS_DoNothing);
}
private function bool ReformatChatMessage(
EPlayer sender,
EPlayer sender,
MutableText message,
bool teamMessage)
{
local Text messageCopy;
local BaseText.Formatting defaultFormatting;
bool teamMessage
) {
local Text messageCopy;
local BaseText.Formatting defaultFormatting;
if (sender == none) return true;
if (message == none) return true;
if (colorSetting == CCS_DoNothing) return true;
defaultFormatting.isColored = true;
if ( colorSetting == CCS_TeamColorForced
|| colorSetting == CCS_TeamColorCustom)
{
if (colorSetting == CCS_TeamColorForced || colorSetting == CCS_TeamColorCustom) {
defaultFormatting.color = ModColor(sender.GetTeamColor());
}
else {
} else {
defaultFormatting.color = configuredColor;
}
if (message.StartsWith(P("|"))) {
message.Remove(0, 1);
}
else if ( colorSetting != CCS_TeamColorForced
&& colorSetting != CCS_ConfigColorForced)
{
} else if (colorSetting != CCS_TeamColorForced && colorSetting != CCS_ConfigColorForced) {
messageCopy = message.Copy();
class'FormattingStringParser'.static
.ParseFormatted(messageCopy, message.Clear());
class'FormattingStringParser'.static.ParseFormatted(messageCopy, message.Clear());
_.memory.Free(messageCopy);
}
message.ChangeDefaultFormatting(defaultFormatting);
return true;
}
private function Color ModColor(Color inputColor)
{
private function Color ModColor(Color inputColor) {
local Color mixColor;
local Color outputColor;
local float clampedModifier;
if (Abs(teamColorModifier) < 0.001) {
return inputColor;
}
clampedModifier = FClamp(teamColorModifier, -1.0, 1.0);
if (clampedModifier > 0) {
mixColor = _.color.White;
}
else
{
} else {
mixColor = _.color.Black;
clampedModifier *= -1.0;
}
@ -147,7 +106,6 @@ private function Color ModColor(Color inputColor)
return outputColor;
}
defaultproperties
{
defaultproperties {
configClass = class'FutilityChat'
}

164
sources/Features/FutileNickames/FutilityNicknames.uc

@ -1,6 +1,6 @@
/**
* Config object for `FutileNickames_Feature`.
* Copyright 2022 Anton Tarasenko
* Config class for storing map lists.
* Copyright 2022-2023 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@ -21,62 +21,108 @@ class FutilityNicknames extends FeatureConfig
perobjectconfig
config(FutilityNicknames);
enum NicknameSpacesAction
{
enum NicknameSpacesAction {
NSA_DoNothing,
NSA_Trim,
NSA_Simplify
};
enum NicknameColorPermissions
{
enum NicknameColorPermissions {
NCP_ForbidColor,
NCP_ForceTeamColor,
NCP_ForceSingleColor,
NCP_AllowAnyColor
};
var public config NicknameSpacesAction spacesAction;
var public config NicknameColorPermissions colorPermissions;
var public config bool replaceSpacesWithUnderscores;
var public config bool removeSingleQuotationMarks;
var public config bool removeDoubleQuotationMarks;
var public config bool correctEmptyNicknames;
var public config int maxNicknameLength;
var public config array<string> fallbackNickname;
/// How to treat whitespace characters inside players' nicknames:
///
/// * `NSA_DoNothing` - does nothing, leaving whitespaces as they are;
/// * `NSA_Trim` - removes leading and trailing whitespaces for nicknames;
/// * `NSA_Simplify` - removes leading and trailing whitespaces
/// for nicknames, also reducing a sequence of whitespaces inside
/// nickname to a single space, e.g. "my nick" becomes "my nick".
///
/// Default is `NSA_DoNothing`, same as on vanilla.
var public config NicknameSpacesAction spacesAction;
/// How to treat colored nicknames:
///
/// * `NCP_ForbidColor` - completely strips down any color from nicknames;
/// * `NCP_ForceTeamColor` - forces all nicknames to have player's current
/// team's color;
/// * `NCP_ForceSingleColor` - allows nickname to be painted with a single
/// color (sets nickname's color to that of the first character);
/// * `NCP_AllowAnyColor` - allows nickname to be colored in any way player
/// wants.
/// Default is `NCP_ForbidColor`, same as on vanilla.
var public config NicknameColorPermissions colorPermissions;
/// Set this to `true` if you wish to replace all whitespace characters with
/// underscores and `false` to leave them as is.
/// Default is `true`, same as on vanilla. However there is one difference:
/// Futility replaces all whitespace characters (including tabulations,
/// non-breaking spaces, etc.) instead of only ' '.
var public config bool replaceSpacesWithUnderscores;
/// Set this to `true` to remove single 'quotation marks' and `false` to
/// leave them. Default is `false`, same as on vanilla.
var public config bool removeSingleQuotationMarks;
/// Set this to `true` to remove dobule 'quotation marks' and `false` to
/// leave them. Default is `true`, same as on vanilla.
var public config bool removeDoubleQuotationMarks;
/// Should we replace empty player nicknames with a random fallback nickname
/// (defined in `fallbackNickname` array)?
var public config bool correctEmptyNicknames;
/// Max allowed nickname length. Negative values disable any length limits.
///
/// NOTE #1: `0` resets all nicknames to be empty and,
/// if `correctEmptyNicknames` is set to `true`, they will be replaced with
/// one of the fallback nicknames (see `correctEmptyNicknames` and
/// `fallbackNickname`).
///
/// NOTE #2: Because of how color swapping in vanilla Killing Floor works,
/// every color swap makes text count as being about 4 characters longer.
/// So if one uses too many colors in the nickname, for drawing functions
/// it will appear to be longer than it actually is and it *will* mess up
/// UI. Unless you are using custom HUD it is recommended to keep this value
/// at default `20` and forbid colored nicknames (by setting
/// `colorPermissions=NCP_ForbidColor`). Or to allow only one color (by setting
/// `colorPermissions=NCP_ForceSingleColor` or
/// `colorPermissions=NCP_ForceTeamColor`) and reducing `maxNicknameLength` to
/// `16` (20 characters - 4 for color swap).
/// If you want to increase the limit above that, you can also do your own
/// research by testing nicknames of various length on screen resolutions you
/// care about.
var public config int maxNicknameLength;
/// Array of fallback nicknames that will be used to replace any empty nicknames
/// if `correctEmptyNicknames` is set to `true`.
var public config array<string> fallbackNickname;
protected function HashTable ToData()
{
local int i;
protected function HashTable ToData() {
local int i;
local ArrayList fallbackNicknamesData;
local HashTable data;
data = __().collections.EmptyHashTable();
data.SetString(P("spacesAction"), string(spacesAction));
data.SetString(P("colorPermissions"), string(colorPermissions));
data.SetBool( P("replaceSpacesWithUnderscores"),
replaceSpacesWithUnderscores);
data.SetBool( P("removeSingleQuotationMarks"),
removeSingleQuotationMarks);
data.SetBool( P("removeDoubleQuotationMarks"),
removeDoubleQuotationMarks);
data.SetBool(P("replaceSpacesWithUnderscores"), replaceSpacesWithUnderscores);
data.SetBool(P("removeSingleQuotationMarks"), removeSingleQuotationMarks);
data.SetBool(P("removeDoubleQuotationMarks"), removeDoubleQuotationMarks);
data.SetBool(P("correctEmptyNicknames"), correctEmptyNicknames);
data.SetInt(P("maxNicknameLength"), maxNicknameLength);
fallbackNicknamesData = __().collections.EmptyArrayList();
for (i = 0; i < fallbackNickname.length; i += 1)
{
fallbackNicknamesData.AddItem(
__().text.FromFormattedString(fallbackNickname[i]));
for (i = 0; i < fallbackNickname.length; i += 1) {
fallbackNicknamesData.AddItem(__().text.FromFormattedString(fallbackNickname[i]));
}
data.SetItem(P("fallbackNickname"), fallbackNicknamesData);
_.memory.Free(fallbackNicknamesData);
return data;
}
protected function FromData(HashTable source)
{
local int i;
local Text nextNickName, storedText;
protected function FromData(HashTable source) {
local int i;
local Text nextNickName, storedText;
local ArrayList fallbackNicknamesData;
if (source == none) {
return;
}
@ -86,25 +132,20 @@ protected function FromData(HashTable source)
storedText = source.GetText(P("colorPermissions"));
colorPermissions = ColorPermissionsFromText(storedText);
_.memory.Free(storedText);
replaceSpacesWithUnderscores =
source.GetBool(P("replaceSpacesWithUnderscores"), true);
removeSingleQuotationMarks =
source.GetBool(P("removeSingleQuotationMarks"), true);
removeDoubleQuotationMarks =
source.GetBool(P("removeDoubleQuotationMarks"), true);
replaceSpacesWithUnderscores = source.GetBool(P("replaceSpacesWithUnderscores"), true);
removeSingleQuotationMarks = source.GetBool(P("removeSingleQuotationMarks"), true);
removeDoubleQuotationMarks = source.GetBool(P("removeDoubleQuotationMarks"), true);
correctEmptyNicknames = source.GetBool(P("correctEmptyNicknames"), true);
maxNicknameLength = source.GetInt(P("correctEmptyNicknames"), 20);
fallbackNicknamesData = source.GetArrayList(P("fallbackNickname"));
if (fallbackNickname.length > 0) {
fallbackNickname.length = 0;
}
for (i = 0; i < fallbackNicknamesData.GetLength(); i += 1)
{
for (i = 0; i < fallbackNicknamesData.GetLength(); i += 1) {
nextNickName = fallbackNicknamesData.GetText(i);
if (nextNickName != none) {
fallbackNickname[i] = nextNickName.ToFormattedString();
}
else {
} else {
fallbackNickname[i] = "";
}
_.memory.Free(nextNickName);
@ -112,8 +153,7 @@ protected function FromData(HashTable source)
_.memory.Free(fallbackNicknamesData);
}
private function NicknameSpacesAction SpaceActionFromText(BaseText action)
{
private function NicknameSpacesAction SpaceActionFromText(BaseText action) {
if (action == none) {
return NSA_DoNothing;
}
@ -129,9 +169,7 @@ private function NicknameSpacesAction SpaceActionFromText(BaseText action)
return NSA_DoNothing;
}
private function NicknameColorPermissions ColorPermissionsFromText(
BaseText permissions)
{
private function NicknameColorPermissions ColorPermissionsFromText(BaseText permissions) {
if (permissions == none) {
return NCP_ForbidColor;
}
@ -150,15 +188,14 @@ private function NicknameColorPermissions ColorPermissionsFromText(
return NCP_ForbidColor;
}
protected function DefaultIt()
{
spacesAction = NSA_DoNothing;
colorPermissions = NCP_ForbidColor;
replaceSpacesWithUnderscores = true;
removeSingleQuotationMarks = false;
removeDoubleQuotationMarks = true;
correctEmptyNicknames = true;
maxNicknameLength = 20;
protected function DefaultIt() {
spacesAction = NSA_DoNothing;
colorPermissions = NCP_ForbidColor;
replaceSpacesWithUnderscores = true;
removeSingleQuotationMarks = false;
removeDoubleQuotationMarks = true;
correctEmptyNicknames = true;
maxNicknameLength = 20;
if (fallbackNickname.length > 0) {
fallbackNickname.length = 0;
}
@ -174,16 +211,15 @@ protected function DefaultIt()
fallbackNickname[9] = "Bug Meat";
}
defaultproperties
{
defaultproperties {
configName = "FutilityNicknames"
spacesAction = NSA_DoNothing
colorPermissions = NCP_ForbidColor
replaceSpacesWithUnderscores = true
removeSingleQuotationMarks = false
removeDoubleQuotationMarks = true
correctEmptyNicknames = true
maxNicknameLength = 20
spacesAction = NSA_DoNothing
colorPermissions = NCP_ForbidColor
replaceSpacesWithUnderscores = true
removeSingleQuotationMarks = false
removeDoubleQuotationMarks = true
correctEmptyNicknames = true
maxNicknameLength = 20
fallbackNickname(0) = "Fresh Meat"
fallbackNickname(1) = "Rotten Meat"
fallbackNickname(2) = "Troll Meat"

368
sources/Features/FutileNickames/FutilityNicknames_Feature.uc

@ -1,9 +1,6 @@
/**
* This feature allows to configure nickname limitations for the server.
* It allows you to customize vanilla limitations for nickname length and
* color with those of your own design. Enabling this feature overwrites
* default behaviour.
* Copyright 2022 Anton Tarasenko
* Config class for storing map lists.
* Copyright 2022-2023 Anton Tarasenko
*------------------------------------------------------------------------------
* This file is part of Acedia.
*
@ -23,224 +20,133 @@
class FutilityNicknames_Feature extends Feature
dependson(FutilityNicknames);
/**
* This feature's functionality is rather simple, but we will still break up
* what its various components are.
*
* Fallback nicknames are picked at random from
* the `fallbackNicknames` array. This is done by copying that array into
* `unusedNicknames` and then picking and removing its random elements each
* time we need a fallback. Once `unusedNicknames` is empty - it is copied from
* `fallbackNicknames` once again, letting already used nicknames to be reused.
* `unusedNicknames` contains same references as `fallbackNicknames`,
* so they need to be separately deallocated and should also be forgotten once
* `fallbackNicknames` are deallocated`.
* This is implemented inside `PickNextFallback()` method.
*
* Nickname changes are applied inside `CensorNickname()` method that uses
* several auxiliary methods to perform different stages of "censoring".
* Censoring is performed:
* 1. On any player's name change
* (using `OnPlayerNameChanging()` signal, connected to
* `HandleNicknameChange()`);
* 2. When new player logins (using `OnNewPlayer()` signal,
* conneted to `CensorOriginalNickname()`) to enforce our own
* handling of player's original nickname;
* 3. When censoring is re-activated.
* In case all censoring options of this feature are disabled
* (checked by `IsAnyCensoringEnabled()`) - we do not attempt to
* catch any events or do anything at all.
* If settings change mid-execution, this feature might need to
* enable or disable censoring on-the-fly. To accomplish that we
* remember current status inside `censoringNicknames` boolean
* variable and enable/disable events if required by settings.
* So whenever we re-activate censoring we also need to update
* ("censor") current players' nicknames - a third occasion to
* call `CensorNickname()`, implemented inside
* `CensorCurrentPlayersNicknames()`.
*/
//! This feature's functionality is rather simple, but we will still break up
//! what its various components are.
//!
//! Fallback nicknames are picked at random from
//! the `fallbackNicknames` array. This is done by copying that array into
//! `unusedNicknames` and then picking and removing its random elements each
//! time we need a fallback. Once `unusedNicknames` is empty - it is copied from
//! `fallbackNicknames` once again, letting already used nicknames to be reused.
//! `unusedNicknames` contains same references as `fallbackNicknames`,
//! so they need to be separately deallocated and should also be forgotten once
//! `fallbackNicknames` are deallocated`.
//! This is implemented inside `PickNextFallback()` method.
//!
//! Nickname changes are applied inside `CensorNickname()` method that uses
//! several auxiliary methods to perform different stages of "censoring".
//! Censoring is performed:
//! 1. On any player's name change
//! (using `OnPlayerNameChanging()` signal, connected to
//! `HandleNicknameChange()`);
//! 2. When new player logins (using `OnNewPlayer()` signal,
//! conneted to `CensorOriginalNickname()`) to enforce our own
//! handling of player's original nickname.
// How to treat whitespace characters inside players' nicknames.
// * `NSA_DoNothing` - does nothing, leaving whitespaces as they are;
// * `NSA_Trim` - removes leading and trailing whitespaces for nicknames;
// * `NSA_Simplify` - removes leading and trailing whitespaces
// for nicknames, also reducing a sequence of whitespaces inside
// nickname to a single space, e.g. "my nick" becomes "my nick".
// Default is `NSA_DoNothing`, same as on vanilla.
var private /*config*/ FutilityNicknames.NicknameSpacesAction spacesAction;
// How to treat colored nicknames.
// * `NCP_ForbidColor` - completely strips down any color from nicknames;
// * `NCP_ForceTeamColor` - forces all nicknames to have player's current
// team's color;
// * `NCP_ForceSingleColor` - allows nickname to be painted with a single
// color (sets nickname's color to that of the first character);
// * `NCP_AllowAnyColor` - allows nickname to be colored in any way player
// wants.
// Default is `NCP_ForbidColor`, same as on vanilla.
var private /*config*/ FutilityNicknames.NicknameColorPermissions colorPermissions;
var private /*config*/ bool replaceSpacesWithUnderscores;
var private /*config*/ bool removeSingleQuotationMarks;
var private /*config*/ bool removeDoubleQuotationMarks;
var private /*config*/ int maxNicknameLength;
var private /*config*/ bool correctEmptyNicknames;
var private /*config*/ array<Text> fallbackNickname;
// Set this to `true` if you wish to replace all whitespace characters with
// underscores and `false` to leave them as is.
// Default is `true`, same as on vanilla. However there is one difference:
// Futility replaces all whitespace characters (including tabulations,
// non-breaking spaces, etc.) instead of only ' '.
var private /*config*/ bool replaceSpacesWithUnderscores;
// Set this to `true` to remove single 'quotation marks' and `false` to
// leave them. Default is `false`, same as on vanilla.
var private /*config*/ bool removeSingleQuotationMarks;
// Set this to `true` to remove dobule 'quotation marks' and `false` to
// leave them. Default is `true`, same as on vanilla.
var private /*config*/ bool removeDoubleQuotationMarks;
// Max allowed nickname length. Negative values disable any length limits.
//
// NOTE #1: `0` resets all nicknames to be empty and,
// if `correctEmptyNicknames` is set to `true`, they will be replaced with
// one of the fallback nicknames
// (see `correctEmptyNicknames` and `fallbackNickname`).
// NOTE #2: Because of how color swapping in vanilla Killing Floor works,
// every color swap makes text count as being about 4 characters longer.
// So if one uses too many colors in the nickname, for drawing functions
// it will appear to be longer than it actually is and it *will* mess up
// UI. Unless you are using custom HUD it is recommended to keep this value
// at default `20` and forbid colored nicknames
// (by setting `colorPermissions=NCP_ForbidColor`). Or to allow only one
// color (by setting `colorPermissions=NCP_ForceSingleColor` or
// `colorPermissions=NCP_ForceTeamColor`) and reducing `maxNicknameLength`
// to `16` (20 characters - 4 for color swap).
// If you want to increase the limit above that, you can also do your
// own research by testing nicknames of various length on
// screen resolutions you care about.
var private /*config*/ int maxNicknameLength;
/// Guaranteed order of applying changes (only chosen ones) is as following:
///
/// 1. Trim/simplify spaces;
/// 2. Remove single and double quotation marks;
/// 3. Enforce max limit of nickname's length;
/// 4. Replace empty nickname with fallback nickname (no further changes
/// will be applied to fallback nickname in that case);
/// 5. Enforce color limitation;
/// 6. Replace remaining whitespaces with underscores.
///
/// NOTE #1: as follows from the instruction described above, no changes will
/// ever be applied to fallback nicknames (unless player's nickname coincides
/// with one by pure accident).
///
/// NOTE #2: whitespaces inside steam nicknames are converted into underscores
/// before they are passed into the game and this is a change Futility
/// cannot currently abort. Therefore all changes relevant to whitespaces inside
/// nicknames will only be applied to in-game changes.
// Should we replace empty player nicknames with a random fallback nickname
// (defined in `fallbackNickname` array)?
var private /*config*/ bool correctEmptyNicknames;
// Array of fallback nicknames that will be used to replace any empty nicknames
// if `correctEmptyNicknames` is set to `true`.
var private /*config*/ array<Text> fallbackNickname;
// Guaranteed order of applying changes (only chosen ones) is as following:
// 1. Trim/simplify spaces;
// 2. Remove single and double quotation marks;
// 3. Enforce max limit of nickname's length;
// 4. Replace empty nickname with fallback nickname (no further changes
// will be applied to fallback nickname in that case);
// 5. Enforce color limitation;
// 6. Replace remaining whitespaces with underscores.
//
// NOTE #1: as follows from the instruction described above, no changes will
// ever be applied to fallback nicknames (unless player's nickname
// coincides with one by pure accident).
// NOTE #2: whitespaces inside steam nicknames are converted into underscores
// before they are passed into the game and this is a change Futility
// cannot currently abort.
// Therefore all changes relevant to whitespaces inside nicknames will only
// be applied to in-game changes.
// Nicknames from `fallbackNickname` that can still be picked in
// the current rotation.
/// Nicknames from `fallbackNickname` that can still be picked in the current
/// rotation.
var private array<Text> unusedNicknames;
// Are we currently censoring nicknames?
// Set to `false` if none of the feature's options require
// any action (censoring) and, therefore, we do not listen to any signals.
var private bool censoringNicknames;
var private const int CODEPOINT_UNDERSCORE;
protected function OnDisabled()
{
_.memory.FreeMany(fallbackNickname);
// Free this `Text` data - it will be refilled with `SwapConfig()`
// if this feature is ever reenabled
if (fallbackNickname.length > 0)
{
_.memory.FreeMany(fallbackNickname);
fallbackNickname.length = 0;
unusedNicknames.length = 0;
protected function OnEnabled() {
if (IsAnyCensoringEnabled()) {
// Do this before adding event handler to avoid censoring nicknames
// second time (censoring nickname will trigger `OnPlayerNameChanging()`
// signal)
CensorCurrentPlayersNicknames();
_.players.OnPlayerNameChanging(self).connect = HandleNicknameChange;
_.players.OnNewPlayer(self).connect = CensorOriginalNickname;
}
if (censoringNicknames)
{
censoringNicknames = false;
}
protected function OnDisabled() {
_.memory.FreeMany(fallbackNickname);
_.memory.FreeMany(unusedNicknames);
fallbackNickname.length = 0;
unusedNicknames.length = 0;
if (IsAnyCensoringEnabled()) {
_.players.OnPlayerNameChanging(self).Disconnect();
_.players.OnNewPlayer(self).Disconnect();
}
}
protected function SwapConfig(FeatureConfig config)
{
local bool configRequiresCensoring;
protected function SwapConfig(FeatureConfig config) {
local FutilityNicknames newConfig;
newConfig = FutilityNicknames(config);
if (newConfig == none) {
return;
}
replaceSpacesWithUnderscores = newConfig.replaceSpacesWithUnderscores;
removeSingleQuotationMarks = newConfig.removeSingleQuotationMarks;
removeDoubleQuotationMarks = newConfig.removeDoubleQuotationMarks;
correctEmptyNicknames = newConfig.correctEmptyNicknames;
spacesAction = newConfig.spacesAction;
colorPermissions = newConfig.colorPermissions;
maxNicknameLength = newConfig.maxNicknameLength;
configRequiresCensoring = IsAnyCensoringEnabled();
// Enable or disable censoring if `IsAnyCensoringEnabled()`'s response
// has changed.
if (!censoringNicknames && configRequiresCensoring)
{
censoringNicknames = true;
// Do this before adding event handler to
// avoid censoring nicknames second time
CensorCurrentPlayersNicknames();
_.players.OnPlayerNameChanging(self).connect = HandleNicknameChange;
_.players.OnNewPlayer(self).connect = CensorOriginalNickname;
}
if (censoringNicknames && !configRequiresCensoring)
{
censoringNicknames = false;
_.players.OnPlayerNameChanging(self).Disconnect();
_.players.OnNewPlayer(self).Disconnect();
}
replaceSpacesWithUnderscores = newConfig.replaceSpacesWithUnderscores;
removeSingleQuotationMarks = newConfig.removeSingleQuotationMarks;
removeDoubleQuotationMarks = newConfig.removeDoubleQuotationMarks;
correctEmptyNicknames = newConfig.correctEmptyNicknames;
spacesAction = newConfig.spacesAction;
colorPermissions = newConfig.colorPermissions;
maxNicknameLength = newConfig.maxNicknameLength;
SwapFallbackNicknames(newConfig);
}
private function Text PickNextFallback()
{
local int pickedIndex;
local Text result;
if (fallbackNickname.length <= 0)
{
// Just in case this feature is really misconfigured
private function SwapFallbackNicknames(FutilityNicknames newConfig) {
local int i;
_.memory.FreeMany(fallbackNickname);
fallbackNickname.length = 0;
for (i = 0; i < newConfig.fallbackNickname.length; i += 1) {
fallbackNickname[i] = _.text.FromFormattedString(newConfig.fallbackNickname[i]);
}
unusedNicknames = fallbackNickname;
}
private function Text PickNextFallback() {
local int pickedIndex;
local Text result;
if (fallbackNickname.length <= 0) {
// Just in case this feature is really misconfigured
return P("Fresh Meat").Copy();
}
if (unusedNicknames.length <= 0) {
unusedNicknames = fallbackNickname;
}
// Pick one nickname at random.
// `pickedIndex` will belong to [0; unusedNicknames.length - 1] segment.
// Pick one nickname at random.
// `pickedIndex` will belong to [0; unusedNicknames.length - 1] segment.
pickedIndex = Rand(unusedNicknames.length);
result = unusedNicknames[pickedIndex].Copy();
unusedNicknames.Remove(pickedIndex, 1);
return result;
}
protected function SwapFallbackNicknames(FutilityNicknames newConfig)
{
local int i;
_.memory.FreeMany(fallbackNickname);
if (fallbackNickname.length > 0) {
fallbackNickname.length = 0;
}
for (i = 0; i < newConfig.fallbackNickname.length; i += 1)
{
fallbackNickname[i] =
_.text.FromFormattedString(newConfig.fallbackNickname[i]);
}
unusedNicknames = fallbackNickname;
}
private function bool IsAnyCensoringEnabled()
{
private function bool IsAnyCensoringEnabled() {
return ( replaceSpacesWithUnderscores
|| removeSingleQuotationMarks
|| removeDoubleQuotationMarks
@ -250,40 +156,38 @@ private function bool IsAnyCensoringEnabled()
|| spacesAction != NSA_DoNothing);
}
// For nickname changes mid-game.
// For nickname changes mid-game.
private function HandleNicknameChange(
EPlayer affectedPlayer,
BaseText oldName,
MutableText newName)
{
EPlayer affectedPlayer,
BaseText oldName,
MutableText newName
) {
CensorNickname(newName, affectedPlayer);
}
// For handling of player's original nicknames.
private function CensorOriginalNickname(EPlayer affectedPlayer)
{
// For handling of player's original nicknames.
private function CensorOriginalNickname(EPlayer affectedPlayer) {
local Text originalNickname;
if (affectedPlayer == none) {
return;
}
originalNickname = affectedPlayer.GetOriginalName();
// This will automatically trigger `OnPlayerNameChanging()` signal and
// our `HandleNicknameChange()` handler.
// This will automatically trigger `OnPlayerNameChanging()` signal and
// our `HandleNicknameChange()` handler.
affectedPlayer.SetName(originalNickname);
_.memory.Free(originalNickname);
}
// For handling nicknames of players after censoring is re-activated by
// config change.
private function CensorCurrentPlayersNicknames()
{
local int i;
local Text nextNickname;
local MutableText alteredNickname;
local array<EPlayer> currentPlayers;
// For handling nicknames of players after censoring is re-activated by
// config change.
private function CensorCurrentPlayersNicknames() {
local int i;
local Text nextNickname;
local MutableText alteredNickname;
local array<EPlayer> currentPlayers;
currentPlayers = _.players.GetAll();
for (i = 0; i < currentPlayers.length; i += 1)
{
for (i = 0; i < currentPlayers.length; i += 1) {
nextNickname = currentPlayers[i].GetName();
alteredNickname = nextNickname.MutableCopy();
CensorNickname(alteredNickname, currentPlayers[i]);
@ -295,10 +199,10 @@ private function CensorCurrentPlayersNicknames()
}
}
private function CensorNickname(MutableText nickname, EPlayer affectedPlayer)
{
local Text fallback;
local BaseText.Formatting newFormatting;
private function CensorNickname(MutableText nickname, EPlayer affectedPlayer) {
local Text fallback;
local BaseText.Formatting newFormatting;
if (nickname == none) return;
if (affectedPlayer == none) return;
@ -314,25 +218,21 @@ private function CensorNickname(MutableText nickname, EPlayer affectedPlayer)
if (maxNicknameLength >= 0) {
nickname.Remove(maxNicknameLength);
}
if (correctEmptyNicknames && nickname.IsEmpty())
{
if (correctEmptyNicknames && nickname.IsEmpty()) {
fallback = PickNextFallback();
nickname.Append(fallback);
_.memory.Free(fallback);
return;
}
if (colorPermissions != NCP_AllowAnyColor)
{
if (colorPermissions != NCP_AllowAnyColor) {
if (colorPermissions == NCP_ForceSingleColor) {
newFormatting = nickname.GetCharacter(0).formatting;
}
else if (colorPermissions == NCP_ForceTeamColor)
{
} else if (colorPermissions == NCP_ForceTeamColor) {
newFormatting.isColored = true;
newFormatting.color = affectedPlayer.GetTeamColor();
}
// `colorPermissions == NCP_ForbidColor`
// `newFormatting` is colorless by default
// `colorPermissions == NCP_ForbidColor`
// `newFormatting` is colorless by default
nickname.ChangeFormatting(newFormatting);
}
if (replaceSpacesWithUnderscores) {
@ -340,21 +240,18 @@ private function CensorNickname(MutableText nickname, EPlayer affectedPlayer)
}
}
// Asusmes `nickname != none`.
private function ReplaceSpaces(MutableText nickname)
{
local int i;
local MutableText nicknameCopy;
local BaseText.Character nextCharacter, underscoreCharacter;
// Asusmes `nickname != none`.
private function ReplaceSpaces(MutableText nickname) {
local int i;
local MutableText nicknameCopy;
local BaseText.Character nextCharacter, underscoreCharacter;
nicknameCopy = nickname.MutableCopy();
nickname.Clear();
underscoreCharacter =
_.text.CharacterFromCodePoint(CODEPOINT_UNDERSCORE);
for (i = 0; i < nicknameCopy.GetLength(); i += 1)
{
underscoreCharacter = _.text.CharacterFromCodePoint(CODEPOINT_UNDERSCORE);
for (i = 0; i < nicknameCopy.GetLength(); i += 1) {
nextCharacter = nicknameCopy.GetCharacter(i);
if (_.text.IsWhitespace(nextCharacter))
{
if (_.text.IsWhitespace(nextCharacter)) {
// Replace character with underscore, leaving the formatting
underscoreCharacter.formatting = nextCharacter.formatting;
nextCharacter = underscoreCharacter;
@ -364,8 +261,7 @@ private function ReplaceSpaces(MutableText nickname)
_.memory.Free(nicknameCopy);
}
defaultproperties
{
defaultproperties {
configClass = class'FutilityNicknames'
CODEPOINT_UNDERSCORE = 95 // '_'
}

12
sources/Futility.uc

@ -21,20 +21,16 @@ class Futility extends FeatureConfig
perobjectconfig
config(Futility);
protected function HashTable ToData()
{
protected function HashTable ToData() {
return _.collections.EmptyHashTable();
}
protected function FromData(HashTable source)
{
protected function FromData(HashTable source) {
}
protected function DefaultIt()
{
protected function DefaultIt() {
}
defaultproperties
{
defaultproperties {
configName = "Futility"
}

6
sources/Futility_Feature.uc

@ -36,7 +36,7 @@ protected function OnEnabled()
return;
}
for (i = 0; i < allCommandClasses.length; i += 1) {
commandsFeature.RegisterCommand(allCommandClasses[i]);
//commandsFeature.RegisterCommand(allCommandClasses[i]);
}
_.environment.OnFeatureEnabled(self).connect = RegisterAllCommandClasses;
}
@ -52,7 +52,7 @@ protected function OnDisabled()
return;
}
for (i = 0; i < allCommandClasses.length; i += 1) {
commandsFeature.RegisterCommand(allCommandClasses[i]);
//ommandsFeature.RegisterCommand(allCommandClasses[i]);
}
}
@ -65,7 +65,7 @@ private final function RegisterAllCommandClasses(Feature enabledFeature)
return;
}
for (i = 0; i < allCommandClasses.length; i += 1) {
commandsFeature.RegisterCommand(allCommandClasses[i]);
//commandsFeature.RegisterCommand(allCommandClasses[i]);
}
}

10
sources/Tools/PendingConfigsTool.uc

@ -189,7 +189,7 @@ public function PendingConfigToolResult EditConfig(
AcediaObject newValue)
{
local HashTable pendingData;
local JSONPointer pointer;
local MutableJsonPointer pointer;
local PendingConfigToolResult result;
if (pathToValue == none) {
@ -200,7 +200,7 @@ public function PendingConfigToolResult EditConfig(
return PCTE_ConfigMissing;
}
// Set new data
pointer = _.json.Pointer(pathToValue);
pointer = _.json.MutablePointer(pathToValue);
result = SetItemByJSON(pendingData, pointer, newValue);
pointer.FreeSelf();
pendingData.FreeSelf();
@ -208,9 +208,9 @@ public function PendingConfigToolResult EditConfig(
}
private function PendingConfigToolResult SetItemByJSON(
HashTable data,
JSONPointer pointer,
AcediaObject jsonValue)
HashTable data,
MutableJsonPointer pointer,
AcediaObject jsonValue)
{
local Text containerIndex;
local AcediaObject container;

Loading…
Cancel
Save