Anton Tarasenko
2 years ago
3 changed files with 1101 additions and 0 deletions
@ -0,0 +1,220 @@ |
|||||||
|
/** |
||||||
|
* Command for managing (displaying + adding and removing to/items from it) |
||||||
|
* player's inventory. |
||||||
|
* Copyright 2021 - 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 <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class ACommandInventory extends Command; |
||||||
|
|
||||||
|
var protected const int TINVENTORY, TADD, TREMOVE, TITEMS, TEQUIP, TALL, TKEEP; |
||||||
|
var protected const int THIDDEN, TFORCE, TAMMO, TALL_WEAPONS; |
||||||
|
|
||||||
|
protected function BuildData(CommandDataBuilder builder) |
||||||
|
{ |
||||||
|
builder.Name(T(TINVENTORY)) |
||||||
|
.Summary(P("Manages player's inventory.")) |
||||||
|
.Describe(P("Command for displaying and editing players' inventories." |
||||||
|
@ "If called without specifying subcommand - simply displays" |
||||||
|
@ "targeted player's inventory.")); |
||||||
|
builder.RequireTarget(); |
||||||
|
builder.SubCommand(T(TADD)) |
||||||
|
.OptionalParams() |
||||||
|
.ParamTextList(T(TITEMS)) |
||||||
|
.Describe(P("This command adds items (based on listed templates) to" |
||||||
|
@ "the targeted player's inventory." |
||||||
|
@ "Instead of templates item aliases can be specified.")); |
||||||
|
builder.SubCommand(T(TREMOVE)) |
||||||
|
.OptionalParams() |
||||||
|
.ParamTextList(T(TITEMS)) |
||||||
|
.Describe(P("This command removes items (based on listed templates)" |
||||||
|
@ "from the targeted player's inventory." |
||||||
|
@ "Instead of templates item aliases can be specified.")); |
||||||
|
builder.Option(T(TEQUIP)) |
||||||
|
.Describe(F("Affect items currently equipped by the targeted player." |
||||||
|
@ "Releveant for a {$TextEmphasis remove} subcommand.")); |
||||||
|
builder.Option(T(TALL)) |
||||||
|
.Describe(F("This flag tells editing commands to affect all items." |
||||||
|
@ "When adding items it means \"all available weapons in the game\"" |
||||||
|
@ "and when removing it means \"all weapons in" |
||||||
|
@ "the player's inventory\".")); |
||||||
|
builder.Option(T(TKEEP)) |
||||||
|
.Describe(F("Removing items by default means simply destroying them." |
||||||
|
@ "This flag makes command to try and keep them in some form." |
||||||
|
@ "Success for all items is not guaranteed.")); |
||||||
|
builder.Option(T(THIDDEN)) |
||||||
|
.Describe(F("Some of the items in the inventory are" |
||||||
|
@ "{$TextEmphasis hidden} and are not supposed to be seem by" |
||||||
|
@ "the player. To avoid weird behavior, {$TextEmphasis inventory}" |
||||||
|
@ "command by default ignores them when affecting groups of items" |
||||||
|
@ "(like when removing all items) unless they're directly" |
||||||
|
@ "specified. This flag tells it to also affect hidden items.")); |
||||||
|
builder.Option(T(TFORCE)) |
||||||
|
.Describe(P("Sometimes adding and removing items is impossible due to" |
||||||
|
@ "the limitations imposed by the game. This option allows to" |
||||||
|
@ "ignore some of those limitation.")); |
||||||
|
builder.Option(T(TAMMO), P("A")) |
||||||
|
.Describe(P("When adding weapons - signals that their" |
||||||
|
@ "ammo / charge / whatever has to be filled after addition.")); |
||||||
|
} |
||||||
|
|
||||||
|
protected function ExecutedFor( |
||||||
|
EPlayer player, |
||||||
|
CallData result, |
||||||
|
EPlayer callerPlayer) |
||||||
|
{ |
||||||
|
local ConsoleWriter publicWriter; |
||||||
|
local InventoryTool tool; |
||||||
|
tool = class'InventoryTool'.static.CreateFor(player); |
||||||
|
if (tool == none) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (result.subCommandName.IsEmpty()) |
||||||
|
{ |
||||||
|
tool.ReportInventory( callerPlayer.BorrowConsole(), |
||||||
|
result.options.HasKey(T(THIDDEN))); |
||||||
|
} |
||||||
|
else if (result.subCommandName.Compare(T(TADD))) |
||||||
|
{ |
||||||
|
SubCommandAdd( tool, result.parameters.GetDynamicArray(T(TITEMS)), |
||||||
|
result.options.HasKey(T(TALL)), |
||||||
|
result.options.HasKey(T(TFORCE)), |
||||||
|
result.options.HasKey(T(TAMMO))); |
||||||
|
} |
||||||
|
else if (result.subCommandName.Compare(T(TREMOVE))) |
||||||
|
{ |
||||||
|
SubCommandRemove( tool, |
||||||
|
result.parameters.GetDynamicArray(T(TITEMS)), |
||||||
|
result.options.HasKey(T(TALL)), |
||||||
|
result.options.HasKey(T(TFORCE)), |
||||||
|
result.options.HasKey(T(TKEEP)), |
||||||
|
result.options.HasKey(T(TEQUIP)), |
||||||
|
result.options.HasKey(T(THIDDEN))); |
||||||
|
} |
||||||
|
tool.ReportChanges(callerPlayer, player.BorrowConsole(), false); |
||||||
|
publicWriter = _.console.ForAll().ButPlayer(callerPlayer); |
||||||
|
tool.ReportChanges(callerPlayer, publicWriter, true); |
||||||
|
_.memory.Free(tool); |
||||||
|
_.memory.Free(publicWriter); |
||||||
|
} |
||||||
|
|
||||||
|
protected function SubCommandAdd( |
||||||
|
InventoryTool tool, |
||||||
|
DynamicArray templateList, |
||||||
|
bool flagAll, |
||||||
|
bool doForce, |
||||||
|
bool doFillAmmo) |
||||||
|
{ |
||||||
|
if (flagAll) { |
||||||
|
AddAllItems(tool, doForce, doFillAmmo); |
||||||
|
} |
||||||
|
else { |
||||||
|
AddGivenTemplates(tool, templateList, doForce, doFillAmmo); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function SubCommandRemove( |
||||||
|
InventoryTool tool, |
||||||
|
DynamicArray templateList, |
||||||
|
bool flagAll, |
||||||
|
bool doForce, |
||||||
|
bool doKeep, |
||||||
|
bool flagEquip, |
||||||
|
bool flagHidden) |
||||||
|
{ |
||||||
|
if (flagAll) |
||||||
|
{ |
||||||
|
tool.RemoveAllItems(doKeep, doForce, flagHidden); |
||||||
|
return; |
||||||
|
} |
||||||
|
if (flagEquip) { |
||||||
|
tool.RemoveEquippedItems(doKeep, doForce, flagHidden); |
||||||
|
} |
||||||
|
RemoveGivenTemplates(tool, templateList, doForce, doKeep); |
||||||
|
} |
||||||
|
|
||||||
|
protected function AddAllItems( |
||||||
|
InventoryTool tool, |
||||||
|
bool doForce, |
||||||
|
bool doFillAmmo) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
local array<Text> allTempaltes; |
||||||
|
if (tool == none) { |
||||||
|
return; |
||||||
|
} |
||||||
|
allTempaltes = _.kf.templates.GetItemList(T(TALL_WEAPONS)); |
||||||
|
for (i = 0; i < allTempaltes.length; i += 1) { |
||||||
|
tool.AddItem(allTempaltes[i], doForce, doFillAmmo); |
||||||
|
} |
||||||
|
_.memory.FreeMany(allTempaltes); |
||||||
|
} |
||||||
|
|
||||||
|
protected function AddGivenTemplates( |
||||||
|
InventoryTool tool, |
||||||
|
DynamicArray templateList, |
||||||
|
bool doForce, |
||||||
|
bool doFillAmmo) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
if (tool == none) return; |
||||||
|
if (templateList == none) return; |
||||||
|
|
||||||
|
for (i = 0; i < templateList.GetLength(); i += 1) { |
||||||
|
tool.AddItem(templateList.GetText(i), doForce, doFillAmmo); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected function RemoveGivenTemplates( |
||||||
|
InventoryTool tool, |
||||||
|
DynamicArray templateList, |
||||||
|
bool doForce, |
||||||
|
bool doKeep) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
if (tool == none) return; |
||||||
|
if (templateList == none) return; |
||||||
|
|
||||||
|
for (i = 0; i < templateList.GetLength(); i += 1) { |
||||||
|
tool.RemoveItem(templateList.GetText(i), doKeep, doForce); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
TINVENTORY = 0 |
||||||
|
stringConstants(0) = "inventory" |
||||||
|
TADD = 1 |
||||||
|
stringConstants(1) = "add" |
||||||
|
TREMOVE = 2 |
||||||
|
stringConstants(2) = "remove" |
||||||
|
TITEMS = 3 |
||||||
|
stringConstants(3) = "items" |
||||||
|
TEQUIP = 4 |
||||||
|
stringConstants(4) = "equip" |
||||||
|
TALL = 5 |
||||||
|
stringConstants(5) = "all" |
||||||
|
TKEEP = 6 |
||||||
|
stringConstants(6) = "keep" |
||||||
|
THIDDEN = 7 |
||||||
|
stringConstants(7) = "hidden" |
||||||
|
TFORCE = 8 |
||||||
|
stringConstants(8) = "force" |
||||||
|
TAMMO = 9 |
||||||
|
stringConstants(9) = "ammo" |
||||||
|
TALL_WEAPONS = 10 |
||||||
|
stringConstants(10) = "all weapons" |
||||||
|
} |
@ -0,0 +1,657 @@ |
|||||||
|
/** |
||||||
|
* Auxiliary object for working with player's inventory and making reports |
||||||
|
* about it. Simplifies code for inventory commands themselves by |
||||||
|
* taking care of actual item addition/removal and reporting about successes, |
||||||
|
* failures and inventory status. |
||||||
|
* This tool is supposed to be created for one player and provides wrapper |
||||||
|
* methods for his usual inventory methods that take care of information |
||||||
|
* collection about outcome of operations and then reporting on them. |
||||||
|
* 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 <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
class InventoryTool extends AcediaObject; |
||||||
|
|
||||||
|
/** |
||||||
|
* Every instance of this class is created for a particular player and that |
||||||
|
* player cannot be changed. It allows: |
||||||
|
* 1. This object allows for editing player's inventory in a way that allows |
||||||
|
* it to produce a human-readable report about the changes. |
||||||
|
* Call `AddItem()`, `RemoveItem()` or `RemoveAllItems()` and then call |
||||||
|
* `ReportChanges()` to write changes made into the `ConsoleWriter`. |
||||||
|
* 2. `ReportInventory()` summarizes player's inventory. |
||||||
|
*/ |
||||||
|
|
||||||
|
// References to player (for whom this tool was created)... |
||||||
|
var private EPlayer targetPlayer; |
||||||
|
// ...and his inventory (for easy access) |
||||||
|
var private EInventory targetInventory; |
||||||
|
|
||||||
|
/** |
||||||
|
* `ReportTool`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; |
||||||
|
* ~ two other ones (`itemsAdded` and `itemsRemoved`) make reports about |
||||||
|
* 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 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; |
||||||
|
var const int TITEM_MISSING, TITEM_NOT_REMOVABLE, TUNKNOWN, TVISIBLE; |
||||||
|
var const int TDISPLAYING_INVENTORY, THEADER_COLON, TDOT_SPACE, TCOLON_SPACE; |
||||||
|
var const int TCOMMA_SPACE, TSPACE, TOUT_OF, THIDDEN_ITEMS, TDOLLAR; |
||||||
|
|
||||||
|
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)); |
||||||
|
} |
||||||
|
|
||||||
|
protected function Finalizer() |
||||||
|
{ |
||||||
|
// Deallocate report tools |
||||||
|
_.memory.Free(itemsAdded); |
||||||
|
_.memory.Free(itemsRemoved); |
||||||
|
_.memory.Free(itemsAddedPrivate); |
||||||
|
_.memory.Free(itemsRemovedPrivate); |
||||||
|
_.memory.Free(itemsAdditionFailed); |
||||||
|
_.memory.Free(itemsRemovalFailed); |
||||||
|
itemsAdded = none; |
||||||
|
itemsRemoved = none; |
||||||
|
itemsAddedPrivate = none; |
||||||
|
itemsRemovedPrivate = none; |
||||||
|
itemsAdditionFailed = none; |
||||||
|
itemsRemovalFailed = none; |
||||||
|
// Deallocate player references |
||||||
|
_.memory.Free(targetPlayer); |
||||||
|
_.memory.Free(targetInventory); |
||||||
|
targetPlayer = none; |
||||||
|
targetInventory = none; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates new `InventoryTool` instance for a given player `target`. |
||||||
|
* |
||||||
|
* @param target Player for which to create new `InventoryTool`. |
||||||
|
* @return `InventoryTool` created for the given player - not a copy of any |
||||||
|
* preexisting instance. `none` iff `target == none` or refers to |
||||||
|
* a non-existent player. |
||||||
|
*/ |
||||||
|
public static final function InventoryTool CreateFor(EPlayer target) |
||||||
|
{ |
||||||
|
local InventoryTool newInventoryTool; |
||||||
|
if (target == none) return none; |
||||||
|
if (!target.IsExistent()) return none; |
||||||
|
|
||||||
|
newInventoryTool = |
||||||
|
InventoryTool(__().memory.Allocate(class'InventoryTool')); |
||||||
|
newInventoryTool.targetPlayer = EPlayer(target.Copy()); |
||||||
|
newInventoryTool.targetInventory = target.GetInventory(); |
||||||
|
return newInventoryTool; |
||||||
|
} |
||||||
|
|
||||||
|
// Checks whether reference to the `EPlayer` that caller `InventoryTool` was |
||||||
|
// created for is still valid. |
||||||
|
private final function bool TargetPlayerIsInvalid() |
||||||
|
{ |
||||||
|
if (targetPlayer == none) return true; |
||||||
|
if (!targetPlayer.IsExistent()) return true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Resets `InventoryTool`, forgetting about changes made with it so far. |
||||||
|
*/ |
||||||
|
public final function Reset() |
||||||
|
{ |
||||||
|
itemsAddedPrivate.Reset(); |
||||||
|
itemsRemovedPrivate.Reset(); |
||||||
|
itemsAdded.Reset(); |
||||||
|
itemsRemoved.Reset(); |
||||||
|
itemsAdditionFailed.Reset(); |
||||||
|
itemsRemovalFailed.Reset(); |
||||||
|
} |
||||||
|
|
||||||
|
// Makes "`resolvedWhat` resolved into `intoWhat`" line |
||||||
|
// In case `resolvedWhat == intoWhat` just returns copy of |
||||||
|
// original `resolvedWhat` |
||||||
|
private function MutableText MakeResolvedIntoLine( |
||||||
|
Text resolvedWhat, |
||||||
|
Text intoWhat) |
||||||
|
{ |
||||||
|
if (resolvedWhat == none) { |
||||||
|
return none; |
||||||
|
} |
||||||
|
if (_.text.IsEmpty(intoWhat) || resolvedWhat.Compare(intoWhat)) { |
||||||
|
return resolvedWhat.MutableCopy(); |
||||||
|
} |
||||||
|
return _.text.Empty() |
||||||
|
.Append(T(TTILDE_QUOTE)) |
||||||
|
.Append(resolvedWhat) |
||||||
|
.Append(T(TRESOLVED_INTO)) |
||||||
|
.Append(intoWhat) |
||||||
|
.Append(T(TTILDE_QUOTE)); |
||||||
|
} |
||||||
|
|
||||||
|
// Tries to fill ammo for the `item` in case it is a weapon |
||||||
|
private function TryFillAmmo(EItem item) |
||||||
|
{ |
||||||
|
local EWeapon itemAsWeapon; |
||||||
|
if (item == none) { |
||||||
|
return; |
||||||
|
} |
||||||
|
itemAsWeapon = EWeapon(item.As(class'EWeapon')); |
||||||
|
if (itemAsWeapon != none) |
||||||
|
{ |
||||||
|
itemAsWeapon.FillAmmo(); |
||||||
|
_.memory.Free(itemAsWeapon); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Adds a new item, based on user provided name `userProvidedName`. |
||||||
|
* |
||||||
|
* @param userProvidedName Name of the inventory, provided by the user. |
||||||
|
* If it is started with "$", then tool tried to treat it as |
||||||
|
* an alias first. If it either does not start with "$" or does not |
||||||
|
* correspond to a valid alias - it is treated as a template. |
||||||
|
* @param doForce Set to `true` if we must try to add an item |
||||||
|
* even if it normally cannot be added. |
||||||
|
* @param doFillAmmo Set to `true` if we must also fill ammo reserves |
||||||
|
* of weapons we have added to the full. |
||||||
|
*/ |
||||||
|
public function AddItem(Text userProvidedName, bool doForce, bool doFillAmmo) |
||||||
|
{ |
||||||
|
local EItem addedItem; |
||||||
|
local MutableText resolvedLine; |
||||||
|
local Text realItemName, itemTemplate, failureReason; |
||||||
|
if (TargetPlayerIsInvalid()) return; |
||||||
|
if (userProvidedName == none) return; |
||||||
|
|
||||||
|
// Get template in case alias was specified |
||||||
|
// (`itemTemplate` cannot be `none`, since `userProvidedName != none`) |
||||||
|
if (userProvidedName.StartsWith(T(TDOLLAR))) { |
||||||
|
itemTemplate = _.alias.ResolveWeapon(userProvidedName, true); |
||||||
|
} |
||||||
|
else { |
||||||
|
itemTemplate = userProvidedName.Copy(); |
||||||
|
} |
||||||
|
// The only way we can fail in a valid way is when API says we will |
||||||
|
// via `CanAddTemplateExplain()` |
||||||
|
failureReason = targetInventory |
||||||
|
.CanAddTemplateExplain(itemTemplate, doForce); |
||||||
|
if (failureReason != none) |
||||||
|
{ |
||||||
|
itemsAdditionFailed.Item(userProvidedName).Detail(failureReason); |
||||||
|
_.memory.Free(failureReason); |
||||||
|
_.memory.Free(itemTemplate); |
||||||
|
return; |
||||||
|
} |
||||||
|
// Actually try to add specified item |
||||||
|
addedItem = targetInventory.AddTemplate(itemTemplate, doForce); |
||||||
|
if (addedItem != none) |
||||||
|
{ |
||||||
|
if (doFillAmmo) { |
||||||
|
TryFillAmmo(addedItem); |
||||||
|
} |
||||||
|
realItemName = addedItem.GetName(); |
||||||
|
resolvedLine = MakeResolvedIntoLine(userProvidedName, itemTemplate); |
||||||
|
itemsAdded.Item(realItemName); |
||||||
|
itemsAddedPrivate.Item(realItemName).Detail(resolvedLine); |
||||||
|
_.memory.Free(realItemName); |
||||||
|
_.memory.Free(resolvedLine); |
||||||
|
_.memory.Free(addedItem); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
// `CanAddTemplateExplain()` told us that we should not have failed, |
||||||
|
// so complain about bad API |
||||||
|
itemsAdditionFailed.Item(userProvidedName) |
||||||
|
.Detail(T(TFAULTY_INVENTORY_IMPLEMENTATION)); |
||||||
|
} |
||||||
|
_.memory.Free(itemTemplate); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes a specified item, based on user provided name `userProvidedName`. |
||||||
|
* |
||||||
|
* @param userProvidedName Name of inventory, provided by the user. |
||||||
|
* If it is started with "$", then tool tried to treat it as |
||||||
|
* an alias first. If it either does not start with "$" or does not |
||||||
|
* correspond to a valid alias - it is treated as a template. |
||||||
|
* @param doKeep Set to `true` if item should be preserved |
||||||
|
* (or, at least, attempted to be preserved) and not simply destroyed. |
||||||
|
* @param doForce Set to `true` if we must try to remove an item |
||||||
|
* even if it normally cannot be removed. |
||||||
|
*/ |
||||||
|
public function RemoveItem(Text userProvidedName, bool doKeep, bool doForce) |
||||||
|
{ |
||||||
|
local bool itemWasMissing; |
||||||
|
local Text realItemName, itemTemplate; |
||||||
|
local MutableText resolvedLine; |
||||||
|
local EItem storedItem; |
||||||
|
if (TargetPlayerIsInvalid()) return; |
||||||
|
if (userProvidedName == none) return; |
||||||
|
|
||||||
|
// Get template in case alias was specified |
||||||
|
// (`itemTemplate` cannot be `none`, since `userProvidedName != none`) |
||||||
|
if (userProvidedName.StartsWith(T(TDOLLAR))) { |
||||||
|
itemTemplate = _.alias.ResolveWeapon(userProvidedName, true); |
||||||
|
} |
||||||
|
else { |
||||||
|
itemTemplate = userProvidedName.Copy(); |
||||||
|
} |
||||||
|
// Check if item is even in the inventory |
||||||
|
storedItem = targetInventory.GetTemplateItem(itemTemplate); |
||||||
|
if (storedItem == none) |
||||||
|
{ |
||||||
|
// If not, we still need to attempt to remove it, as it can be |
||||||
|
// "merged" into another item |
||||||
|
itemWasMissing = true; |
||||||
|
realItemName = P("").Copy(); |
||||||
|
} |
||||||
|
else { |
||||||
|
// Need to remember the name before removing the item |
||||||
|
realItemName = storedItem.GetName(); |
||||||
|
} |
||||||
|
if (targetInventory.RemoveTemplate(itemTemplate, doKeep, doForce)) |
||||||
|
{ |
||||||
|
resolvedLine = MakeResolvedIntoLine(userProvidedName, itemTemplate); |
||||||
|
itemsRemoved.Item(realItemName); |
||||||
|
itemsRemovedPrivate.Item(realItemName).Detail(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)); |
||||||
|
} |
||||||
|
else if (!doForce && !storedItem.IsRemovable()) // simply was not removable |
||||||
|
{ |
||||||
|
itemsRemovalFailed.Item(userProvidedName) |
||||||
|
.Detail(T(TITEM_NOT_REMOVABLE)); |
||||||
|
} |
||||||
|
else { // no idea about the reason |
||||||
|
itemsRemovalFailed.Item(userProvidedName).Detail(T(TUNKNOWN)); |
||||||
|
} |
||||||
|
_.memory.Free(storedItem); |
||||||
|
_.memory.Free(realItemName); |
||||||
|
_.memory.Free(itemTemplate); |
||||||
|
} |
||||||
|
|
||||||
|
// Auxiliary method for detecting and reporting about removed items by |
||||||
|
/// comparing lists of `EItem` interfeaces created beofer and after removal |
||||||
|
private function DetectAndReportRemovedItems( |
||||||
|
out array<EItem> itemsAfterRemoval, |
||||||
|
array<EItem> itemsBeforeRemoval, |
||||||
|
array<Text> itemNames, |
||||||
|
bool doForce) |
||||||
|
{ |
||||||
|
local int i, j; |
||||||
|
local bool itemWasRemoved; |
||||||
|
for (i = 0; i < itemsBeforeRemoval.length; i += 1) |
||||||
|
{ |
||||||
|
itemWasRemoved = true; |
||||||
|
// If item was not destroyed - double check whether it got removed |
||||||
|
if (itemsBeforeRemoval[i].IsExistent()) |
||||||
|
{ |
||||||
|
for (j = 0; j < itemsAfterRemoval.length; j += 1) |
||||||
|
{ |
||||||
|
if (itemsBeforeRemoval[i].SameAs(itemsAfterRemoval[j])) |
||||||
|
{ |
||||||
|
_.memory.Free(itemsAfterRemoval[j]); |
||||||
|
itemsAfterRemoval.Remove(j, 1); |
||||||
|
itemWasRemoved = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
if (itemWasRemoved) |
||||||
|
{ |
||||||
|
itemsRemoved.Item(itemNames[i]); |
||||||
|
itemsRemovedPrivate.Item(itemNames[i]); |
||||||
|
} |
||||||
|
else if (doForce || itemsBeforeRemoval[i].IsRemovable()) { |
||||||
|
itemsRemovalFailed.Item(itemNames[i]).Detail(T(TUNKNOWN)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes all items from the player's inventory. |
||||||
|
* |
||||||
|
* @param doKeep Set to `true` if items should be preserved |
||||||
|
* (or, at least, attempted to be preserved) and not simply destroyed. |
||||||
|
* @param doForce Set to `true` if we must try to remove an item |
||||||
|
* even if it normally cannot be removed. |
||||||
|
* @param includeHidden Set to `true` if "hidden" items should also be |
||||||
|
* targeted by this method. These are items player cannot directly see in |
||||||
|
* their inventory, usually serving some sort of technical role. |
||||||
|
*/ |
||||||
|
public function RemoveAllItems(bool doKeep, bool doForce, bool includeHidden) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
local array<Text> itemNames; |
||||||
|
local array<EItem> itemsBeforeRemoval, itemsAfterRemoval; |
||||||
|
if (TargetPlayerIsInvalid()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
// Remove all items! |
||||||
|
// Remember what items we have had before to output them and |
||||||
|
// what items we have after removal to detect what we have actually |
||||||
|
// removed. |
||||||
|
// This is necessary, since (to an extent depending on flags) |
||||||
|
// some items might not be removable. |
||||||
|
if (includeHidden) { |
||||||
|
itemsBeforeRemoval = targetInventory.GetAllItems(); |
||||||
|
} |
||||||
|
else { |
||||||
|
itemsBeforeRemoval = targetInventory.GetTagItems(T(TVISIBLE)); |
||||||
|
} |
||||||
|
for (i = 0; i < itemsBeforeRemoval.length; i += 1) { |
||||||
|
itemNames[i] = itemsBeforeRemoval[i].GetName(); |
||||||
|
} |
||||||
|
targetInventory.RemoveAll(doKeep, doForce, includeHidden); |
||||||
|
itemsAfterRemoval = targetInventory.GetAllItems(); |
||||||
|
// Figure out what items are actually gone and report about them |
||||||
|
DetectAndReportRemovedItems( itemsAfterRemoval, |
||||||
|
itemsBeforeRemoval, itemNames, |
||||||
|
doForce); |
||||||
|
_.memory.FreeMany(itemNames); |
||||||
|
_.memory.FreeMany(itemsBeforeRemoval); |
||||||
|
_.memory.FreeMany(itemsAfterRemoval); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Removes all equipped items from the player's inventory. |
||||||
|
* |
||||||
|
* @param doKeep Set to `true` if items should be preserved |
||||||
|
* (or, at least, attempted to be preserved) and not simply destroyed. |
||||||
|
* @param doForce Set to `true` if we must try to remove an item |
||||||
|
* even if it normally cannot be removed. |
||||||
|
* @param includeHidden Set to `true` if "hidden" items should also be |
||||||
|
* targeted by this method. These are items player cannot directly see in |
||||||
|
* their inventory, usually serving some sort of technical role. |
||||||
|
*/ |
||||||
|
public function RemoveEquippedItems( |
||||||
|
bool doKeep, |
||||||
|
bool doForce, |
||||||
|
bool includeHidden) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
local EItem nextItem; |
||||||
|
local Text nextItemName; |
||||||
|
local array<EItem> equippedItems; |
||||||
|
if (TargetPlayerIsInvalid()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
equippedItems = targetInventory.GetEquippedItems(); |
||||||
|
for (i = 0; i < equippedItems.length; i += 1) |
||||||
|
{ |
||||||
|
nextItem = equippedItems[i]; |
||||||
|
if (!nextItem.IsExistent()) continue; |
||||||
|
if (!includeHidden && !nextItem.HasTag(T(TVISIBLE))) continue; |
||||||
|
|
||||||
|
nextItemName = nextItem.GetName(); |
||||||
|
// Try to guess the reason we cannot remove the item |
||||||
|
if (!doForce && !nextItem.IsRemovable()) |
||||||
|
{ |
||||||
|
itemsRemovalFailed |
||||||
|
.Item(nextItemName) |
||||||
|
.Detail(T(TITEM_NOT_REMOVABLE)); |
||||||
|
} |
||||||
|
else if (!targetInventory.Remove(nextItem, doKeep, doForce)) |
||||||
|
{ |
||||||
|
itemsRemovalFailed |
||||||
|
.Item(nextItemName) |
||||||
|
.Detail(T(TUNKNOWN)); |
||||||
|
} |
||||||
|
_.memory.Free(nextItemName); |
||||||
|
nextItemName = none; |
||||||
|
} |
||||||
|
_.memory.FreeMany(equippedItems); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Reports changes made to the player's inventory so far. |
||||||
|
* |
||||||
|
* Ability to provide this reports is pretty much the main reason for |
||||||
|
* using `InventoryTool` |
||||||
|
* @param blamedPlayer Player that should be listed as the one who caused |
||||||
|
* 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. |
||||||
|
*/ |
||||||
|
public final function ReportChanges( |
||||||
|
EPlayer blamedPlayer, |
||||||
|
ConsoleWriter writer, |
||||||
|
bool publicReport) |
||||||
|
{ |
||||||
|
local Text blamedName, targetName; |
||||||
|
if (TargetPlayerIsInvalid()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
targetName = targetPlayer.GetName(); |
||||||
|
if (blamedPlayer != none) { |
||||||
|
blamedName = blamedPlayer.GetName(); |
||||||
|
} |
||||||
|
if (publicReport) |
||||||
|
{ |
||||||
|
itemsAdded.Report(writer, blamedName, targetName); |
||||||
|
itemsRemoved.Report(writer, blamedName, targetName); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
itemsAddedPrivate.Report(writer, blamedName, targetName); |
||||||
|
itemsRemovedPrivate.Report(writer, blamedName, targetName); |
||||||
|
itemsAdditionFailed.Report(writer, blamedName, targetName); |
||||||
|
itemsRemovalFailed.Report(writer, blamedName, targetName); |
||||||
|
} |
||||||
|
_.memory.Free(blamedName); |
||||||
|
_.memory.Free(targetName); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Command that outputs summary of the player's inventory. |
||||||
|
* |
||||||
|
* @param writer `ConsoleWriter` into which to output information. |
||||||
|
* Method does nothing if given `writer` is `none`. |
||||||
|
* @param includeHidden Set to `true` if "hidden" items should also be |
||||||
|
* targeted by this method. These are items player cannot directly see in |
||||||
|
* their inventory, usually serving some sort of technical role. |
||||||
|
*/ |
||||||
|
public final function ReportInventory(ConsoleWriter writer, bool includeHidden) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
local int lineCounter; |
||||||
|
local array<EItem> availableItems; |
||||||
|
local Text playerName; |
||||||
|
if (writer == none) return; |
||||||
|
if (TargetPlayerIsInvalid()) return; |
||||||
|
|
||||||
|
playerName = targetPlayer.GetName(); |
||||||
|
writer.Flush() |
||||||
|
.Write(T(TDISPLAYING_INVENTORY)) |
||||||
|
.UseColorOnce(_.color.White).Write(playerName) |
||||||
|
.Write(T(THEADER_COLON)).Flush(); |
||||||
|
lineCounter = 1; |
||||||
|
availableItems = targetInventory.GetAllItems(); |
||||||
|
// First show visible items |
||||||
|
for (i = 0; i < availableItems.length; i += 1) |
||||||
|
{ |
||||||
|
if (availableItems[i].HasTag(T(TVISIBLE))) |
||||||
|
{ |
||||||
|
AppendItemInfo(writer, availableItems[i], lineCounter); |
||||||
|
lineCounter += 1; |
||||||
|
} |
||||||
|
} |
||||||
|
// Once more pass for non-visible items, to display them at the end |
||||||
|
if (includeHidden) |
||||||
|
{ |
||||||
|
writer.Write(T(THIDDEN_ITEMS)).Flush(); |
||||||
|
for (i = 0; i < availableItems.length; i += 1) |
||||||
|
{ |
||||||
|
if (!availableItems[i].HasTag(T(TVISIBLE))) |
||||||
|
{ |
||||||
|
AppendItemInfo(writer, availableItems[i], lineCounter); |
||||||
|
lineCounter += 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
_.memory.Free(playerName); |
||||||
|
_.memory.FreeMany(availableItems); |
||||||
|
} |
||||||
|
|
||||||
|
private final function AppendItemInfo( |
||||||
|
ConsoleWriter writer, |
||||||
|
EItem item, |
||||||
|
int lineNumber) |
||||||
|
{ |
||||||
|
local Text itemName; |
||||||
|
local Text lineNumberAsText; |
||||||
|
local EWeapon itemAsWeapon; |
||||||
|
local Mutabletext allAmmoInfo; |
||||||
|
if (writer == none) return; |
||||||
|
if (item == none) return; |
||||||
|
|
||||||
|
itemName = item.GetName(); |
||||||
|
lineNumberAsText = _.text.FromInt(lineNumber); |
||||||
|
writer.Write(lineNumberAsText) |
||||||
|
.Write(T(TDOT_SPACE)) |
||||||
|
.UseColorOnce(_.color.TextEmphasis).Write(itemName); |
||||||
|
// Try to display additional ammo info if this is a weapon |
||||||
|
itemAsWeapon = EWeapon(item.As(class'EWeapon')); |
||||||
|
if (itemAsWeapon != none) |
||||||
|
{ |
||||||
|
allAmmoInfo = DisplayAllAmmoInfo(itemAsWeapon); |
||||||
|
if (allAmmoInfo != none) { |
||||||
|
writer.Write(T(TCOLON_SPACE)).Write(allAmmoInfo); |
||||||
|
} |
||||||
|
_.memory.Free(itemAsWeapon); |
||||||
|
_.memory.Free(allAmmoInfo); |
||||||
|
} |
||||||
|
writer.Flush(); |
||||||
|
_.memory.Free(itemName); |
||||||
|
_.memory.Free(lineNumberAsText); |
||||||
|
} |
||||||
|
|
||||||
|
private final function MutableText DisplayAllAmmoInfo(EWeapon weapon) |
||||||
|
{ |
||||||
|
local int i; |
||||||
|
local array<EAmmo> allAmmo; |
||||||
|
local MutableText builder; |
||||||
|
allAmmo = weapon.GetAvailableAmmo(); |
||||||
|
if (allAmmo.length == 0) { |
||||||
|
return none; |
||||||
|
} |
||||||
|
builder = _.text.Empty(); |
||||||
|
for (i = 0; i < allAmmo.length; i += 1) |
||||||
|
{ |
||||||
|
if (i > 0) { |
||||||
|
builder.Append(T(TCOMMA_SPACE)); |
||||||
|
} |
||||||
|
AppendAmmoInstanceInfo(builder, allAmmo[i]); |
||||||
|
} |
||||||
|
_.memory.FreeMany(allAmmo); |
||||||
|
return builder; |
||||||
|
} |
||||||
|
|
||||||
|
private final function AppendAmmoInstanceInfo(MutableText builder, EAmmo ammo) |
||||||
|
{ |
||||||
|
local Text ammoName; |
||||||
|
if (ammo == none) { |
||||||
|
return; |
||||||
|
} |
||||||
|
ammoName = ammo.GetName(); |
||||||
|
builder.AppendString( string(ammo.GetTotalAmount()), |
||||||
|
_.text.FormattingFromColor(_.color.TypeNumber)) |
||||||
|
.Append(T(TSPACE)).Append(ammoName).Append(T(TOUT_OF)) |
||||||
|
.AppendString( string(ammo.GetMaxTotalAmount()), |
||||||
|
_.text.FormattingFromColor(_.color.TypeNumber)); |
||||||
|
_.memory.Free(ammoName); |
||||||
|
} |
||||||
|
|
||||||
|
defaultproperties |
||||||
|
{ |
||||||
|
TITEMS_ADDED_MESSAGE = 0 |
||||||
|
stringConstants(0) = "%cause% has {$TextPositive added} following weapons to %target%:" |
||||||
|
TITEMS_ADDED_VEROBSE_MESSAGE = 1 |
||||||
|
stringConstants(1) = "Weapons {$TextPositive added} to %target%:" |
||||||
|
TITEMS_REMOVED_MESSAGE = 2 |
||||||
|
stringConstants(2) = "%cause% has {$TextNegative removed} following weapons from %target%:" |
||||||
|
TITEMS_REMOVED_VERBOSE_MESSAGE = 3 |
||||||
|
stringConstants(3) = "Weapons {$TextNegative removed} from %target%:" |
||||||
|
TITEMS_ADDITION_FAILED_MESSAGE = 4 |
||||||
|
stringConstants(4) = "Weapons we've {$TextFailure failed} to add to %target%:" |
||||||
|
TITEMS_REMOVAL_FAILED_MESSAGE = 5 |
||||||
|
stringConstants(5) = "Weapons we've {$TextFailure failed} to remove from %target%:" |
||||||
|
TRESOLVED_INTO = 6 |
||||||
|
stringConstants(6) = "` resolved into `" |
||||||
|
TTILDE_QUOTE = 7 |
||||||
|
stringConstants(7) = "`" |
||||||
|
TFAULTY_INVENTORY_IMPLEMENTATION = 8 |
||||||
|
stringConstants(8) = "faulty inventory implementation" |
||||||
|
TITEM_MISSING = 9 |
||||||
|
stringConstants(9) = "item missing" |
||||||
|
TITEM_NOT_REMOVABLE = 10 |
||||||
|
stringConstants(10) = "item not removable" |
||||||
|
TUNKNOWN = 11 |
||||||
|
stringConstants(11) = "unknown" |
||||||
|
TVISIBLE = 12 |
||||||
|
stringConstants(12) = "visible" |
||||||
|
TDISPLAYING_INVENTORY = 13 |
||||||
|
stringConstants(13) = "{$TextHeader Displaying inventory for player }" |
||||||
|
THEADER_COLON = 14 |
||||||
|
stringConstants(14) = "{$TextHeader :}" |
||||||
|
TDOT_SPACE = 15 |
||||||
|
stringConstants(15) = ". " |
||||||
|
TCOLON_SPACE = 16 |
||||||
|
stringConstants(16) = ": " |
||||||
|
TCOMMA_SPACE = 17 |
||||||
|
stringConstants(17) = ", " |
||||||
|
TSPACE = 18 |
||||||
|
stringConstants(18) = " " |
||||||
|
TOUT_OF = 19 |
||||||
|
stringConstants(19) = " out of " |
||||||
|
THIDDEN_ITEMS = 20 |
||||||
|
stringConstants(20) = "{$TextSubHeader Hidden items:}" |
||||||
|
TDOLLAR = 21 |
||||||
|
stringConstants(21) = "$" |
||||||
|
} |
@ -0,0 +1,224 @@ |
|||||||
|
/** |
||||||
|
* 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 "<list header, noting who affected who>: |
||||||
|
* 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 <https://www.gnu.org/licenses/>. |
||||||
|
*/ |
||||||
|
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 "%cause%" 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 "%cause%" 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<Text> details; |
||||||
|
}; |
||||||
|
// All items recorded reported thus far |
||||||
|
var private array<ReportItem> 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 "%cause%" 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(Text 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(Text 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(Text detail) |
||||||
|
{ |
||||||
|
local array<Text> 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`. |
||||||
|
* |
||||||
|
* @param writer `ConsoleWriter` to output report into. |
||||||
|
* @param cause Player that caused the change this report is about. |
||||||
|
* Their name will replace "%cause%" 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 Text cause, |
||||||
|
optional Text target) |
||||||
|
{ |
||||||
|
local int i, j; |
||||||
|
local MutableText intro; |
||||||
|
local array<Text> detailToReport; |
||||||
|
if (headerTemplate == none) return self; |
||||||
|
if (itemsToReport.length == 0) return self; |
||||||
|
if (writer == none) return self; |
||||||
|
|
||||||
|
intro = headerTemplate.MutableCopy() |
||||||
|
.Replace(T(TCAUSE), cause) |
||||||
|
.Replace(T(TTARGET), target); |
||||||
|
writer.Flush().Write(intro); |
||||||
|
_.memory.Free(intro); |
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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) = "%cause%" |
||||||
|
TTARGET = 1 |
||||||
|
stringConstants(1) = "%target%" |
||||||
|
TCOMMA = 2 |
||||||
|
stringConstants(2) = "," |
||||||
|
TSPACE = 3 |
||||||
|
stringConstants(3) = " " |
||||||
|
TSPACE_OPEN_PARANSIS = 4 |
||||||
|
stringConstants(4) = " (" |
||||||
|
TCLOSE_PARANSIS = 5 |
||||||
|
stringConstants(5) = ")" |
||||||
|
} |
Loading…
Reference in new issue