/** * Author: dkanus * Home repo: https://www.insultplayers.ru/git/AcediaFramework/AcediaCore * License: GPL * Copyright 2023 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 ACommandVote extends Command dependson(CommandAPI) dependson(VotingModel); var private CommandDataBuilder dataBuilder; protected function Constructor() { ResetVotingInfo(); _.commands.OnVotingAdded(self).connect = AddVotingInfo; _.commands.OnVotingRemoved(self).connect = HandleRemovedVoting; _.chat.OnVoiceMessage(self).connect = VoteWithVoice; } protected function Finalizer() { super.Finalizer(); _.memory.Free(dataBuilder); dataBuilder = none; _.commands.OnVotingAdded(self).Disconnect(); _.commands.OnVotingRemoved(self).Disconnect(); _.chat.OnVoiceMessage(self).Disconnect(); } protected function BuildData(CommandDataBuilder builder) { builder.Group(P("core")); builder.Summary(P("Allows players to initiate any available voting." @ "Voting options themselves are specified as sub-commands.")); builder.Describe(P("Default command simply displaces information about current vote.")); dataBuilder.SubCommand(P("yes")); builder.Describe(P("Vote `yes` on the current vote.")); dataBuilder.SubCommand(P("no")); builder.Describe(P("Vote `no` on the current vote.")); builder.Option(P("force")); builder.Describe(P("Tries to force voting to end immediately with the desired result.")); } protected function Executed( CallData arguments, EPlayer instigator, CommandPermissions permissions ) { local bool forcingVoting; local VotingModel.ForceEndingType forceType; local Voting currentVoting; forcingVoting = arguments.options.HasKey(P("force")); currentVoting = _.commands.GetCurrentVoting(); if (arguments.subCommandName.IsEmpty()) { DisplayInfoAboutVoting(instigator, currentVoting); } else if (arguments.subCommandName.Compare(P("yes"), SCASE_INSENSITIVE)) { CastVote(currentVoting, instigator, true); forceType = FET_Success; } else if (arguments.subCommandName.Compare(P("no"), SCASE_INSENSITIVE)) { CastVote(currentVoting, instigator, false); forceType = FET_Failure; } else if (StartVoting(arguments, currentVoting, instigator)) { _.memory.Free(currentVoting); currentVoting = _.commands.GetCurrentVoting(); forceType = FET_Success; } else { forcingVoting = false; } if (currentVoting != none && !currentVoting.HasEnded() && forcingVoting) { if (currentVoting.ForceEnding(instigator, forceType) == FEO_Forbidden) { callerConsole .WriteLine(F("You {$TextNegative aren't allowed} to forcibly end current voting")); } } _.memory.Free(currentVoting); } private final function VoteWithVoice(EPlayer sender, ChatApi.BuiltInVoiceMessage message) { local Voting currentVoting; currentVoting = _.commands.GetCurrentVoting(); if (message == BIVM_AckYes) { CastVote(currentVoting, sender, true); } if (message == BIVM_AckNo) { CastVote(currentVoting, sender, false); } _.memory.Free(currentVoting); } /// Adds sub-command information about given voting with a given name. public final function AddVotingInfo(class processClass, Text processName) { if (processName == none) return; if (processClass == none) return; if (dataBuilder == none) return; dataBuilder.SubCommand(processName); processClass.static.AddInfo(dataBuilder); commandData = dataBuilder.BorrowData(); } public final function HandleRemovedVoting(class votingClass) { local int i; local array votingsNames; ResetVotingInfo(); // Rebuild the whole voting data votingsNames = _.commands.GetAllVotingsNames(); for (i = 0; i < votingsNames.length; i += 1) { AddVotingInfo(_.commands.GetVotingClass(votingsNames[i]), votingsNames[i]); } _.memory.FreeMany(votingsNames); } /// Clears all sub-command information added from [`Voting`]s. public final function ResetVotingInfo() { _.memory.Free(dataBuilder); dataBuilder = CommandDataBuilder(_.memory.Allocate(class'CommandDataBuilder')); BuildData(dataBuilder); commandData = dataBuilder.BorrowData(); } private final function DisplayInfoAboutVoting(EPlayer instigator, Voting currentVoting) { if (currentVoting == none) { callerConsole.WriteLine(P("No voting is active right now.")); } else { currentVoting.PrintVotingInfoFor(instigator); } } private final function CastVote(Voting currentVoting, EPlayer voter, bool voteForSuccess) { if (currentVoting != none) { currentVoting.CastVote(voter, voteForSuccess); } else { callerConsole.UseColor(_.color.TextWarning).WriteLine(P("No voting is active right now.")); } } // Assumes all arguments aren't `none`. private final function bool StartVoting( CallData arguments, Voting currentVoting, EPlayer instigator ) { local Voting newVoting; local User callerUser; local CommandAPI.VotingConfigInfo pair; local CommandAPI.StartVotingResult result; callerUser = instigator.GetIdentity(); pair = _.commands.ResolveVotingForUser(arguments.subCommandName, callerUser); _.memory.Free(callerUser); if (pair.votingClass == none) { callerConsole .UseColor(_.color.TextFailure) .Write(P("Unknown voting option \"")) .Write(arguments.subCommandName) .WriteLine(P("\"")); return false; } if (pair.usageForbidden) { callerConsole .UseColor(_.color.TextFailure) .Write(P("You aren't allowed to start \"")) .Write(arguments.subCommandName) .WriteLine(P("\" voting")); return false; } result = _.commands.StartVoting(pair, arguments.parameters); Log("Result:" @ result); // Handle errors. // `SVR_UnknownVoting` is impossible, since we've already checked that // `pair.votingClass != none`) if (result == SVR_AlreadyInProgress) { callerConsole .UseColor(_.color.TextWarning) .WriteLine(P("Another voting is already in progress!")); return false; } if (result == SVR_NoVoters) { callerConsole .UseColor(_.color.TextWarning) .WriteLine(P("There are no players eligible for that voting.")); return false; } // Cast a vote from instigator newVoting = _.commands.GetCurrentVoting(); if (newVoting != none) { newVoting.CastVote(instigator, true); } else { callerConsole .UseColor(_.color.TextFailure) .WriteLine(P("Voting should be available, but it isn't." @ "This is unexpected, something broke terribly.")); _.memory.Free(newVoting); return false; } _.memory.Free(newVoting); return true; } defaultproperties { preferredName = "vote" }