/** * 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 . */ 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 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 MakeArray( AnnouncementVariations variations) { local array 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.Say(result); _.memory.Free(result); } defaultproperties { }