From 882830f273dbd5c4755bae5470666dc8ee1bff07 Mon Sep 17 00:00:00 2001 From: Anton Tarasenko Date: Thu, 30 Jun 2022 04:44:18 +0700 Subject: [PATCH] Refactor `ReportTool` into `ListBuilder` --- sources/Commands/ACommandFeature.uc | 22 +-- sources/Commands/ACommandInventory.uc | 17 +- sources/Tools/FormattingReportTool.uc | 56 +++--- sources/Tools/InventoryTool.uc | 197 ++++++++++++++------ sources/Tools/ListBuilder.uc | 241 ++++++++++++++++++++++++ sources/Tools/ReportTool.uc | 252 -------------------------- 6 files changed, 437 insertions(+), 348 deletions(-) create mode 100644 sources/Tools/ListBuilder.uc delete mode 100644 sources/Tools/ReportTool.uc diff --git a/sources/Commands/ACommandFeature.uc b/sources/Commands/ACommandFeature.uc index 7e66779..0cac2e8 100644 --- a/sources/Commands/ACommandFeature.uc +++ b/sources/Commands/ACommandFeature.uc @@ -190,7 +190,7 @@ protected function ShowFeature(class feature) local int i; local Text autoConfig; local MutableText featureName, builder; - local ReportTool reportTool; + local ListBuilder configList; local array availableConfigs; local class configClass; @@ -203,8 +203,7 @@ protected function ShowFeature(class feature) } featureName = _.text .FromClassM(feature) - .ChangeDefaultFormatting( - _.text.FormattingFromColor(_.color.TextEmphasis)); + .ChangeDefaultColor(_.color.TextEmphasis); builder = _.text.Empty(); if (feature.static.IsEnabled()) { builder.Append(F("[ {$TextPositive enabled} ] ")); @@ -220,26 +219,25 @@ protected function ShowFeature(class feature) else if (availableConfigs.length > 1) { builder.Append(P(" with configs:")); } - reportTool = ReportTool(_.memory.Allocate(class'ReportTool')); - reportTool.Initialize(builder); + callerConsole.Write(builder); _.memory.Free(builder); + configList = ListBuilder(_.memory.Allocate(class'ListBuilder')); autoConfig = configClass.static.GetAutoEnabledConfig(); for (i = 0; i < availableConfigs.length; i += 1) { - builder = _.text.Empty().Append(availableConfigs[i]); - reportTool.Item(builder); + configList.Item(availableConfigs[i]); if ( autoConfig != none && autoConfig.Compare(availableConfigs[i], SCASE_INSENSITIVE)) { - reportTool.Detail(F("{$TextPositive auto enabled}")); + configList.Comment(F("{$TextPositive auto enabled}")); } - _.memory.Free(builder); - builder = none; } - reportTool.Report(callerConsole); + builder = configList.GetMutable(); + callerConsole.WriteLine(builder); _.memory.FreeMany(availableConfigs); - _.memory.Free(reportTool); + _.memory.Free(configList); _.memory.Free(autoConfig); + _.memory.Free(builder); } defaultproperties diff --git a/sources/Commands/ACommandInventory.uc b/sources/Commands/ACommandInventory.uc index abec758..a61cc5b 100644 --- a/sources/Commands/ACommandInventory.uc +++ b/sources/Commands/ACommandInventory.uc @@ -122,7 +122,7 @@ protected function ExecutedFor( if (!callerPlayer.SameAs(player)) { tool.ReportChanges(callerPlayer, targetConsole, IRT_Target); } - tool.ReportChanges(callerPlayer, callerConsole, IRT_Caller); + tool.ReportChanges(callerPlayer, callerConsole, IRT_Instigator); tool.ReportChanges(callerPlayer, othersConsole, IRT_Others); _.memory.Free(tool); } @@ -219,12 +219,14 @@ protected function array LoadAllItemsLists(DynamicArray specifiedLists) local array result; local array nextItemBatch; local array availableLists; - local ReportTool badLists; + local ListBuilder badLists; + local MutableText badListsAsText; + if (specifiedLists == none) { return result; } - badLists = ReportTool(_.memory.Allocate(class'ReportTool')); - badLists.Initialize(T(TLISTS_SKIPPED)); + badLists = ListBuilder(_.memory.Allocate(class'ListBuilder')); + callerConsole.Write(T(TLISTS_SKIPPED)); availableLists = _.kf.templates.GetAvailableLists(); for (i = 0; i < specifiedLists.Getlength(); i += 1) { @@ -234,8 +236,9 @@ protected function array LoadAllItemsLists(DynamicArray specifiedLists) result[result.length] = nextItemBatch[j]; } } - badLists.Report(callerConsole); - _.memory.Free(badLists); + badListsAsText = badLists.IntoMutableText(); + callerConsole.WriteLine(badListsAsText); + _.memory.Free(badListsAsText); _.memory.FreeMany(availableLists); return result; } @@ -243,7 +246,7 @@ protected function array LoadAllItemsLists(DynamicArray specifiedLists) protected function array LoadItemsList( BaseText listName, array availableLists, - ReportTool badLists) + ListBuilder badLists) { local int i; local array emptyArray; diff --git a/sources/Tools/FormattingReportTool.uc b/sources/Tools/FormattingReportTool.uc index 74e0b07..367b076 100644 --- a/sources/Tools/FormattingReportTool.uc +++ b/sources/Tools/FormattingReportTool.uc @@ -38,16 +38,18 @@ public final static function Report( array errors) { local int i; - local ReportTool reportTool; - reportTool = ReportTool(__().memory.Allocate(class'ReportTool')); - reportTool.Initialize(T(default.TREPORT_HEADER)); + local ListBuilder builder; + local MutableText itemList; + + builder = ListBuilder(__().memory.Allocate(class'ListBuilder')); + writer.Write(T(default.TREPORT_HEADER)); for (i = 0; i < errors.length; i += 1) { if (errors[i].type == FSE_UnmatchedClosingBrackets) { ReportCount( errors[i], - reportTool, + builder, default.TUNMATCHED_SINGLE, default.TUNMATCHED_MULTIPLE); } @@ -55,25 +57,26 @@ public final static function Report( { ReportCount( errors[i], - reportTool, + builder, default.TEMPTY_TAG_SINGLE, default.TEMPTY_TAG_MULTIPLE); } else if (errors[i].type == FSE_BadColor) { - reportTool.Item(T(default.TBAD_COLOR)).Detail(errors[i].cause); + builder.Item(T(default.TBAD_COLOR)).Comment(errors[i].cause); } else if (errors[i].type == FSE_BadShortColorTag) { - reportTool.Item(T(default.TBADSHORT_TAG)).Detail(errors[i].cause); + builder.Item(T(default.TBADSHORT_TAG)).Comment(errors[i].cause); } else if (errors[i].type == FSE_BadGradientPoint) { - reportTool + builder .Item(T(default.TBAD_GRADIENT_POINT)) - .Detail(errors[i].cause); + .Comment(errors[i].cause); } } - reportTool.Report(writer); - __().memory.Free(reportTool); + itemList = builder.IntoMutableText(); + writer.WriteLine(itemList); + __().memory.Free(itemList); } /** @@ -86,6 +89,7 @@ public final static function FreeErrors( array errors) { local int i; + for (i = 0; i < errors.length; i += 1) { __().memory.Free(errors[i].cause); } @@ -93,40 +97,42 @@ public final static function FreeErrors( private final static function ReportCause( FormattingErrorsReport.FormattedStringError error, - ReportTool reportTool, + ListBuilder builder, int sentence) { - local MutableText builder; + local MutableText causeBuilder; + if (error.cause == none) { return; } - reportTool.Item(T(sentence)); - builder = __().text.FromIntM(error.count).Append(T(default.TCASES)); - reportTool.Detail(builder); - __().memory.Free(builder); + builder.Item(T(sentence)); + causeBuilder = __().text.FromIntM(error.count).Append(T(default.TCASES)); + builder.Comment(causeBuilder); + __().memory.Free(causeBuilder); } // In the methods below, do not double check the error type in the following -// errors or whether `reportTool != none` +// errors or whether `builder != none` private final static function ReportCount( FormattingErrorsReport.FormattedStringError error, - ReportTool reportTool, + ListBuilder builder, int singleSentence, int multipleSentence) { - local MutableText builder; + local MutableText commentBuilder; + if (error.count < 1) { return; } if (error.count == 1) { - reportTool.Item(T(singleSentence)); + builder.Item(T(singleSentence)); return; } - reportTool.Item(T(multipleSentence)); - builder = __().text.FromIntM(error.count).Append(T(default.TCASES)); - reportTool.Detail(builder); - __().memory.Free(builder); + builder.Item(T(multipleSentence)); + commentBuilder = __().text.FromIntM(error.count).Append(T(default.TCASES)); + builder.Comment(commentBuilder); + __().memory.Free(commentBuilder); } defaultproperties diff --git a/sources/Tools/InventoryTool.uc b/sources/Tools/InventoryTool.uc index 1f1da08..3d0a275 100644 --- a/sources/Tools/InventoryTool.uc +++ b/sources/Tools/InventoryTool.uc @@ -48,7 +48,7 @@ var private EPlayer targetPlayer; var private EInventory targetInventory; /** - * `ReportTool`s for 6 different cases: + * `ListBuilder`s for 6 different cases: * ~ two of "...Verbose" and "...Failed" ones make reports about * successes and failures of adding and removals to the instigator of * these changes; @@ -56,14 +56,18 @@ var private EInventory targetInventory; * successful changes to everybody else present on the server. * Supposed to be created via `CreateFor()` method. */ -var private ReportTool itemsAdded; -var private ReportTool itemsRemoved; -var private ReportTool itemsAddedPrivate; -var private ReportTool itemsRemovedPrivate; -var private ReportTool itemsAdditionFailed; -var private ReportTool itemsRemovalFailed; - -var const int TITEMS_ADDED_MESSAGE, TITEMS_ADDED_VEROBSE_MESSAGE; +var public ListBuilder itemsAdded; +var public ListBuilder itemsRemoved; +var public ListBuilder itemsAddedPrivate; +var public ListBuilder itemsRemovedPrivate; +var public ListBuilder itemsAdditionFailed; +var public ListBuilder itemsRemovalFailed; + +var private TextTemplate templateItemsAdded, templateItemsRemoved; +var private TextTemplate templateItemsAddedVerbose, templateItemsRemovedVerbose; +var private TextTemplate templateAdditionFailed, templateRemovalFailed; + +var const int TINSTIGATOR, TTARGET; var const int TITEMS_REMOVED_MESSAGE, TITEMS_REMOVED_VERBOSE_MESSAGE; var const int TITEMS_ADDITION_FAILED_MESSAGE, TITEMS_REMOVAL_FAILED_MESSAGE; var const int TRESOLVED_INTO, TTILDE_QUOTE, TFAULTY_INVENTORY_IMPLEMENTATION; @@ -72,20 +76,35 @@ var const int TDISPLAYING_INVENTORY, THEADER_COLON, TDOT_SPACE, TCOLON_SPACE; var const int TCOMMA_SPACE, TSPACE, TOUT_OF, THIDDEN_ITEMS, TDOLLAR, TYOU; var const int TYOURSELF, TTHEMSELVES; +public static function StaticConstructor() +{ + if (StaticConstructorGuard()) { + return; + } + default.templateItemsAdded = __().text.MakeTemplate_S( + "%%instigator%% {$TextPositive added} following weapons to" + @ "%%target%%: "); + default.templateItemsRemoved = __().text.MakeTemplate_S( + "Weapons {$TextPositive added} to %%target%%: "); + default.templateItemsAddedVerbose = __().text.MakeTemplate_S( + "%%instigator%% {$TextNegative removed} following weapons from" + @ "%%target%%: "); + default.templateItemsRemovedVerbose = __().text.MakeTemplate_S( + "Weapons {$TextNegative removed} from %%target%%: "); + default.templateAdditionFailed = __().text.MakeTemplate_S( + "Weapons we've {$TextFailure failed} to add to %%target%%: "); + default.templateRemovalFailed = __().text.MakeTemplate_S( + "Weapons we've {$TextFailure failed} to remove from %%target%%:" ); +} + protected function Constructor() { - itemsAdded = ReportTool(_.memory.Allocate(class'ReportTool')); - itemsRemoved = ReportTool(_.memory.Allocate(class'ReportTool')); - itemsAddedPrivate = ReportTool(_.memory.Allocate(class'ReportTool')); - itemsRemovedPrivate = ReportTool(_.memory.Allocate(class'ReportTool')); - itemsAdditionFailed = ReportTool(_.memory.Allocate(class'ReportTool')); - itemsRemovalFailed = ReportTool(_.memory.Allocate(class'ReportTool')); - itemsAdded.Initialize(T(TITEMS_ADDED_MESSAGE)); - itemsRemoved.Initialize(T(TITEMS_REMOVED_MESSAGE)); - itemsAddedPrivate.Initialize(T(TITEMS_ADDED_VEROBSE_MESSAGE)); - itemsRemovedPrivate.Initialize(T(TITEMS_REMOVED_VERBOSE_MESSAGE)); - itemsAdditionFailed.Initialize(T(TITEMS_ADDITION_FAILED_MESSAGE)); - itemsRemovalFailed.Initialize(T(TITEMS_REMOVAL_FAILED_MESSAGE)); + itemsAdded = ListBuilder(_.memory.Allocate(class'ListBuilder')); + itemsRemoved = ListBuilder(_.memory.Allocate(class'ListBuilder')); + itemsAddedPrivate = ListBuilder(_.memory.Allocate(class'ListBuilder')); + itemsRemovedPrivate = ListBuilder(_.memory.Allocate(class'ListBuilder')); + itemsAdditionFailed = ListBuilder(_.memory.Allocate(class'ListBuilder')); + itemsRemovalFailed = ListBuilder(_.memory.Allocate(class'ListBuilder')); } protected function Finalizer() @@ -121,6 +140,7 @@ protected function Finalizer() public static final function InventoryTool CreateFor(EPlayer target) { local InventoryTool newInventoryTool; + if (target == none) return none; if (!target.IsExistent()) return none; @@ -137,6 +157,7 @@ private final function bool TargetPlayerIsInvalid() { if (targetPlayer == none) return true; if (!targetPlayer.IsExistent()) return true; + return false; } @@ -178,6 +199,7 @@ private function MutableText MakeResolvedIntoLine( private function TryFillAmmo(EItem item) { local EWeapon itemAsWeapon; + if (item == none) { return; } @@ -209,6 +231,7 @@ public function AddItem( local EItem addedItem; local MutableText resolvedLine; local Text realItemName, itemTemplate, failureReason; + if (TargetPlayerIsInvalid()) return; if (userProvidedName == none) return; @@ -226,7 +249,7 @@ public function AddItem( .CanAddTemplateExplain(itemTemplate, doForce); if (failureReason != none) { - itemsAdditionFailed.Item(userProvidedName).Detail(failureReason); + itemsAdditionFailed.Item(userProvidedName).Comment(failureReason); _.memory.Free(failureReason); _.memory.Free(itemTemplate); return; @@ -241,7 +264,7 @@ public function AddItem( realItemName = addedItem.GetName(); resolvedLine = MakeResolvedIntoLine(userProvidedName, itemTemplate); itemsAdded.Item(realItemName); - itemsAddedPrivate.Item(realItemName).Detail(resolvedLine); + itemsAddedPrivate.Item(realItemName).Comment(resolvedLine); _.memory.Free(realItemName); _.memory.Free(resolvedLine); _.memory.Free(addedItem); @@ -251,7 +274,7 @@ public function AddItem( // `CanAddTemplateExplain()` told us that we should not have failed, // so complain about bad API itemsAdditionFailed.Item(userProvidedName) - .Detail(T(TFAULTY_INVENTORY_IMPLEMENTATION)); + .Comment(T(TFAULTY_INVENTORY_IMPLEMENTATION)); } _.memory.Free(itemTemplate); } @@ -280,6 +303,7 @@ public function RemoveItem( local Text realItemName, itemTemplate; local MutableText resolvedLine; local EItem storedItem; + if (TargetPlayerIsInvalid()) return; if (userProvidedName == none) return; @@ -309,21 +333,21 @@ public function RemoveItem( { resolvedLine = MakeResolvedIntoLine(userProvidedName, itemTemplate); itemsRemoved.Item(realItemName); - itemsRemovedPrivate.Item(realItemName).Detail(resolvedLine); + itemsRemovedPrivate.Item(realItemName).Comment(resolvedLine); _.memory.Free(resolvedLine); } // Try to guess why operation failed // (no special explanation method is present in the API) else if (itemWasMissing) { // likely because it was missing - itemsRemovalFailed.Item(userProvidedName).Detail(T(TITEM_MISSING)); + itemsRemovalFailed.Item(userProvidedName).Comment(T(TITEM_MISSING)); } else if (!doForce && !storedItem.IsRemovable()) // simply was not removable { itemsRemovalFailed.Item(userProvidedName) - .Detail(T(TITEM_NOT_REMOVABLE)); + .Comment(T(TITEM_NOT_REMOVABLE)); } else { // no idea about the reason - itemsRemovalFailed.Item(userProvidedName).Detail(T(TUNKNOWN)); + itemsRemovalFailed.Item(userProvidedName).Comment(T(TUNKNOWN)); } _.memory.Free(storedItem); _.memory.Free(realItemName); @@ -340,6 +364,7 @@ private function DetectAndReportRemovedItems( { local int i, j; local bool itemWasRemoved; + for (i = 0; i < itemsBeforeRemoval.length; i += 1) { itemWasRemoved = true; @@ -363,7 +388,7 @@ private function DetectAndReportRemovedItems( itemsRemovedPrivate.Item(itemNames[i]); } else if (doForce || itemsBeforeRemoval[i].IsRemovable()) { - itemsRemovalFailed.Item(itemNames[i]).Detail(T(TUNKNOWN)); + itemsRemovalFailed.Item(itemNames[i]).Comment(T(TUNKNOWN)); } } } @@ -384,6 +409,7 @@ public function RemoveAllItems(bool doKeep, bool doForce, bool includeHidden) local int i; local array itemNames; local array itemsBeforeRemoval, itemsAfterRemoval; + if (TargetPlayerIsInvalid()) { return; } @@ -433,6 +459,7 @@ public function RemoveEquippedItems( local EItem nextItem; local Text nextItemName; local array equippedItems; + if (TargetPlayerIsInvalid()) { return; } @@ -449,13 +476,13 @@ public function RemoveEquippedItems( { itemsRemovalFailed .Item(nextItemName) - .Detail(T(TITEM_NOT_REMOVABLE)); + .Comment(T(TITEM_NOT_REMOVABLE)); } else if (!targetInventory.Remove(nextItem, doKeep, doForce)) { itemsRemovalFailed .Item(nextItemName) - .Detail(T(TUNKNOWN)); + .Comment(T(TUNKNOWN)); } _.memory.Free(nextItemName); nextItemName = none; @@ -472,10 +499,10 @@ public function RemoveEquippedItems( * the changes. * @param writer `ConsoleWriter` that will be used to output report. * Method does nothing if given `writer` is `none`. - * @param publicReport Is this report meant for the public or for - * the player that caused the changes? Former only (briefly) lists - * successful changes, while latter also provides report about failed - * changes. + * @param reportTarget For who is this report meant to? For general public + * and target only actually occured changes are reported (with different + * phrasing), but for instigator changes that tool failed to do will also + * be reported. */ public final function ReportChanges( EPlayer blamedPlayer, @@ -483,6 +510,7 @@ public final function ReportChanges( InventoryReportTarget reportTarget) { local Text blamedName, targetName; + if (TargetPlayerIsInvalid()) return; if (blamedPlayer == none) return; @@ -495,36 +523,95 @@ public final function ReportChanges( if (reportTarget == IRT_Instigator) { targetName = T(TYOURSELF).Copy(); } + else if (reportTarget == IRT_Target) { + targetName = T(TYOU).Copy(); + } else { targetName = T(TTHEMSELVES).Copy(); } } + default.templateItemsAdded + .TextArg(T(TINSTIGATOR), blamedName) + .TextArg(T(TTARGET), targetName); + default.templateItemsRemoved + .TextArg(T(TINSTIGATOR), blamedName) + .TextArg(T(TTARGET), targetName); if (reportTarget == IRT_Others) { - itemsAdded.Report(writer, blamedName, targetName); - itemsRemoved.Report(writer, blamedName, targetName); + ReportWeaponList(writer, default.templateItemsAdded, itemsAdded); + ReportWeaponList(writer, default.templateItemsRemoved, itemsRemoved); } else if (reportTarget == IRT_Target) { - itemsAdded.Report(writer, blamedName, T(TYOU)); - itemsRemoved.Report(writer, blamedName, T(TYOU)); + ReportWeaponList(writer, default.templateItemsAdded, itemsAdded); + ReportWeaponList(writer, default.templateItemsRemoved, itemsRemoved); } else if (reportTarget == IRT_Instigator) { - if (targetPlayer.SameAs(blamedPlayer)) - { - _.memory.Free(targetName); - targetName = T(TYOURSELF).Copy(); - } - itemsAddedPrivate.Report(writer, blamedName, targetName); - itemsRemovedPrivate.Report(writer, blamedName, targetName); - itemsAdditionFailed.Report(writer, blamedName, targetName); - itemsRemovalFailed.Report(writer, blamedName, targetName); + default.templateItemsAddedVerbose + .TextArg(T(TINSTIGATOR), blamedName) + .TextArg(T(TTARGET), targetName); + default.templateItemsRemovedVerbose + .TextArg(T(TINSTIGATOR), blamedName) + .TextArg(T(TTARGET), targetName); + default.templateAdditionFailed + .TextArg(T(TINSTIGATOR), blamedName) + .TextArg(T(TTARGET), targetName); + default.templateremovalFailed + .TextArg(T(TINSTIGATOR), blamedName) + .TextArg(T(TTARGET), targetName); + ReportWeaponList( + writer, + default.templateItemsAddedVerbose, + itemsAddedPrivate); + ReportWeaponList( + writer, + default.templateItemsRemovedVerbose, + itemsRemovedPrivate); + ReportWeaponList( + writer, + default.templateAdditionFailed, + itemsAdditionFailed); + ReportWeaponList( + writer, + default.templateremovalFailed, + itemsRemovalFailed); } _.memory.Free(blamedName); _.memory.Free(targetName); } +private final function ReportWeaponList( + ConsoleWriter writer, + TextTemplate header, + ListBuilder builder) +{ + local MutableText output; + + if (writer == none) { + return; + } + if (header != none) + { + output = header.CollectFormattedM(); + writer.Write(output); + _.memory.Free(output); + output = none; + } + if (builder != none) { + output = builder.GetMutable(); + } + writer.WriteLine(output); + _.memory.Free(output); +} + +/* itemsAdded.Initialize(T(TITEMS_ADDED_MESSAGE)); + itemsRemoved.Initialize(T(TITEMS_REMOVED_MESSAGE)); + itemsAddedPrivate.Initialize(T(TITEMS_ADDED_VEROBSE_MESSAGE)); + itemsRemovedPrivate.Initialize(T(TITEMS_REMOVED_VERBOSE_MESSAGE)); + itemsAdditionFailed.Initialize(T(TITEMS_ADDITION_FAILED_MESSAGE)); + itemsRemovalFailed.Initialize(T(TITEMS_REMOVAL_FAILED_MESSAGE)); */ + /** * Command that outputs summary of the player's inventory. * @@ -540,6 +627,7 @@ public final function ReportInventory(ConsoleWriter writer, bool includeHidden) local int lineCounter; local array availableItems; local Text playerName; + if (writer == none) return; if (TargetPlayerIsInvalid()) return; @@ -585,6 +673,7 @@ private final function AppendItemInfo( local Text lineNumberAsText; local EWeapon itemAsWeapon; local Mutabletext allAmmoInfo; + if (writer == none) return; if (item == none) return; @@ -614,6 +703,7 @@ private final function MutableText DisplayAllAmmoInfo(EWeapon weapon) local int i; local array allAmmo; local MutableText builder; + allAmmo = weapon.GetAvailableAmmo(); if (allAmmo.length == 0) { return none; @@ -633,6 +723,7 @@ private final function MutableText DisplayAllAmmoInfo(EWeapon weapon) private final function AppendAmmoInstanceInfo(MutableText builder, EAmmo ammo) { local Text ammoName; + if (ammo == none) { return; } @@ -647,10 +738,12 @@ private final function AppendAmmoInstanceInfo(MutableText builder, EAmmo ammo) defaultproperties { - TITEMS_ADDED_MESSAGE = 0 - stringConstants(0) = "%%instigator%% {$TextPositive added} following weapons to %%target%%:" - TITEMS_ADDED_VEROBSE_MESSAGE = 1 - stringConstants(1) = "Weapons {$TextPositive added} to %%target%%:" + // TODO remove old constants + // TODO refactor `ReportChanges()` + TINSTIGATOR = 0 + stringConstants(0) = "%%instigator%%" + TTARGET = 1 + stringConstants(1) = "%%target%%" TITEMS_REMOVED_MESSAGE = 2 stringConstants(2) = "%%instigator%% {$TextNegative removed} following weapons from %%target%%:" TITEMS_REMOVED_VERBOSE_MESSAGE = 3 diff --git a/sources/Tools/ListBuilder.uc b/sources/Tools/ListBuilder.uc new file mode 100644 index 0000000..5cd6fc5 --- /dev/null +++ b/sources/Tools/ListBuilder.uc @@ -0,0 +1,241 @@ +/** + * Many different Futility's commands need to output lists of items + * separated by commas, each item possibly containing comments in + * the parentheses (also separated by commas). This class provides necessary + * functionality. + * Copyright 2022 Anton Tarasenko + *------------------------------------------------------------------------------ + * This file is part of Acedia. + * + * Acedia is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License, or + * (at your option) any later version. + * + * Acedia is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Acedia. If not, see . + */ +class ListBuilder extends AcediaObject; + +/** + * # `ListBuilder` + * + * Many different Futility's commands need to output lists of items + * separated by commas, each item possibly containing comments in + * the parentheses (also separated by commas). This class provides necessary + * functionality. + * Example of such list: + * "item1, item2 (comment1, comment2), item3 (just_comment), item4, item5". + * + * ## Usage + * + * 1. Use `Item()` method to add new items (they will be listed after + * list header + whitespace, separated by commas and whitespaces ", "); + * 2. Use `Comment()` method to specify comments for the item (they will + * be listed between the paranthesisasd after the corresponding item). + * Comments will be added to the last item, added via `Item()` call. + * If no items were added, specified comment will be discarded. + * 3. Use `Get()` / `GetMutable()` methods to return list built so far. + * 4. Use `Reset()` to forget all the items and comments + * (but not list header), allowing to start forming a new report. + */ + +// Represents one item + all of its comments +struct FutilityListItem +{ + var Text itemTitle; + var array comments; +}; +// All items recorded reported thus far +var private array collectedItems; + +var const int TCAUSE, TTARGET, TCOMMA, TSPACE, TCOMMA_SPACE; +var const int TSPACE_OPEN_PARANSIS, TCLOSE_PARANSIS; + +protected function Finalizer() +{ + Reset(); +} + +/** + * Adds new `item` to the current report. + * + * @param item Text to be included into the report as an item. + * One should avoid using commas or parantheses inside an `item`, but + * this limitation is not checked or prevented by `Item()` method. + * Does nothing if `item == none` (`Comment()` will continue adding + * comments to the previously added item). + * @return Reference to the caller `ListBuilder` to allow for method chaining. + */ +public final function ListBuilder Item(BaseText item) +{ + local FutilityListItem newItem; + + if (item == none) { + return self; + } + newItem.itemTitle = item.Copy(); + collectedItems[collectedItems.length] = newItem; + return self; +} + +/** + * Adds new `comment` to the last added `item` in the current report. + * + * @param comment Text to be included into the report as a comment to + * the last added item. One should avoid using commas or parantheses inside + * a `comment`, but this limitation is not checked or prevented by + * `Comment()` method. + * Does nothing if `comment == none` or no items were added thuis far. + * @return Reference to the caller `ListBuilder` to allow for method chaining. + */ +public final function ListBuilder Comment(BaseText comment) +{ + local array itemComments; + + if (comment == none) return self; + if (collectedItems.length == 0) return self; + + itemComments = collectedItems[collectedItems.length - 1].comments; + itemComments[itemComments.length] = comment.Copy(); + collectedItems[collectedItems.length - 1].comments = itemComments; + return self; +} + +/** + * Returns list, assembled from items and their comment specified so far as + * `Text`. + * + * @see `GetMutable()`, `IntoText()`, `IntoMutableText()` + * + * @return Assembled list of specified items with specified comments. + */ +public final function Text Get() +{ + local MutableText mutableResult; + + mutableResult = GetMutable(); + if (mutableResult != none) { + return mutableResult.IntoText(); + } + return none; +} + +/** + * Returns list, assembled from items and their comment specified so far as + * `MutableText`. + * + * @see `Get()`, `IntoText()`, `IntoMutableText()` + * + * @return Assembled list of specified items with specified comments. + */ +public final function MutableText GetMutable() +{ + local int i, j; + local MutableText result; + local array itemComments; + + if (collectedItems.length == 0) { + return _.text.Empty(); + } + result = _.text.Empty(); + for (i = 0; i < collectedItems.length; i += 1) + { + if (i > 0) { + result.Append(T(TCOMMA_SPACE)); + } + result.Append(collectedItems[i].itemTitle); + itemComments = collectedItems[i].comments; + if (itemComments.length > 0) { + result.Append(T(TSPACE_OPEN_PARANSIS)); + } + for (j = 0; j < itemComments.length; j += 1) + { + if (j > 0) { + result.Append(T(TCOMMA_SPACE)); + } + result.Append(itemComments[j]); + } + if (itemComments.length > 0) { + result.Append(T(TCLOSE_PARANSIS)); + } + } + return result; +} + +/** + * Converts caller `Listbuilder` into list, assembled from items and their + * comment specified so far as `Text`. + * Caller `ListBuilder()` is atuomatically deallocated. + * + * @see `Get()`, `GetMutable()`, `IntoMutableText()` + * + * @return Assembled list of specified items with specified comments. + */ +public final function Text IntoText() +{ + local Text result; + + result = Get(); + FreeSelf(); + return result; +} + +/** + * Converts caller `Listbuilder` into list, assembled from items and their + * comment specified so far as `MutableText`. + * Caller `ListBuilder()` is atuomatically deallocated. + * + * @see `Get()`, `GetMutable()`, `IntoText()` + * + * @return Assembled list of specified items with specified comments. + */ +public final function MutableText IntoMutableText() +{ + local MutableText result; + + result = GetMutable(); + FreeSelf(); + return result; +} + +/** + * Forgets all items or comments specified for the caller `ListBuilder` so far, + * allowing to start forming a new report. Does not reset template header, + * specified in the `Initialize()` method. + * + * @return Reference to the caller `ListBuilder` to allow for method chaining. + */ +public final function ListBuilder Reset() +{ + local int i; + + for (i = 0; i < collectedItems.length; i += 1) + { + _.memory.Free(collectedItems[i].itemTitle); + _.memory.FreeMany(collectedItems[i].comments); + } + collectedItems.length = 0; + return self; +} + +defaultproperties +{ + TCAUSE = 0 + stringConstants(0) = "%%instigator%%" + TTARGET = 1 + stringConstants(1) = "%%target%%" + TCOMMA = 2 + stringConstants(2) = "," + TCOMMA_SPACE = 3 + stringConstants(3) = ", " + TSPACE_OPEN_PARANSIS = 4 + stringConstants(4) = " (" + TCLOSE_PARANSIS = 5 + stringConstants(5) = ")" +} \ No newline at end of file diff --git a/sources/Tools/ReportTool.uc b/sources/Tools/ReportTool.uc deleted file mode 100644 index 687dda4..0000000 --- a/sources/Tools/ReportTool.uc +++ /dev/null @@ -1,252 +0,0 @@ -/** - * Auxiliary object for outputting lists of values (with optional comments) - * for Futility's commands. Some of the commands need to report that one of - * the player did something to affect the other and then list the changes. - * This tool is made to simplify forming such reports. - * Produced reports have a form of ": - * item1 (detail), item2, item3 (detail1, detail2)". - * Copyright 2022 Anton Tarasenko - *------------------------------------------------------------------------------ - * This file is part of Acedia. - * - * Acedia is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3 of the License, or - * (at your option) any later version. - * - * Acedia is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Acedia. If not, see . - */ -class ReportTool extends AcediaObject; - -/** - * How to use: - * 1. Specify "list header" via `Initialize()` method right after creating - * a new instance of `ReportTool`. It can contain "%%instigator%%" and - * "%%target%%" substrings, that will be replaces with approprtiate - * parameters of `Report()` method when it is invoked; - * 2. Use `Item()` method to add new items (they will be listed after - * list header + whitespace, separated by commas and whitespaces ", "); - * 3. Use `Detail()` method to specify details for the item (they will be - * listed between the paranthesisasd after the corresponding item). - * Details will be added to the last item, added via `Item()` call. - * If no items were added, specified details will be discarded. - * 4. Use `Report()` method to feed the `ConsoleWriter` with report that - * has been assebled so far. - * 5. Use `Reset()` to forget all the items and details - * (but not list header), allowing to start forming a new report. - */ - -// Header template (with possible "%%instigator%%" and "%%target%%" -// placeholders) for the lists this `ReportTool` will generate. -// Doubles as a way to remember whether `ReportTool` was already -// initialized (iff `headerTemplate != none`). -var private Text headerTemplate; - -// Represents one item + all of its details. -struct ReportItem -{ - var Text itemTitle; - var array details; -}; -// All items recorded reported thus far -var private array itemsToReport; - -var const int TCAUSE, TTARGET, TCOMMA, TSPACE, TSPACE_OPEN_PARANSIS; -var const int TCLOSE_PARANSIS; - -protected function Finalizer() -{ - Reset(); - _.memory.Free(headerTemplate); - headerTemplate = none; -} - -/** - * Initialized a new `ReportTool` with appropriate template to serve as - * a header. - * - * Template (`template`) is allowed to contain "%%instigator%%" and - * "%%target%%" placeholder substrings that will be replaced with corresponding - * names of the player that caused a change we are reporting and player - * affefcted by that change. - * - * @param template Template for the header of the reports made by - * the caller `ReportTool`. - * Method does nothing (initialization fails) iff `template == none`. - */ -public final function Initialize(BaseText template) -{ - if (template == none) { - return; - } - headerTemplate = template.Copy(); -} - -/** - * Adds new `item` to the current report. - * - * @param item Text to be included into the report as an item. - * One should avoid using commas or parantheses inside an `item`, but - * this limitation is not checked or prevented by `Item()` method. - * Does nothing if `item == none` (`Detail()` will continue adding details - * to the previously added item). - * @return Reference to the caller `ReportTool` to allow for method chaining. - */ -public final function ReportTool Item(BaseText item) -{ - local ReportItem newItem; - - if (headerTemplate == none) return self; - if (item == none) return self; - - newItem.itemTitle = item.Copy(); - itemsToReport[itemsToReport.length] = newItem; - return self; -} - -/** - * Adds new `detail` to the last added `item` in the current report. - * - * @param detail Text to be included into the report as a detail to - * the last added item. One should avoid using commas or parantheses inside - * a `detail`, but this limitation is not checked or prevented by - * `Detail()` method. - * Does nothing if `detail == none` or no items were added thuis far. - * @return Reference to the caller `ReportTool` to allow for method chaining. - */ -public final function ReportTool Detail(BaseText detail) -{ - local array detailToReport; - - if (headerTemplate == none) return self; - if (detail == none) return self; - if (itemsToReport.length == 0) return self; - - detailToReport = itemsToReport[itemsToReport.length - 1].details; - detailToReport[detailToReport.length] = detail.Copy(); - itemsToReport[itemsToReport.length - 1].details = detailToReport; - return self; -} - -/** - * Outputs report assembled thus far into the provided `ConsoleWriter`. - * Reports will be made only if at least one items was added (see `Item()`). - * - * @param writer `ConsoleWriter` to output report into. - * @param instigator Player that caused the change this report is about. - * Their name will replace "%%instigator%%" substring in the header - * template (if it is contained there). - * @param target Player that was affected by the change this report is - * about. Their name will replace "%%target%%" substring in the header - * template (if it is contained there). - * @return Reference to the caller `ReportTool` to allow for method chaining. - */ -public final function ReportTool Report( - ConsoleWriter writer, - optional BaseText instigator, - optional BaseText target) -{ - local int i, j; - local array detailToReport; - - if (headerTemplate == none) return self; - if (itemsToReport.length == 0) return self; - if (writer == none) return self; - - AppendHeader(writer, instigator, target); - for (i = 0; i < itemsToReport.length; i += 1) - { - if (i > 0) { - writer.Write(T(TCOMMA)); - } - writer.Write(T(TSPACE)).Write(itemsToReport[i].itemTitle); - detailToReport = itemsToReport[i].details; - if (detailToReport.length > 0) { - writer.Write(T(TSPACE_OPEN_PARANSIS)); - } - for (j = 0; j < detailToReport.length; j += 1) - { - if (j > 0) { - writer.Write(P(", ")); - } - writer.Write(detailToReport[j]); - } - if (detailToReport.length > 0) { - writer.Write(T(TCLOSE_PARANSIS)); - } - } - writer.Flush(); - return self; -} - -private final function AppendHeader( - ConsoleWriter writer, - optional BaseText instigator, - optional BaseText target) -{ - local MutableText intro; - local MutableText grayInstigatorName, grayTargetName; - - if (headerTemplate == none) return; - if (writer == none) return; - - if (instigator != none) - { - grayInstigatorName = instigator - .MutableCopy() - .ChangeDefaultColor(_.color.Gray); - } - if (target != none) { - grayTargetName = target.MutableCopy().ChangeDefaultColor(_.color.Gray); - } - intro = headerTemplate.MutableCopy() - .Replace(T(TCAUSE), grayInstigatorName) - .Replace(T(TTARGET), grayTargetName); - writer.Flush().Write(intro); - _.memory.Free(intro); - _.memory.Free(grayTargetName); - _.memory.Free(grayInstigatorName); -} - -/** - * Forgets all items or details specified for the caller `ReportTool` so far, - * allowing to start forming a new report. Does not reset template header, - * specified in the `Initialize()` method. - * - * @return Reference to the caller `ReportTool` to allow for method chaining. - */ -public final function ReportTool Reset() -{ - local int i; - for (i = 0; i < itemsToReport.length; i += 1) - { - _.memory.Free(itemsToReport[i].itemTitle); - _.memory.FreeMany(itemsToReport[i].details); - } - if (itemsToReport.length > 0) { - itemsToReport.length = 0; - } - return self; -} - -defaultproperties -{ - TCAUSE = 0 - stringConstants(0) = "%%instigator%%" - TTARGET = 1 - stringConstants(1) = "%%target%%" - TCOMMA = 2 - stringConstants(2) = "," - TSPACE = 3 - stringConstants(3) = " " - TSPACE_OPEN_PARANSIS = 4 - stringConstants(4) = " (" - TCLOSE_PARANSIS = 5 - stringConstants(5) = ")" -} \ No newline at end of file