Anton Tarasenko
2 years ago
6 changed files with 437 additions and 348 deletions
@ -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 <https://www.gnu.org/licenses/>. |
||||
*/ |
||||
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<Text> comments; |
||||
}; |
||||
// All items recorded reported thus far |
||||
var private array<FutilityListItem> 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<Text> 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<Text> 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) = ")" |
||||
} |
@ -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 "<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 "%%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<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 "%%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<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`. |
||||
* 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<Text> 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) = ")" |
||||
} |
Loading…
Reference in new issue