Browse Source

Change Futility classes to support new AcediaCore

develop
Anton Tarasenko 1 year ago
parent
commit
0c928a5a33
  1. 55
      config/FutilityChat.ini
  2. 142
      config/FutilityNicknames.ini
  3. 368
      sources/Commands/ACommandFeature.uc
  4. 150
      sources/Commands/ACommandFeature_Announcer.uc
  5. 74
      sources/Commands/ACommandSpawn.uc
  6. 112
      sources/Features/FutileChat/FutilityChat.uc
  7. 114
      sources/Features/FutileChat/FutilityChat_Feature.uc
  8. 164
      sources/Features/FutileNickames/FutilityNicknames.uc
  9. 368
      sources/Features/FutileNickames/FutilityNicknames_Feature.uc
  10. 12
      sources/Futility.uc

55
config/FutilityChat.ini

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

142
config/FutilityNicknames.ini

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

368
sources/Commands/ACommandFeature.uc

@ -20,28 +20,25 @@
class ACommandFeature extends Command class ACommandFeature extends Command
dependson(PendingConfigsTool); dependson(PendingConfigsTool);
var private class<Feature> selectedFeatureClass; var private class<Feature> selectedFeatureClass;
var private Text selectedConfigName; var private Text selectedFeatureName;
var private Text selectedConfigName;
var private PendingConfigsTool pendingConfigs; var private PendingConfigsTool pendingConfigs;
var private ACommandFeature_Announcer announcer; var private ACommandFeature_Announcer announcer;
protected function Constructor() protected function Constructor() {
{ pendingConfigs = PendingConfigsTool(_.memory.Allocate(class'PendingConfigsTool'));
pendingConfigs =
PendingConfigsTool(_.memory.Allocate(class'PendingConfigsTool'));
super.Constructor(); super.Constructor();
} }
protected function Finalizer() protected function Finalizer() {
{
_.memory.Free(announcer); _.memory.Free(announcer);
_.memory.Free(pendingConfigs); _.memory.Free(pendingConfigs);
super.Finalizer(); super.Finalizer();
} }
protected function BuildData(CommandDataBuilder builder) protected function BuildData(CommandDataBuilder builder) {
{
builder.Group(P("admin")); builder.Group(P("admin"));
builder.Summary(P("Managing features.")); builder.Summary(P("Managing features."));
builder.Describe(P("Command for managing features and their configs.")); builder.Describe(P("Command for managing features and their configs."));
@ -108,126 +105,109 @@ protected function Executed(
local bool saveFlag, allFlag; local bool saveFlag, allFlag;
announcer.Setup(none, instigator, othersConsole); announcer.Setup(none, instigator, othersConsole);
saveFlag = arguments.options.HasKey(P("save")); saveFlag = arguments.options.HasKey(P("save"));
allFlag = arguments.options.HasKey(P("all")); allFlag = arguments.options.HasKey(P("all"));
SelectFeatureAndConfig(arguments); SelectFeatureAndConfig(arguments);
if (arguments.subCommandName.IsEmpty()) { if (arguments.subCommandName.IsEmpty()) {
ShowAllFeatures(); ShowAllFeatures();
} } else if (arguments.subCommandName.Compare(P("enable"))) {
else if (arguments.subCommandName.Compare(P("enable"))) {
EnableFeature(); EnableFeature();
} } else if (arguments.subCommandName.Compare(P("disable"))) {
else if (arguments.subCommandName.Compare(P("disable"))) {
DisableFeature(); DisableFeature();
} } else if (arguments.subCommandName.Compare(P("showconf"))) {
else if (arguments.subCommandName.Compare(P("showconf"))) {
ShowSelectedConfigs(allFlag); ShowSelectedConfigs(allFlag);
} } else if (arguments.subCommandName.Compare(P("editconf"))) {
else if (arguments.subCommandName.Compare(P("editconf")))
{
EditFeatureConfig( EditFeatureConfig(
arguments.parameters.GetText(P("variable_path")), arguments.parameters.GetText(P("variable_path")),
arguments.parameters.GetItem(P("value")), arguments.parameters.GetItem(P("value")),
saveFlag); saveFlag);
} } else if (arguments.subCommandName.Compare(P("saveconf"))) {
else if (arguments.subCommandName.Compare(P("saveconf"))) {
SaveFeatureConfig(); SaveFeatureConfig();
} } else if (arguments.subCommandName.Compare(P("newconf"))) {
else if (arguments.subCommandName.Compare(P("newconf"))) {
NewFeatureConfig(); NewFeatureConfig();
} } else if (arguments.subCommandName.Compare(P("removeconf"))) {
else if (arguments.subCommandName.Compare(P("removeconf"))) {
RemoveFeatureConfig(); RemoveFeatureConfig();
} } else if (arguments.subCommandName.Compare(P("autoconf"))) {
else if (arguments.subCommandName.Compare(P("autoconf"))) {
SetAutoFeatureConfig(); SetAutoFeatureConfig();
} }
_.memory.Free(selectedConfigName); _.memory.Free2(selectedConfigName, selectedFeatureName);
selectedConfigName = none; selectedConfigName = none;
selectedFeatureName = none;
} }
protected function SelectFeatureAndConfig(CallData arguments) protected function SelectFeatureAndConfig(CallData arguments) {
{
local Text featureClassName, userGivenConfigName; 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); selectedFeatureClass = LoadFeatureClass(featureClassName);
if (selectedFeatureClass == none && !arguments.subCommandName.IsEmpty()) { if (selectedFeatureClass == none && !arguments.subCommandName.IsEmpty()) {
_.memory.Free(selectedFeatureName);
selectedFeatureName = none;
return; return;
} }
_.memory.Free(featureClassName); _.memory.Free(featureClassName);
userGivenConfigName = arguments.parameters.GetText(P("config")); userGivenConfigName = arguments.parameters.GetText(P("config"));
if (userGivenConfigName != none) if (userGivenConfigName != none) {
{
selectedConfigName = userGivenConfigName.LowerCopy(); selectedConfigName = userGivenConfigName.LowerCopy();
userGivenConfigName.FreeSelf(); userGivenConfigName.FreeSelf();
} }
pendingConfigs.SelectConfig(selectedFeatureClass, selectedConfigName); pendingConfigs.SelectConfig(selectedFeatureClass, selectedConfigName);
} }
protected function EnableFeature() protected function EnableFeature() {
{ local bool wasEnabled;
local bool wasEnabled; local Text oldConfig, newConfig;
local Text oldConfig, newConfig; local Feature instance;
local Feature instance;
wasEnabled = selectedFeatureClass.static.IsEnabled(); wasEnabled = selectedFeatureClass.static.IsEnabled();
oldConfig = selectedFeatureClass.static.GetCurrentConfig(); oldConfig = selectedFeatureClass.static.GetCurrentConfig();
newConfig = PickConfigBasedOnParameter(); newConfig = PickConfigBasedOnParameter();
// Already enabled with the same config! // Already enabled with the same config!
if (oldConfig != none && oldConfig.Compare(newConfig, SCASE_INSENSITIVE)) if (oldConfig != none && oldConfig.Compare(newConfig, SCASE_INSENSITIVE)) {
{ announcer.AnnounceFailedAlreadyEnabled(selectedFeatureName, newConfig);
announcer.AnnounceFailedAlreadyEnabled(selectedFeatureClass, newConfig);
_.memory.Free(newConfig); _.memory.Free(newConfig);
_.memory.Free(oldConfig); _.memory.Free(oldConfig);
return; return;
} }
// Try enabling and report the result // Try enabling and report the result
instance = selectedFeatureClass.static.EnableMe(newConfig); instance = selectedFeatureClass.static.EnableMe(newConfig);
if (instance == none) if (instance == none) {
{
announcer.AnnounceFailedCannotEnableFeature( announcer.AnnounceFailedCannotEnableFeature(
selectedFeatureClass, selectedFeatureName,
newConfig); newConfig);
} } else if (wasEnabled) {
else if (wasEnabled)
{
announcer.AnnounceSwappedConfig( announcer.AnnounceSwappedConfig(
selectedFeatureClass, selectedFeatureName,
oldConfig, oldConfig,
newConfig); newConfig);
} } else {
else { announcer.AnnounceEnabledFeature(selectedFeatureName, newConfig);
announcer.AnnounceEnabledFeature(selectedFeatureClass, newConfig);
} }
_.memory.Free(newConfig); _.memory.Free(newConfig);
_.memory.Free(oldConfig); _.memory.Free(oldConfig);
} }
protected function DisableFeature() protected function DisableFeature() {
{ if (!selectedFeatureClass.static.IsEnabled()) {
if (!selectedFeatureClass.static.IsEnabled()) announcer.AnnounceFailedAlreadyDisabled(selectedFeatureName);
{
announcer.AnnounceFailedAlreadyDisabled(selectedFeatureClass);
return; return;
} }
selectedFeatureClass.static.DisableMe(); selectedFeatureClass.static.DisableMe();
// It is possible that this command itself is destroyed after above command // It is possible that this command itself is destroyed after above command
// so do the check just in case // so do the check just in case
if (IsAllocated()) { if (IsAllocated()) {
announcer.AnnounceDisabledFeature(selectedFeatureClass); announcer.AnnounceDisabledFeature(selectedFeatureName);
} }
} }
protected function ShowSelectedConfigs(bool showAllFeatures) protected function ShowSelectedConfigs(bool showAllFeatures) {
{ local int i;
local int i; local array<Text> availableConfigs;
local array<Text> availableConfigs; local MutableText configList;
local MutableText configList; local class<FeatureConfig> configClass;
local class<FeatureConfig> configClass;
if (showAllFeatures) if (showAllFeatures) {
{
configClass = selectedFeatureClass.default.configClass; configClass = selectedFeatureClass.default.configClass;
if (configClass != none) { if (configClass != none) {
availableConfigs = configClass.static.AvailableConfigs(); availableConfigs = configClass.static.AvailableConfigs();
@ -238,8 +218,7 @@ protected function ShowSelectedConfigs(bool showAllFeatures)
_.memory.FreeMany(availableConfigs); _.memory.FreeMany(availableConfigs);
return; return;
} }
if (selectedConfigName != none) if (selectedConfigName != none) {
{
ShowFeatureConfig(selectedConfigName); ShowFeatureConfig(selectedConfigName);
return; return;
} }
@ -251,35 +230,32 @@ protected function ShowSelectedConfigs(bool showAllFeatures)
_.memory.Free(configList); _.memory.Free(configList);
} }
protected function ShowFeatureConfig(BaseText configName) protected function ShowFeatureConfig(BaseText configName) {
{ local MutableText dataAsJSON;
local MutableText dataAsJSON; local HashTable currentData, pendingData;
local HashTable currentData, pendingData;
if (configName == none) { if (configName == none) {
return; return;
} }
currentData = GetCurrentConfigData(configName); currentData = GetCurrentConfigData(configName);
if (currentData == none) if (currentData == none) {
{
announcer.AnnounceFailedNoDataForConfig( announcer.AnnounceFailedNoDataForConfig(
selectedFeatureClass, selectedFeatureName,
configName); configName);
return; return;
} }
// Display current data // Display current data
dataAsJSON = _.json.PrettyPrint(currentData); dataAsJSON = _.json.PrettyPrint(currentData);
announcer.AnnounceCurrentConfig(selectedFeatureClass, configName); announcer.AnnounceCurrentConfig(selectedFeatureName, configName);
callerConsole.Flush().WriteLine(dataAsJSON); callerConsole.Flush().WriteLine(dataAsJSON);
_.memory.Free(dataAsJSON); _.memory.Free(dataAsJSON);
// Display pending data // Display pending data
pendingConfigs.SelectConfig(selectedFeatureClass, configName); pendingConfigs.SelectConfig(selectedFeatureClass, configName);
pendingData = pendingConfigs.GetPendingConfigData(); pendingData = pendingConfigs.GetPendingConfigData();
if (pendingData != none) if (pendingData != none) {
{
dataAsJSON = _.json.PrettyPrint(pendingData); dataAsJSON = _.json.PrettyPrint(pendingData);
announcer.AnnouncePendingConfig( announcer.AnnouncePendingConfig(
selectedFeatureClass, selectedFeatureName,
configName); configName);
callerConsole.Flush().WriteLine(dataAsJSON); callerConsole.Flush().WriteLine(dataAsJSON);
_.memory.Free(dataAsJSON); _.memory.Free(dataAsJSON);
@ -288,20 +264,17 @@ protected function ShowFeatureConfig(BaseText configName)
_.memory.Free(currentData); _.memory.Free(currentData);
} }
protected function Text PickConfigBasedOnParameter() protected function Text PickConfigBasedOnParameter() {
{ local Text resolvedConfig;
local Text resolvedConfig; local class<FeatureConfig> configClass;
local class<FeatureConfig> configClass;
configClass = selectedFeatureClass.default.configClass; configClass = selectedFeatureClass.default.configClass;
if (configClass == none) if (configClass == none) {
{ announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
return none; return none;
} }
// If config was specified - simply check that it exists // If config was specified - simply check that it exists
if (selectedConfigName != none) if (selectedConfigName != none) {
{
if (configClass.static.Exists(selectedConfigName)) { if (configClass.static.Exists(selectedConfigName)) {
return selectedConfigName.Copy(); return selectedConfigName.Copy();
} }
@ -311,13 +284,12 @@ protected function Text PickConfigBasedOnParameter()
// If it wasn't specified - try auto config instead // If it wasn't specified - try auto config instead
resolvedConfig = configClass.static.GetAutoEnabledConfig(); resolvedConfig = configClass.static.GetAutoEnabledConfig();
if (resolvedConfig == none) { if (resolvedConfig == none) {
announcer.AnnounceFailedNoConfigProvided(selectedFeatureClass); announcer.AnnounceFailedNoConfigProvided(selectedFeatureName);
} }
return resolvedConfig; return resolvedConfig;
} }
protected function class<Feature> LoadFeatureClass(BaseText featureClassName) protected function class<Feature> LoadFeatureClass(BaseText featureClassName) {
{
local class<Feature> featureClass; local class<Feature> featureClass;
if (featureClassName == none) { if (featureClassName == none) {
@ -330,10 +302,10 @@ protected function class<Feature> LoadFeatureClass(BaseText featureClassName)
return featureClass; return featureClass;
} }
protected function ShowAllFeatures() protected function ShowAllFeatures() {
{ local int i;
local int i; local array< class<Feature> > availableFeatures;
local array< class<Feature> > availableFeatures;
availableFeatures = _.environment.GetAvailableFeatures(); availableFeatures = _.environment.GetAvailableFeatures();
for (i = 0; i < availableFeatures.length; i ++) { for (i = 0; i < availableFeatures.length; i ++) {
ShowFeature(availableFeatures[i]); ShowFeature(availableFeatures[i]);
@ -366,45 +338,47 @@ protected function ShowFeature(class<Feature> featureClass)
_.memory.Free(configList); _.memory.Free(configList);
} }
protected function MutableText PrintConfigList(class<Feature> featureClass) protected function MutableText PrintConfigList(class<Feature> featureClass) {
{ local int i;
local int i; local Text autoConfig, enabledConfig;
local Text autoConfig; local ListBuilder configList;
local ListBuilder configList; local MutableText result, nextConfig;
local MutableText result, nextConfig; local array<Text> availableConfigs;
local array<Text> availableConfigs; local class<FeatureConfig> configClass;
local class<FeatureConfig> configClass;
if (featureClass == none) return none; if (featureClass == none) return none;
configClass = featureClass.default.configClass; configClass = featureClass.default.configClass;
if (configClass == none) return none; if (configClass == none) return none;
availableConfigs = configClass.static.AvailableConfigs(); availableConfigs = configClass.static.AvailableConfigs();
autoConfig = configClass.static.GetAutoEnabledConfig(); enabledConfig = featureClass.static.GetCurrentConfig();
configList = ListBuilder(_.memory.Allocate(class'ListBuilder')); autoConfig = configClass.static.GetAutoEnabledConfig();
for (i = 0; i < availableConfigs.length; i += 1) configList = ListBuilder(_.memory.Allocate(class'ListBuilder'));
{ for (i = 0; i < availableConfigs.length; i += 1) {
nextConfig = availableConfigs[i].MutableCopy(); nextConfig = availableConfigs[i].MutableCopy();
if (enabledConfig != none && enabledConfig.Compare(nextConfig, SCASE_INSENSITIVE)) {
nextConfig.ChangeDefaultColor(_.color.TextPositive);
}
if (pendingConfigs.HasPendingConfigFor(featureClass, nextConfig)) { if (pendingConfigs.HasPendingConfigFor(featureClass, nextConfig)) {
nextConfig.Append(F("{$TextEmphasis *}")); nextConfig.Append(F("{$TextEmphasis *}"));
} }
configList.Item(nextConfig); configList.Item(nextConfig);
_.memory.Free(nextConfig); _.memory.Free(nextConfig);
if ( autoConfig != none if (autoConfig != none && autoConfig.Compare(availableConfigs[i], SCASE_INSENSITIVE)) {
&& autoConfig.Compare(availableConfigs[i], SCASE_INSENSITIVE)) if (autoConfig.Compare(enabledConfig, SCASE_INSENSITIVE)) {
{ configList.Comment(F("{$TextPositive auto enabled}"));
configList.Comment(F("{$TextPositive auto enabled}")); } else {
configList.Comment(F("{$TextNegative auto enabled}"));
}
} }
} }
result = configList.GetMutable(); result = configList.GetMutable();
_.memory.Free(configList); _.memory.Free3(configList, autoConfig, enabledConfig);
_.memory.Free(autoConfig);
_.memory.FreeMany(availableConfigs); _.memory.FreeMany(availableConfigs);
return result; return result;
} }
protected function MutableText PrettyPrintValueAt(BaseText pathToValue) protected function MutableText PrettyPrintValueAt(BaseText pathToValue) {
{
local MutableText printedValue; local MutableText printedValue;
local AcediaObject value; local AcediaObject value;
local HashTable relevantData; local HashTable relevantData;
@ -416,8 +390,7 @@ protected function MutableText PrettyPrintValueAt(BaseText pathToValue)
if (relevantData != none) { if (relevantData != none) {
value = relevantData.GetItemBy(pathToValue); value = relevantData.GetItemBy(pathToValue);
} }
if (value != none) if (value != none) {
{
printedValue = _.json.PrettyPrint(value); printedValue = _.json.PrettyPrint(value);
_.memory.Free(value); _.memory.Free(value);
} }
@ -425,11 +398,7 @@ protected function MutableText PrettyPrintValueAt(BaseText pathToValue)
return printedValue; return printedValue;
} }
protected function EditFeatureConfig( protected function EditFeatureConfig(BaseText pathToValue, AcediaObject newValue, bool saveConfig) {
BaseText pathToValue,
AcediaObject newValue,
bool saveConfig)
{
local MutableText printedOldValue; local MutableText printedOldValue;
local MutableText printedNewValue; local MutableText printedNewValue;
local PendingConfigsTool.PendingConfigToolResult error; local PendingConfigsTool.PendingConfigToolResult error;
@ -447,26 +416,20 @@ protected function EditFeatureConfig(
} }
else if (error == PCTE_ExpectedObject) { else if (error == PCTE_ExpectedObject) {
announcer.AnnounceFailedExpectedObject(); announcer.AnnounceFailedExpectedObject();
} } else if (error == PCTE_BadPointer) {
else if (error == PCTE_BadPointer)
{
announcer.AnnounceFailedBadPointer( announcer.AnnounceFailedBadPointer(
selectedFeatureClass, selectedFeatureName,
selectedConfigName, selectedConfigName,
pathToValue); pathToValue);
} } else if (printedOldValue == none) {
else if (printedOldValue == none)
{
announcer.AnnounceConfigNewValue( announcer.AnnounceConfigNewValue(
selectedFeatureClass, selectedFeatureName,
selectedConfigName, selectedConfigName,
pathToValue, pathToValue,
printedNewValue); printedNewValue);
} } else {
else
{
announcer.AnnounceConfigEdited( announcer.AnnounceConfigEdited(
selectedFeatureClass, selectedFeatureName,
selectedConfigName, selectedConfigName,
pathToValue, pathToValue,
printedOldValue, printedOldValue,
@ -481,21 +444,18 @@ protected function EditFeatureConfig(
_.memory.Free(newValue); _.memory.Free(newValue);
} }
protected function SaveFeatureConfig() protected function SaveFeatureConfig() {
{ local BaseText enabledConfigName;
local BaseText enabledConfigName; local HashTable pendingData;
local HashTable pendingData; local class<FeatureConfig> configClass;
local class<FeatureConfig> configClass;
configClass = selectedFeatureClass.default.configClass; configClass = selectedFeatureClass.default.configClass;
if (configClass == none) if (configClass == none) {
{ announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
return; return;
} }
pendingData = pendingConfigs.GetPendingConfigData(); pendingData = pendingConfigs.GetPendingConfigData();
if (pendingData == none) if (pendingData == none) {
{
announcer.AnnounceFailedPendingConfigMissing(selectedConfigName); announcer.AnnounceFailedPendingConfigMissing(selectedConfigName);
return; return;
} }
@ -504,13 +464,11 @@ protected function SaveFeatureConfig()
configClass.static.SaveData(selectedConfigName, pendingData); configClass.static.SaveData(selectedConfigName, pendingData);
// Re-apply config if it is active? // Re-apply config if it is active?
enabledConfigName = selectedFeatureClass.static.GetCurrentConfig(); enabledConfigName = selectedFeatureClass.static.GetCurrentConfig();
if (selectedConfigName.Compare(enabledConfigName, SCASE_INSENSITIVE)) if (selectedConfigName.Compare(enabledConfigName, SCASE_INSENSITIVE)) {
{
selectedFeatureClass.static.EnableMe(selectedConfigName); selectedFeatureClass.static.EnableMe(selectedConfigName);
announcer.AnnouncePublicPendingConfigSaved(selectedFeatureClass); announcer.AnnouncePublicPendingConfigSaved(selectedFeatureName);
} } else {
else { announcer.AnnouncePrivatePendingConfigSaved(selectedFeatureName, selectedConfigName);
announcer.AnnouncePrivatePendingConfigSaved(selectedFeatureClass);
} }
_.memory.Free(enabledConfigName); _.memory.Free(enabledConfigName);
pendingData.FreeSelf(); pendingData.FreeSelf();
@ -518,125 +476,101 @@ protected function SaveFeatureConfig()
return; return;
} }
protected function NewFeatureConfig() protected function NewFeatureConfig() {
{ local BaseText enabledConfigName;
local BaseText enabledConfigName; local class<FeatureConfig> configClass;
local class<FeatureConfig> configClass;
configClass = selectedFeatureClass.default.configClass; configClass = selectedFeatureClass.default.configClass;
if (configClass == none) if (configClass == none) {
{ announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
return; return;
} }
if (configClass.static.Exists(selectedConfigName)) if (configClass.static.Exists(selectedConfigName)) {
{ announcer.AnnounceFailedConfigAlreadyExists(selectedFeatureName, selectedConfigName);
announcer.AnnounceFailedConfigAlreadyExists(
selectedFeatureClass,
selectedConfigName);
return; return;
} }
if (!configClass.static.NewConfig(selectedConfigName)) if (!configClass.static.NewConfig(selectedConfigName)) {
{
announcer.AnnounceFailedBadConfigName(selectedConfigName); announcer.AnnounceFailedBadConfigName(selectedConfigName);
return; return;
} }
enabledConfigName = selectedFeatureClass.static.GetCurrentConfig(); enabledConfigName = selectedFeatureClass.static.GetCurrentConfig();
if (selectedConfigName.Compare(enabledConfigName, SCASE_INSENSITIVE)) if (selectedConfigName.Compare(enabledConfigName, SCASE_INSENSITIVE)) {
{
selectedFeatureClass.static.EnableMe(selectedConfigName); selectedFeatureClass.static.EnableMe(selectedConfigName);
announcer.AnnouncePublicPendingConfigSaved(selectedFeatureClass); announcer.AnnouncePublicPendingConfigSaved(selectedFeatureName);
} }
_.memory.Free(enabledConfigName); _.memory.Free(enabledConfigName);
announcer.AnnounceConfigCreated(selectedFeatureClass, selectedConfigName); announcer.AnnounceConfigCreated(selectedFeatureName, selectedConfigName);
} }
protected function RemoveFeatureConfig() protected function RemoveFeatureConfig() {
{
local class<FeatureConfig> configClass; local class<FeatureConfig> configClass;
configClass = selectedFeatureClass.default.configClass; configClass = selectedFeatureClass.default.configClass;
if (configClass == none) if (configClass == none) {
{ announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
return; return;
} }
if (!configClass.static.Exists(selectedConfigName)) if (!configClass.static.Exists(selectedConfigName)) {
{
announcer.AnnounceFailedConfigDoesNotExist( announcer.AnnounceFailedConfigDoesNotExist(
selectedFeatureClass, selectedFeatureName,
selectedConfigName); selectedConfigName);
return; return;
} }
pendingConfigs.RemoveConfig(); pendingConfigs.RemoveConfig();
configClass.static.DeleteConfig(selectedConfigName); configClass.static.DeleteConfig(selectedConfigName);
announcer.AnnounceConfigRemoved(selectedFeatureClass, selectedConfigName); announcer.AnnounceConfigRemoved(selectedFeatureName, selectedConfigName);
} }
protected function SetAutoFeatureConfig() protected function SetAutoFeatureConfig() {
{ local Text currentAutoEnabledConfig;
local Text currentAutoEnabledConfig; local class<FeatureConfig> configClass;
local class<FeatureConfig> configClass;
configClass = selectedFeatureClass.default.configClass; configClass = selectedFeatureClass.default.configClass;
if (configClass == none) if (configClass == none) {
{ announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
return; return;
} }
if ( selectedConfigName != none if (selectedConfigName != none && !configClass.static.Exists(selectedConfigName)) {
&& !configClass.static.Exists(selectedConfigName)) announcer.AnnounceFailedConfigDoesNotExist(selectedFeatureName, selectedConfigName);
{
announcer.AnnounceFailedConfigDoesNotExist(
selectedFeatureClass,
selectedConfigName);
return; return;
} }
currentAutoEnabledConfig = configClass.static.GetAutoEnabledConfig(); currentAutoEnabledConfig = configClass.static.GetAutoEnabledConfig();
if (selectedConfigName == none && currentAutoEnabledConfig == none) { if (selectedConfigName == none && currentAutoEnabledConfig == none) {
announcer.AnnounceFailedAlreadyNoAutoEnabled(selectedFeatureClass); announcer.AnnounceFailedAlreadyNoAutoEnabled(selectedFeatureName);
} }
else if (selectedConfigName != none && else if (selectedConfigName != none &&
selectedConfigName.Compare(currentAutoEnabledConfig, SCASE_INSENSITIVE)) selectedConfigName.Compare(currentAutoEnabledConfig, SCASE_INSENSITIVE)) {
{
announcer.AnnounceFailedAlreadySameAutoEnabled( announcer.AnnounceFailedAlreadySameAutoEnabled(
selectedFeatureClass, selectedFeatureName,
selectedConfigName); selectedConfigName);
} } else {
else
{
configClass.static.SetAutoEnabledConfig(selectedConfigName); configClass.static.SetAutoEnabledConfig(selectedConfigName);
if (selectedConfigName != none) if (selectedConfigName != none) {
{
announcer.AnnounceAutoEnabledConfig( announcer.AnnounceAutoEnabledConfig(
selectedFeatureClass, selectedFeatureName,
selectedConfigName); selectedConfigName);
} } else {
else { announcer.AnnounceRemovedAutoEnabledConfig(selectedFeatureName);
announcer.AnnounceRemovedAutoEnabledConfig(selectedFeatureClass);
} }
} }
_.memory.Free(currentAutoEnabledConfig); _.memory.Free(currentAutoEnabledConfig);
} }
private function HashTable GetCurrentConfigData(BaseText configName) private function HashTable GetCurrentConfigData(BaseText configName) {
{
local class<FeatureConfig> configClass; local class<FeatureConfig> configClass;
if (configName == none) { if (configName == none) {
return none; return none;
} }
configClass = selectedFeatureClass.default.configClass; configClass = selectedFeatureClass.default.configClass;
if (configClass == none) if (configClass == none) {
{ announcer.AnnounceFailedNoConfigClass(selectedFeatureName);
announcer.AnnounceFailedNoConfigClass(selectedFeatureClass);
return none; return none;
} }
return configClass.static.LoadData(configName); return configClass.static.LoadData(configName);
} }
private function HashTable GetCurrentSelectedConfigData() private function HashTable GetCurrentSelectedConfigData() {
{
return GetCurrentConfigData(selectedConfigName); return GetCurrentConfigData(selectedConfigName);
} }

150
sources/Commands/ACommandFeature_Announcer.uc

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

74
sources/Commands/ACommandSpawn.uc

@ -47,57 +47,59 @@ protected function BuildData(CommandDataBuilder builder)
} }
protected function Executed( protected function Executed(
CallData arguments, CallData arguments,
EPlayer instigator, EPlayer instigator,
CommandPermissions permissions CommandPermissions permissions
) { ) {
local Text template; local HashTable value;
local Vector spawnLocation; local Vector spawnLocation;
announcer.Setup(none, instigator, othersConsole); announcer.Setup(none, instigator, othersConsole);
template = arguments.parameters.GetText(P("template")); value = arguments.parameters.GetHashTable(P("template"));
if (arguments.subCommandName.IsEmpty()) { if (arguments.subCommandName.IsEmpty()) {
SpawnInInstigatorSight(instigator, template); SpawnInInstigatorSight(instigator, value);
} } else if (arguments.subCommandName.Compare(P("at"), SCASE_INSENSITIVE)) {
else if (arguments.subCommandName.Compare(P("at"), SCASE_INSENSITIVE))
{
spawnLocation.x = arguments.parameters.GetFloat(P("x")); spawnLocation.x = arguments.parameters.GetFloat(P("x"));
spawnLocation.y = arguments.parameters.GetFloat(P("y")); spawnLocation.y = arguments.parameters.GetFloat(P("y"));
spawnLocation.z = arguments.parameters.GetFloat(P("z")); 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( private final function SpawnAt(
EPlayer instigator, EPlayer instigator,
BaseText template, HashTable value,
Vector spawnLocation) Vector spawnLocation
{ ) {
local Text humanReadable, template;
local EPlaceable result; local EPlaceable result;
humanReadable = value.GetText(P("alias"));
template = value.GetText(P("value"));
result = _server.kf.world.Spawn(template, spawnLocation); result = _server.kf.world.Spawn(template, spawnLocation);
if (result != none) { if (result != none) {
announcer.AnnounceSpawned(template); announcer.AnnounceSpawned(humanReadable);
} } else {
else { announcer.AnnounceSpawningFailed(humanReadable);
announcer.AnnounceSpawningFailed(template);
} }
_.memory.Free(result); _.memory.Free2(humanReadable, result);
} }
private final function SpawnInInstigatorSight( private final function SpawnInInstigatorSight(
EPlayer instigator, EPlayer instigator,
BaseText template) HashTable value
{ ) {
local EPlaceable result; local EPlaceable result;
local Vector spawnLocation; local Vector spawnLocation;
local TracingIterator iter; local TracingIterator iter;
local Text humanReadable, template;
humanReadable = value.GetText(P("alias"));
template = value.GetText(P("value"));
iter = _server.kf.world.TracePlayerSight(instigator); iter = _server.kf.world.TracePlayerSight(instigator);
iter.LeaveOnlyVisible(); iter.LeaveOnlyVisible();
if (iter.HasFinished()) if (iter.HasFinished()) {
{
announcer.AnnounceFailedTrace(); announcer.AnnounceFailedTrace();
return; return;
} }
@ -105,20 +107,16 @@ private final function SpawnInInstigatorSight(
result = _server.kf.world.Spawn(template, spawnLocation); result = _server.kf.world.Spawn(template, spawnLocation);
// Shift position back a little and try again; // Shift position back a little and try again;
// this should fix a ton of spawning failures // this should fix a ton of spawning failures
if (result == none) if (result == none) {
{ spawnLocation = spawnLocation + Normal(iter.GetTracingStart() - spawnLocation) * 100;
spawnLocation = spawnLocation +
Normal(iter.GetTracingStart() - spawnLocation) * 100;
result = _server.kf.world.Spawn(template, spawnLocation); result = _server.kf.world.Spawn(template, spawnLocation);
} }
if (result != none) { if (result != none) {
announcer.AnnounceSpawned(template); announcer.AnnounceSpawned(humanReadable);
} } else {
else { announcer.AnnounceSpawningFailed(humanReadable);
announcer.AnnounceSpawningFailed(template);
} }
_.memory.Free(result); _.memory.Free4(result, iter, humanReadable, result);
_.memory.Free(iter);
} }
defaultproperties { defaultproperties {

112
sources/Features/FutileChat/FutilityChat.uc

@ -1,6 +1,6 @@
/** /**
* Config object for `FutilityChat_Feature`. * Config class for storing map lists.
* Copyright 2022 Anton Tarasenko * Copyright 2022-2023 Anton Tarasenko
*------------------------------------------------------------------------------ *------------------------------------------------------------------------------
* This file is part of Acedia. * This file is part of Acedia.
* *
@ -21,8 +21,7 @@ class FutilityChat extends FeatureConfig
perobjectconfig perobjectconfig
config(FutilityChat); config(FutilityChat);
enum ChatColorSetting enum ChatColorSetting {
{
CCS_DoNothing, CCS_DoNothing,
CCS_TeamColorForced, CCS_TeamColorForced,
CCS_ConfigColorForced, CCS_ConfigColorForced,
@ -30,16 +29,45 @@ enum ChatColorSetting
CCS_ConfigColorCustom CCS_ConfigColorCustom
}; };
var public config ChatColorSetting colorSetting; /// How to color text chat messages?
var public config Color configuredColor; ///
var public config float teamColorModifier; /// 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 HashTable data;
local Text colorAsText; local Text colorAsText;
data = __().collections.EmptyHashTable(); data = __().collections.EmptyHashTable();
data.SetString(P("colorSetting"), string(colorSetting)); data.SetString(P("colorSetting"), StringFromColorSetting(colorSetting));
colorAsText = _.color.ToText(configuredColor); colorAsText = _.color.ToText(configuredColor);
data.SetItem(P("configuredColor"), colorAsText); data.SetItem(P("configuredColor"), colorAsText);
_.memory.Free(colorAsText); _.memory.Free(colorAsText);
@ -47,24 +75,21 @@ protected function HashTable ToData()
return data; return data;
} }
protected function FromData(HashTable source) protected function FromData(HashTable source) {
{
local Text storedText; 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( private function ChatColorSetting ColorSettingFromText(BaseText permissions) {
BaseText permissions)
{
if (permissions == none) { if (permissions == none) {
return CCS_DoNothing; return CCS_DoNothing;
} }
@ -83,17 +108,34 @@ private function ChatColorSetting ColorSettingFromText(
return CCS_DoNothing; return CCS_DoNothing;
} }
protected function DefaultIt() private function string StringFromColorSetting(ChatColorSetting permissions) {
{ if (permissions == CCS_DoNothing) {
colorSetting = CCS_DoNothing; return "DoNothing";
configuredColor = _.color.RGB(255, 255, 255); }
teamColorModifier = 0.6; 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" configName = "FutilityChat"
colorSetting = CCS_DoNothing colorSetting = CCS_DoNothing
configuredColor = (R=255,G=255,B=255,A=255) configuredColor = (R=255,G=255,B=255,A=255)
teamColorModifier = 0.6 teamColorModifier = 0.6
} }

114
sources/Features/FutileChat/FutilityChat_Feature.uc

@ -1,6 +1,6 @@
/** /**
* This feature allows to configure color of text chat messages. * Config class for storing map lists.
* Copyright 2022 Anton Tarasenko * Copyright 2022-2023 Anton Tarasenko
*------------------------------------------------------------------------------ *------------------------------------------------------------------------------
* This file is part of Acedia. * This file is part of Acedia.
* *
@ -20,123 +20,82 @@
class FutilityChat_Feature extends Feature class FutilityChat_Feature extends Feature
dependson(FutilityChat); dependson(FutilityChat);
// How to color text chat messages? var private /*config*/ FutilityChat.ChatColorSetting colorSetting;
// 1. `CCS_DoNothing` - do not change color in any way; var private /*config*/ Color configuredColor;
// 2. `CCS_TeamColorForced` - force players' team colors for var private /*config*/ float teamColorModifier;
// 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;
// Keep track of whether we connected to necessary signals, so that we can /// Keep track of whether we connected to necessary signals, so that we can
// connect to them or disconnect from them once setting get updated /// connect to them or disconnect from them once setting get updated
var private bool connectedToSignal; var private bool connectedToSignal;
protected function OnDisabled() protected function OnEnabled() {
{ if (colorSetting != CCS_DoNothing) {
if (connectedToSignal) _.chat.OnMessage(self).connect = ReformatChatMessage;
{ }
connectedToSignal = false; }
protected function OnDisabled() {
if (colorSetting != CCS_DoNothing) {
_.chat.OnMessage(self).Disconnect(); _.chat.OnMessage(self).Disconnect();
} }
} }
protected function SwapConfig(FeatureConfig config) protected function SwapConfig(FeatureConfig config)
{ {
local bool configRequiresSignal; local bool configRequiresSignal;
local FutilityChat newConfig; local FutilityChat newConfig;
newConfig = FutilityChat(config); newConfig = FutilityChat(config);
if (newConfig == none) { if (newConfig == none) {
return; return;
} }
colorSetting = newConfig.colorSetting; colorSetting = newConfig.colorSetting;
configuredColor = newConfig.configuredColor; configuredColor = newConfig.configuredColor;
teamColorModifier = newConfig.teamColorModifier; teamColorModifier = newConfig.teamColorModifier;
configRequiresSignal = (colorSetting != CCS_DoNothing); 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();
}
} }
private function bool ReformatChatMessage( private function bool ReformatChatMessage(
EPlayer sender, EPlayer sender,
MutableText message, MutableText message,
bool teamMessage) bool teamMessage
{ ) {
local Text messageCopy; local Text messageCopy;
local BaseText.Formatting defaultFormatting; local BaseText.Formatting defaultFormatting;
if (sender == none) return true; if (sender == none) return true;
if (message == none) return true; if (message == none) return true;
if (colorSetting == CCS_DoNothing) return true; if (colorSetting == CCS_DoNothing) return true;
defaultFormatting.isColored = true; defaultFormatting.isColored = true;
if ( colorSetting == CCS_TeamColorForced if (colorSetting == CCS_TeamColorForced || colorSetting == CCS_TeamColorCustom) {
|| colorSetting == CCS_TeamColorCustom)
{
defaultFormatting.color = ModColor(sender.GetTeamColor()); defaultFormatting.color = ModColor(sender.GetTeamColor());
} } else {
else {
defaultFormatting.color = configuredColor; defaultFormatting.color = configuredColor;
} }
if (message.StartsWith(P("|"))) { if (message.StartsWith(P("|"))) {
message.Remove(0, 1); message.Remove(0, 1);
} } else if (colorSetting != CCS_TeamColorForced && colorSetting != CCS_ConfigColorForced) {
else if ( colorSetting != CCS_TeamColorForced
&& colorSetting != CCS_ConfigColorForced)
{
messageCopy = message.Copy(); messageCopy = message.Copy();
class'FormattingStringParser'.static class'FormattingStringParser'.static.ParseFormatted(messageCopy, message.Clear());
.ParseFormatted(messageCopy, message.Clear());
_.memory.Free(messageCopy); _.memory.Free(messageCopy);
} }
message.ChangeDefaultFormatting(defaultFormatting); message.ChangeDefaultFormatting(defaultFormatting);
return true; return true;
} }
private function Color ModColor(Color inputColor) private function Color ModColor(Color inputColor) {
{
local Color mixColor; local Color mixColor;
local Color outputColor; local Color outputColor;
local float clampedModifier; local float clampedModifier;
if (Abs(teamColorModifier) < 0.001) { if (Abs(teamColorModifier) < 0.001) {
return inputColor; return inputColor;
} }
clampedModifier = FClamp(teamColorModifier, -1.0, 1.0); clampedModifier = FClamp(teamColorModifier, -1.0, 1.0);
if (clampedModifier > 0) { if (clampedModifier > 0) {
mixColor = _.color.White; mixColor = _.color.White;
} } else {
else
{
mixColor = _.color.Black; mixColor = _.color.Black;
clampedModifier *= -1.0; clampedModifier *= -1.0;
} }
@ -147,7 +106,6 @@ private function Color ModColor(Color inputColor)
return outputColor; return outputColor;
} }
defaultproperties defaultproperties {
{
configClass = class'FutilityChat' configClass = class'FutilityChat'
} }

164
sources/Features/FutileNickames/FutilityNicknames.uc

@ -1,6 +1,6 @@
/** /**
* Config object for `FutileNickames_Feature`. * Config class for storing map lists.
* Copyright 2022 Anton Tarasenko * Copyright 2022-2023 Anton Tarasenko
*------------------------------------------------------------------------------ *------------------------------------------------------------------------------
* This file is part of Acedia. * This file is part of Acedia.
* *
@ -21,62 +21,108 @@ class FutilityNicknames extends FeatureConfig
perobjectconfig perobjectconfig
config(FutilityNicknames); config(FutilityNicknames);
enum NicknameSpacesAction enum NicknameSpacesAction {
{
NSA_DoNothing, NSA_DoNothing,
NSA_Trim, NSA_Trim,
NSA_Simplify NSA_Simplify
}; };
enum NicknameColorPermissions enum NicknameColorPermissions {
{
NCP_ForbidColor, NCP_ForbidColor,
NCP_ForceTeamColor, NCP_ForceTeamColor,
NCP_ForceSingleColor, NCP_ForceSingleColor,
NCP_AllowAnyColor NCP_AllowAnyColor
}; };
var public config NicknameSpacesAction spacesAction; /// How to treat whitespace characters inside players' nicknames:
var public config NicknameColorPermissions colorPermissions; ///
var public config bool replaceSpacesWithUnderscores; /// * `NSA_DoNothing` - does nothing, leaving whitespaces as they are;
var public config bool removeSingleQuotationMarks; /// * `NSA_Trim` - removes leading and trailing whitespaces for nicknames;
var public config bool removeDoubleQuotationMarks; /// * `NSA_Simplify` - removes leading and trailing whitespaces
var public config bool correctEmptyNicknames; /// for nicknames, also reducing a sequence of whitespaces inside
var public config int maxNicknameLength; /// nickname to a single space, e.g. "my nick" becomes "my nick".
var public config array<string> fallbackNickname; ///
/// 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() protected function HashTable ToData() {
{ local int i;
local int i;
local ArrayList fallbackNicknamesData; local ArrayList fallbackNicknamesData;
local HashTable data; local HashTable data;
data = __().collections.EmptyHashTable(); data = __().collections.EmptyHashTable();
data.SetString(P("spacesAction"), string(spacesAction)); data.SetString(P("spacesAction"), string(spacesAction));
data.SetString(P("colorPermissions"), string(colorPermissions)); data.SetString(P("colorPermissions"), string(colorPermissions));
data.SetBool( P("replaceSpacesWithUnderscores"), data.SetBool(P("replaceSpacesWithUnderscores"), replaceSpacesWithUnderscores);
replaceSpacesWithUnderscores); data.SetBool(P("removeSingleQuotationMarks"), removeSingleQuotationMarks);
data.SetBool( P("removeSingleQuotationMarks"), data.SetBool(P("removeDoubleQuotationMarks"), removeDoubleQuotationMarks);
removeSingleQuotationMarks);
data.SetBool( P("removeDoubleQuotationMarks"),
removeDoubleQuotationMarks);
data.SetBool(P("correctEmptyNicknames"), correctEmptyNicknames); data.SetBool(P("correctEmptyNicknames"), correctEmptyNicknames);
data.SetInt(P("maxNicknameLength"), maxNicknameLength); data.SetInt(P("maxNicknameLength"), maxNicknameLength);
fallbackNicknamesData = __().collections.EmptyArrayList(); fallbackNicknamesData = __().collections.EmptyArrayList();
for (i = 0; i < fallbackNickname.length; i += 1)
{ for (i = 0; i < fallbackNickname.length; i += 1) {
fallbackNicknamesData.AddItem( fallbackNicknamesData.AddItem(__().text.FromFormattedString(fallbackNickname[i]));
__().text.FromFormattedString(fallbackNickname[i]));
} }
data.SetItem(P("fallbackNickname"), fallbackNicknamesData); data.SetItem(P("fallbackNickname"), fallbackNicknamesData);
_.memory.Free(fallbackNicknamesData); _.memory.Free(fallbackNicknamesData);
return data; return data;
} }
protected function FromData(HashTable source) protected function FromData(HashTable source) {
{ local int i;
local int i; local Text nextNickName, storedText;
local Text nextNickName, storedText;
local ArrayList fallbackNicknamesData; local ArrayList fallbackNicknamesData;
if (source == none) { if (source == none) {
return; return;
} }
@ -86,25 +132,20 @@ protected function FromData(HashTable source)
storedText = source.GetText(P("colorPermissions")); storedText = source.GetText(P("colorPermissions"));
colorPermissions = ColorPermissionsFromText(storedText); colorPermissions = ColorPermissionsFromText(storedText);
_.memory.Free(storedText); _.memory.Free(storedText);
replaceSpacesWithUnderscores = replaceSpacesWithUnderscores = source.GetBool(P("replaceSpacesWithUnderscores"), true);
source.GetBool(P("replaceSpacesWithUnderscores"), true); removeSingleQuotationMarks = source.GetBool(P("removeSingleQuotationMarks"), true);
removeSingleQuotationMarks = removeDoubleQuotationMarks = source.GetBool(P("removeDoubleQuotationMarks"), true);
source.GetBool(P("removeSingleQuotationMarks"), true);
removeDoubleQuotationMarks =
source.GetBool(P("removeDoubleQuotationMarks"), true);
correctEmptyNicknames = source.GetBool(P("correctEmptyNicknames"), true); correctEmptyNicknames = source.GetBool(P("correctEmptyNicknames"), true);
maxNicknameLength = source.GetInt(P("correctEmptyNicknames"), 20); maxNicknameLength = source.GetInt(P("correctEmptyNicknames"), 20);
fallbackNicknamesData = source.GetArrayList(P("fallbackNickname")); fallbackNicknamesData = source.GetArrayList(P("fallbackNickname"));
if (fallbackNickname.length > 0) { if (fallbackNickname.length > 0) {
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); nextNickName = fallbackNicknamesData.GetText(i);
if (nextNickName != none) { if (nextNickName != none) {
fallbackNickname[i] = nextNickName.ToFormattedString(); fallbackNickname[i] = nextNickName.ToFormattedString();
} } else {
else {
fallbackNickname[i] = ""; fallbackNickname[i] = "";
} }
_.memory.Free(nextNickName); _.memory.Free(nextNickName);
@ -112,8 +153,7 @@ protected function FromData(HashTable source)
_.memory.Free(fallbackNicknamesData); _.memory.Free(fallbackNicknamesData);
} }
private function NicknameSpacesAction SpaceActionFromText(BaseText action) private function NicknameSpacesAction SpaceActionFromText(BaseText action) {
{
if (action == none) { if (action == none) {
return NSA_DoNothing; return NSA_DoNothing;
} }
@ -129,9 +169,7 @@ private function NicknameSpacesAction SpaceActionFromText(BaseText action)
return NSA_DoNothing; return NSA_DoNothing;
} }
private function NicknameColorPermissions ColorPermissionsFromText( private function NicknameColorPermissions ColorPermissionsFromText(BaseText permissions) {
BaseText permissions)
{
if (permissions == none) { if (permissions == none) {
return NCP_ForbidColor; return NCP_ForbidColor;
} }
@ -150,15 +188,14 @@ private function NicknameColorPermissions ColorPermissionsFromText(
return NCP_ForbidColor; return NCP_ForbidColor;
} }
protected function DefaultIt() protected function DefaultIt() {
{ spacesAction = NSA_DoNothing;
spacesAction = NSA_DoNothing; colorPermissions = NCP_ForbidColor;
colorPermissions = NCP_ForbidColor; replaceSpacesWithUnderscores = true;
replaceSpacesWithUnderscores = true; removeSingleQuotationMarks = false;
removeSingleQuotationMarks = false; removeDoubleQuotationMarks = true;
removeDoubleQuotationMarks = true; correctEmptyNicknames = true;
correctEmptyNicknames = true; maxNicknameLength = 20;
maxNicknameLength = 20;
if (fallbackNickname.length > 0) { if (fallbackNickname.length > 0) {
fallbackNickname.length = 0; fallbackNickname.length = 0;
} }
@ -174,16 +211,15 @@ protected function DefaultIt()
fallbackNickname[9] = "Bug Meat"; fallbackNickname[9] = "Bug Meat";
} }
defaultproperties defaultproperties {
{
configName = "FutilityNicknames" configName = "FutilityNicknames"
spacesAction = NSA_DoNothing spacesAction = NSA_DoNothing
colorPermissions = NCP_ForbidColor colorPermissions = NCP_ForbidColor
replaceSpacesWithUnderscores = true replaceSpacesWithUnderscores = true
removeSingleQuotationMarks = false removeSingleQuotationMarks = false
removeDoubleQuotationMarks = true removeDoubleQuotationMarks = true
correctEmptyNicknames = true correctEmptyNicknames = true
maxNicknameLength = 20 maxNicknameLength = 20
fallbackNickname(0) = "Fresh Meat" fallbackNickname(0) = "Fresh Meat"
fallbackNickname(1) = "Rotten Meat" fallbackNickname(1) = "Rotten Meat"
fallbackNickname(2) = "Troll 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. * Config class for storing map lists.
* It allows you to customize vanilla limitations for nickname length and * Copyright 2022-2023 Anton Tarasenko
* color with those of your own design. Enabling this feature overwrites
* default behaviour.
* Copyright 2022 Anton Tarasenko
*------------------------------------------------------------------------------ *------------------------------------------------------------------------------
* This file is part of Acedia. * This file is part of Acedia.
* *
@ -23,224 +20,133 @@
class FutilityNicknames_Feature extends Feature class FutilityNicknames_Feature extends Feature
dependson(FutilityNicknames); dependson(FutilityNicknames);
/** //! This feature's functionality is rather simple, but we will still break up
* This feature's functionality is rather simple, but we will still break up //! what its various components are.
* what its various components are. //!
* //! Fallback nicknames are picked at random from
* Fallback nicknames are picked at random from //! the `fallbackNicknames` array. This is done by copying that array into
* the `fallbackNicknames` array. This is done by copying that array into //! `unusedNicknames` and then picking and removing its random elements each
* `unusedNicknames` and then picking and removing its random elements each //! time we need a fallback. Once `unusedNicknames` is empty - it is copied from
* time we need a fallback. Once `unusedNicknames` is empty - it is copied from //! `fallbackNicknames` once again, letting already used nicknames to be reused.
* `fallbackNicknames` once again, letting already used nicknames to be reused. //! `unusedNicknames` contains same references as `fallbackNicknames`,
* `unusedNicknames` contains same references as `fallbackNicknames`, //! so they need to be separately deallocated and should also be forgotten once
* so they need to be separately deallocated and should also be forgotten once //! `fallbackNicknames` are deallocated`.
* `fallbackNicknames` are deallocated`. //! This is implemented inside `PickNextFallback()` method.
* This is implemented inside `PickNextFallback()` method. //!
* //! Nickname changes are applied inside `CensorNickname()` method that uses
* Nickname changes are applied inside `CensorNickname()` method that uses //! several auxiliary methods to perform different stages of "censoring".
* several auxiliary methods to perform different stages of "censoring". //! Censoring is performed:
* Censoring is performed: //! 1. On any player's name change
* 1. On any player's name change //! (using `OnPlayerNameChanging()` signal, connected to
* (using `OnPlayerNameChanging()` signal, connected to //! `HandleNicknameChange()`);
* `HandleNicknameChange()`); //! 2. When new player logins (using `OnNewPlayer()` signal,
* 2. When new player logins (using `OnNewPlayer()` signal, //! conneted to `CensorOriginalNickname()`) to enforce our own
* conneted to `CensorOriginalNickname()`) to enforce our own //! handling of player's original nickname.
* 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()`.
*/
// 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; 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*/ 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 /// Guaranteed order of applying changes (only chosen ones) is as following:
// underscores and `false` to leave them as is. ///
// Default is `true`, same as on vanilla. However there is one difference: /// 1. Trim/simplify spaces;
// Futility replaces all whitespace characters (including tabulations, /// 2. Remove single and double quotation marks;
// non-breaking spaces, etc.) instead of only ' '. /// 3. Enforce max limit of nickname's length;
var private /*config*/ bool replaceSpacesWithUnderscores; /// 4. Replace empty nickname with fallback nickname (no further changes
// Set this to `true` to remove single 'quotation marks' and `false` to /// will be applied to fallback nickname in that case);
// leave them. Default is `false`, same as on vanilla. /// 5. Enforce color limitation;
var private /*config*/ bool removeSingleQuotationMarks; /// 6. Replace remaining whitespaces with underscores.
// Set this to `true` to remove dobule 'quotation marks' and `false` to ///
// leave them. Default is `true`, same as on vanilla. /// NOTE #1: as follows from the instruction described above, no changes will
var private /*config*/ bool removeDoubleQuotationMarks; /// ever be applied to fallback nicknames (unless player's nickname coincides
/// with one by pure accident).
// Max allowed nickname length. Negative values disable any length limits. ///
// /// NOTE #2: whitespaces inside steam nicknames are converted into underscores
// NOTE #1: `0` resets all nicknames to be empty and, /// before they are passed into the game and this is a change Futility
// if `correctEmptyNicknames` is set to `true`, they will be replaced with /// cannot currently abort. Therefore all changes relevant to whitespaces inside
// one of the fallback nicknames /// nicknames will only be applied to in-game changes.
// (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;
// Should we replace empty player nicknames with a random fallback nickname /// Nicknames from `fallbackNickname` that can still be picked in the current
// (defined in `fallbackNickname` array)? /// rotation.
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.
var private array<Text> unusedNicknames; 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; var private const int CODEPOINT_UNDERSCORE;
protected function OnDisabled() protected function OnEnabled() {
{ if (IsAnyCensoringEnabled()) {
_.memory.FreeMany(fallbackNickname); // Do this before adding event handler to avoid censoring nicknames
// Free this `Text` data - it will be refilled with `SwapConfig()` // second time (censoring nickname will trigger `OnPlayerNameChanging()`
// if this feature is ever reenabled // signal)
if (fallbackNickname.length > 0) CensorCurrentPlayersNicknames();
{ _.players.OnPlayerNameChanging(self).connect = HandleNicknameChange;
_.memory.FreeMany(fallbackNickname); _.players.OnNewPlayer(self).connect = CensorOriginalNickname;
fallbackNickname.length = 0;
unusedNicknames.length = 0;
} }
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.OnPlayerNameChanging(self).Disconnect();
_.players.OnNewPlayer(self).Disconnect(); _.players.OnNewPlayer(self).Disconnect();
} }
} }
protected function SwapConfig(FeatureConfig config) protected function SwapConfig(FeatureConfig config) {
{
local bool configRequiresCensoring;
local FutilityNicknames newConfig; local FutilityNicknames newConfig;
newConfig = FutilityNicknames(config); newConfig = FutilityNicknames(config);
if (newConfig == none) { if (newConfig == none) {
return; return;
} }
replaceSpacesWithUnderscores = newConfig.replaceSpacesWithUnderscores; replaceSpacesWithUnderscores = newConfig.replaceSpacesWithUnderscores;
removeSingleQuotationMarks = newConfig.removeSingleQuotationMarks; removeSingleQuotationMarks = newConfig.removeSingleQuotationMarks;
removeDoubleQuotationMarks = newConfig.removeDoubleQuotationMarks; removeDoubleQuotationMarks = newConfig.removeDoubleQuotationMarks;
correctEmptyNicknames = newConfig.correctEmptyNicknames; correctEmptyNicknames = newConfig.correctEmptyNicknames;
spacesAction = newConfig.spacesAction; spacesAction = newConfig.spacesAction;
colorPermissions = newConfig.colorPermissions; colorPermissions = newConfig.colorPermissions;
maxNicknameLength = newConfig.maxNicknameLength; 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();
}
SwapFallbackNicknames(newConfig); SwapFallbackNicknames(newConfig);
} }
private function Text PickNextFallback() private function SwapFallbackNicknames(FutilityNicknames newConfig) {
{ local int i;
local int pickedIndex;
local Text result; _.memory.FreeMany(fallbackNickname);
if (fallbackNickname.length <= 0) fallbackNickname.length = 0;
{ for (i = 0; i < newConfig.fallbackNickname.length; i += 1) {
// Just in case this feature is really misconfigured 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(); return P("Fresh Meat").Copy();
} }
if (unusedNicknames.length <= 0) { if (unusedNicknames.length <= 0) {
unusedNicknames = fallbackNickname; unusedNicknames = fallbackNickname;
} }
// Pick one nickname at random. // Pick one nickname at random.
// `pickedIndex` will belong to [0; unusedNicknames.length - 1] segment. // `pickedIndex` will belong to [0; unusedNicknames.length - 1] segment.
pickedIndex = Rand(unusedNicknames.length); pickedIndex = Rand(unusedNicknames.length);
result = unusedNicknames[pickedIndex].Copy(); result = unusedNicknames[pickedIndex].Copy();
unusedNicknames.Remove(pickedIndex, 1); unusedNicknames.Remove(pickedIndex, 1);
return result; return result;
} }
protected function SwapFallbackNicknames(FutilityNicknames newConfig) private function bool IsAnyCensoringEnabled() {
{
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()
{
return ( replaceSpacesWithUnderscores return ( replaceSpacesWithUnderscores
|| removeSingleQuotationMarks || removeSingleQuotationMarks
|| removeDoubleQuotationMarks || removeDoubleQuotationMarks
@ -250,40 +156,38 @@ private function bool IsAnyCensoringEnabled()
|| spacesAction != NSA_DoNothing); || spacesAction != NSA_DoNothing);
} }
// For nickname changes mid-game. // For nickname changes mid-game.
private function HandleNicknameChange( private function HandleNicknameChange(
EPlayer affectedPlayer, EPlayer affectedPlayer,
BaseText oldName, BaseText oldName,
MutableText newName) MutableText newName
{ ) {
CensorNickname(newName, affectedPlayer); CensorNickname(newName, affectedPlayer);
} }
// For handling of player's original nicknames. // For handling of player's original nicknames.
private function CensorOriginalNickname(EPlayer affectedPlayer) private function CensorOriginalNickname(EPlayer affectedPlayer) {
{
local Text originalNickname; local Text originalNickname;
if (affectedPlayer == none) { if (affectedPlayer == none) {
return; return;
} }
originalNickname = affectedPlayer.GetOriginalName(); originalNickname = affectedPlayer.GetOriginalName();
// This will automatically trigger `OnPlayerNameChanging()` signal and // This will automatically trigger `OnPlayerNameChanging()` signal and
// our `HandleNicknameChange()` handler. // our `HandleNicknameChange()` handler.
affectedPlayer.SetName(originalNickname); affectedPlayer.SetName(originalNickname);
_.memory.Free(originalNickname); _.memory.Free(originalNickname);
} }
// For handling nicknames of players after censoring is re-activated by // For handling nicknames of players after censoring is re-activated by
// config change. // config change.
private function CensorCurrentPlayersNicknames() private function CensorCurrentPlayersNicknames() {
{ local int i;
local int i; local Text nextNickname;
local Text nextNickname; local MutableText alteredNickname;
local MutableText alteredNickname; local array<EPlayer> currentPlayers;
local array<EPlayer> currentPlayers;
currentPlayers = _.players.GetAll(); currentPlayers = _.players.GetAll();
for (i = 0; i < currentPlayers.length; i += 1) for (i = 0; i < currentPlayers.length; i += 1) {
{
nextNickname = currentPlayers[i].GetName(); nextNickname = currentPlayers[i].GetName();
alteredNickname = nextNickname.MutableCopy(); alteredNickname = nextNickname.MutableCopy();
CensorNickname(alteredNickname, currentPlayers[i]); CensorNickname(alteredNickname, currentPlayers[i]);
@ -295,10 +199,10 @@ private function CensorCurrentPlayersNicknames()
} }
} }
private function CensorNickname(MutableText nickname, EPlayer affectedPlayer) private function CensorNickname(MutableText nickname, EPlayer affectedPlayer) {
{ local Text fallback;
local Text fallback; local BaseText.Formatting newFormatting;
local BaseText.Formatting newFormatting;
if (nickname == none) return; if (nickname == none) return;
if (affectedPlayer == none) return; if (affectedPlayer == none) return;
@ -314,25 +218,21 @@ private function CensorNickname(MutableText nickname, EPlayer affectedPlayer)
if (maxNicknameLength >= 0) { if (maxNicknameLength >= 0) {
nickname.Remove(maxNicknameLength); nickname.Remove(maxNicknameLength);
} }
if (correctEmptyNicknames && nickname.IsEmpty()) if (correctEmptyNicknames && nickname.IsEmpty()) {
{
fallback = PickNextFallback(); fallback = PickNextFallback();
nickname.Append(fallback); nickname.Append(fallback);
_.memory.Free(fallback); _.memory.Free(fallback);
return; return;
} }
if (colorPermissions != NCP_AllowAnyColor) if (colorPermissions != NCP_AllowAnyColor) {
{
if (colorPermissions == NCP_ForceSingleColor) { if (colorPermissions == NCP_ForceSingleColor) {
newFormatting = nickname.GetCharacter(0).formatting; newFormatting = nickname.GetCharacter(0).formatting;
} } else if (colorPermissions == NCP_ForceTeamColor) {
else if (colorPermissions == NCP_ForceTeamColor)
{
newFormatting.isColored = true; newFormatting.isColored = true;
newFormatting.color = affectedPlayer.GetTeamColor(); newFormatting.color = affectedPlayer.GetTeamColor();
} }
// `colorPermissions == NCP_ForbidColor` // `colorPermissions == NCP_ForbidColor`
// `newFormatting` is colorless by default // `newFormatting` is colorless by default
nickname.ChangeFormatting(newFormatting); nickname.ChangeFormatting(newFormatting);
} }
if (replaceSpacesWithUnderscores) { if (replaceSpacesWithUnderscores) {
@ -340,21 +240,18 @@ private function CensorNickname(MutableText nickname, EPlayer affectedPlayer)
} }
} }
// Asusmes `nickname != none`. // Asusmes `nickname != none`.
private function ReplaceSpaces(MutableText nickname) private function ReplaceSpaces(MutableText nickname) {
{ local int i;
local int i; local MutableText nicknameCopy;
local MutableText nicknameCopy; local BaseText.Character nextCharacter, underscoreCharacter;
local BaseText.Character nextCharacter, underscoreCharacter;
nicknameCopy = nickname.MutableCopy(); nicknameCopy = nickname.MutableCopy();
nickname.Clear(); nickname.Clear();
underscoreCharacter = underscoreCharacter = _.text.CharacterFromCodePoint(CODEPOINT_UNDERSCORE);
_.text.CharacterFromCodePoint(CODEPOINT_UNDERSCORE); for (i = 0; i < nicknameCopy.GetLength(); i += 1) {
for (i = 0; i < nicknameCopy.GetLength(); i += 1)
{
nextCharacter = nicknameCopy.GetCharacter(i); nextCharacter = nicknameCopy.GetCharacter(i);
if (_.text.IsWhitespace(nextCharacter)) if (_.text.IsWhitespace(nextCharacter)) {
{
// Replace character with underscore, leaving the formatting // Replace character with underscore, leaving the formatting
underscoreCharacter.formatting = nextCharacter.formatting; underscoreCharacter.formatting = nextCharacter.formatting;
nextCharacter = underscoreCharacter; nextCharacter = underscoreCharacter;
@ -364,8 +261,7 @@ private function ReplaceSpaces(MutableText nickname)
_.memory.Free(nicknameCopy); _.memory.Free(nicknameCopy);
} }
defaultproperties defaultproperties {
{
configClass = class'FutilityNicknames' configClass = class'FutilityNicknames'
CODEPOINT_UNDERSCORE = 95 // '_' CODEPOINT_UNDERSCORE = 95 // '_'
} }

12
sources/Futility.uc

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