Administration tools: commands and non gameplay server configuration
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

282 lines
10 KiB

/**
* Simple class to simplify announcements of changes made by Futility's
* commands. Allows to announnce different messages to self, target and others;
* changing them up when self coincides with target.
* 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 CommandAnnouncer extends AcediaObject;
/**
* # `CommandAnnouncer`
*
* Simple class to simplify announcements of changes made by Futility's
* commands. Allows to announnce different messages to self, target and others;
* changing them up when self coincides with target.
* Technically only for reporting successes, since failures are only
* reported to the instigator and are, therefore, simple. But for the sake of
* consistency, every report can be placed here.
*
* ## Usage
*
* There is supposed to be a separate announcer class for every command class,
* so there's two steps to setting one up: creating new class and putting it
* to proper use.
*
* ### Creating new `CommandAnnouncer` class
*
* Step by step the process is:
* 1. Declare a new class, extending `CommandAnnouncer`;
* 2. Declare iinside `AnnouncementVariations` field variables for every
* announcement;
* 3. Declare a `Finalizer()` and place a `FreeVariations(...);` line for
* each announcement variable inside. Don't forget to later also make
* a `super.Finalizer();` call;
* 4. For every announcement make a separate method that accepts values to
* be included in that announcement as arguments.
* 5. Inside that method check whether corresponding announcement variable
* was already initialized (`initialized` field in side
* `AnnouncementVariations` struct) and otherwise initialize all
* contained `TextTemplate`s.
* 6. Fill every template with passed arguments ("instigator" and "target"
* arguments are auto-filled later). For that you can use auxiliary
* method `MakeArray()`, e.g.
* ```unrealscript
* local int i;
* local array<TextTemplate> templates;
* // ...
* templates = MakeArray(gainedDosh);
* for (i = 0; i < templates.length; i += 1) {
* templates[i].Reset().ArgInt(doshAmount);
* }
* ```
* 7. Make a `MakeAnnouncement()`, passing it `AnnouncementVariations` that
* you've just (initialized and) filled with arguments.
*
* ### Using created `CommandAnnouncer` class
*
* Simply allocate variable of that class, make a `Setup()` call inside
* `Executed()` and `ExecutedFor()` (only necessary to do in the one you are
* using).
* Since it is way more efficient to only allocate such variable once
* (avoiding creating templates every time), it is recommended that you create
* it inside `BuildData()` call and remember that instance in a field variable.
* You then simply need to declare command's finalizer to deallocate it.
* Just don't forget to call `super.Finalizer()` as well.
*/
var private EPlayer instigator, target;
var private int instigatorLifeVersion, targetLifeVersion;
var private ConsoleWriter publicConsole;
var private int publicConsoleLifeVersion;
var private MutableText instigatorName, targetName;
struct AnnouncementVariations
{
var public bool initialized;
// `toSelf...` == command's instigator is targeting himself/herself;
// `toOther...` == command's instigator is targeting somebody else;
// `...report` == message is for a report to command's instigator;
// `...private` == message is for a report to command's target;
// `...public` == message is for a report to eveyone who isn't
// an instigator or a target.
var public TextTemplate toSelfReport;
var public TextTemplate toSelfPublic;
var public TextTemplate toOtherReport;
var public TextTemplate toOtherPrivate;
var public TextTemplate toOtherPublic;
};
protected function Finalizer()
{
instigator = none;
target = none;
publicConsole = none;
_.memory.Free(instigatorName);
_.memory.Free(targetName);
instigatorName = none;
targetName = none;
}
/**
* Prepares caller `CommandAnnouncer` to make announcements about
* `newInstigator` player affecting `newTarget` player.
*
* @param newTarget Player that is targeted by the command.
* @param newInstigator Player that is calling the command, can be
* the same as `newTarget`.
* @param newPublicConsole Console instance to announce command's action to
* other (directly unaffected) players.
*/
public final function Setup(
EPlayer newTarget,
EPlayer newInstigator,
ConsoleWriter newPublicConsole)
{
target = none;
_.memory.Free(targetName);
targetName = none;
if (newTarget != none && newTarget.IsAllocated())
{
target = newTarget;
targetLifeVersion = newTarget.GetLifeVersion();
targetName = target
.GetName()
.IntoMutableText()
.ChangeDefaultColor(_.color.LightGray);
}
instigator = none;
_.memory.Free(instigatorName);
instigatorName = none;
if (newInstigator != none && newInstigator.IsAllocated())
{
instigator = newInstigator;
instigatorLifeVersion = newInstigator.GetLifeVersion();
instigatorName = instigator
.GetName()
.IntoMutableText()
.ChangeDefaultColor(_.color.LightGray);
}
publicConsole = none;
if (newPublicConsole != none && newPublicConsole.IsAllocated())
{
publicConsole = newPublicConsole;
publicConsoleLifeVersion = newPublicConsole.GetLifeVersion();
}
}
/**
* Makes appropriate announcements from `variations` to appropriate targets.
*
* @param variations Struct with announcement templates to make.
*/
protected final function MakeAnnouncement(AnnouncementVariations variations)
{
local ConsoleWriter instigatorConsole, targetConsole;
if (!variations.initialized) return;
if (!ValidateClasses()) return;
instigatorConsole = _.console.For(instigator);
targetConsole = _.console.For(target);
if (target == none || instigator.SameAs(target))
{
// If instigator is targeting himself, then there is no need for
// a separate announcement to target
AnnounceTemplate(instigatorConsole, variations.toSelfReport);
AnnounceTemplate(publicConsole, variations.toSelfPublic);
}
else
{
// Otherwise report to three different targets
AnnounceTemplate(instigatorConsole, variations.toOtherReport);
AnnounceTemplate(targetConsole, variations.toOtherPrivate);
AnnounceTemplate(publicConsole, variations.toOtherPublic);
}
}
/**
* Auxiliary method to free all objects inside given `AnnouncementVariations`
* struct.
*
* @param variations Struct, whos contained objects methodf should free.
*/
protected final function FreeVariations(out AnnouncementVariations variations)
{
_.memory.Free(variations.toSelfReport);
_.memory.Free(variations.toSelfPublic);
_.memory.Free(variations.toOtherReport);
_.memory.Free(variations.toOtherPrivate);
_.memory.Free(variations.toOtherPublic);
variations.toSelfReport = none;
variations.toSelfPublic = none;
variations.toOtherReport = none;
variations.toOtherPrivate = none;
variations.toOtherPublic = none;
variations.initialized = false;
}
/**
* Auxiliary method to put all `TextTemplate`s inside `variations` into
* an array that can then be easily iterated over.
*/
protected final function array<TextTemplate> MakeArray(
AnnouncementVariations variations)
{
local array<TextTemplate> result;
if (variations.toSelfReport != none) {
result[result.length] = variations.toSelfReport;
}
if (variations.toSelfPublic != none) {
result[result.length] = variations.toSelfPublic;
}
if (variations.toOtherReport != none) {
result[result.length] = variations.toOtherReport;
}
if (variations.toOtherPrivate != none) {
result[result.length] = variations.toOtherPrivate;
}
if (variations.toOtherPublic != none) {
result[result.length] = variations.toOtherPublic;
}
return result;
}
private final function bool ValidateClasses()
{
if (instigator == none) return false;
if (publicConsole == none) return false;
if (instigator.GetLifeVersion() != instigatorLifeVersion) return false;
if (!instigator.IsExistent()) return false;
if (target != none)
{
if ( target.GetLifeVersion() != targetLifeVersion
|| !target.IsExistent())
{
target = none;
}
}
if (publicConsole.GetLifeVersion() != publicConsoleLifeVersion) {
return false;
}
return true;
}
private final function AnnounceTemplate(
ConsoleWriter writer,
TextTemplate template)
{
local MutableText result;
if (writer == none) return;
if (template == none) return;
if (!template.IsInitialized()) return;
template
.TextArg(P("instigator"), instigatorName)
.TextArg(P("target"), targetName);
result = template.CollectFormattedM();
writer.Write(result).Flush();
_.memory.Free(result);
}
defaultproperties
{
}