Restart vote improvements, voting localization.

Restart votes now need 80% majority to succeed.
Restart votes now have a 3 minute cooldown on the caller.
Voting stuff has been localized.
This commit is contained in:
Pieter-Jan Briers
2021-02-28 22:11:45 +01:00
parent 0c059fc699
commit 245f276f35
11 changed files with 145 additions and 31 deletions

View File

@@ -6,7 +6,7 @@
<VBoxContainer> <VBoxContainer>
<HBoxContainer> <HBoxContainer>
<MarginContainer MarginLeftOverride="8" HorizontalExpand="True"> <MarginContainer MarginLeftOverride="8" HorizontalExpand="True">
<Label Text="{Loc 'Call Vote'}" VAlign="Center" StyleClasses="LabelHeading" /> <Label Text="{Loc 'ui-vote-create-title'}" VAlign="Center" StyleClasses="LabelHeading" />
</MarginContainer> </MarginContainer>
<MarginContainer MarginRightOverride="8"> <MarginContainer MarginRightOverride="8">
<TextureButton Name="CloseButton" StyleClasses="windowCloseButton" <TextureButton Name="CloseButton" StyleClasses="windowCloseButton"
@@ -27,12 +27,12 @@
<MarginContainer SizeFlagsHorizontal="Fill" <MarginContainer SizeFlagsHorizontal="Fill"
MarginLeftOverride="8" MarginRightOverride="8" MarginBottomOverride="2"> MarginLeftOverride="8" MarginRightOverride="8" MarginBottomOverride="2">
<Button Name="CreateButton" Text="{Loc 'Call Vote'}" /> <Button Name="CreateButton" Text="{Loc 'ui-vote-create-button'}" />
</MarginContainer> </MarginContainer>
<PanelContainer StyleClasses="LowDivider" /> <PanelContainer StyleClasses="LowDivider" />
<MarginContainer MarginLeftOverride="12"> <MarginContainer MarginLeftOverride="12">
<Label StyleClasses="LabelSubText" Text="{Loc 'Powered by Robust™ Anti-Tamper Technology'}" /> <Label StyleClasses="LabelSubText" Text="{Loc 'ui-vote-fluff'}" />
</MarginContainer> </MarginContainer>
</VBoxContainer> </VBoxContainer>

View File

@@ -20,8 +20,8 @@ namespace Content.Client.Voting
public static readonly (string name, string id, (string name, string id)[]? secondaries)[] AvailableVoteTypes = public static readonly (string name, string id, (string name, string id)[]? secondaries)[] AvailableVoteTypes =
{ {
("Restart round", "restart", null), ("ui-vote-type-restart", "restart", null),
("Next gamemode", "preset", null) ("ui-vote-type-gamemode", "preset", null)
}; };
public VoteCallMenu() public VoteCallMenu()

View File

@@ -16,7 +16,7 @@ namespace Content.Client.Voting
{ {
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
Text = Loc.GetString("Call vote"); Text = Loc.GetString("ui-vote-menu-button");
OnPressed += OnOnPressed; OnPressed += OnOnPressed;
} }

View File

@@ -50,12 +50,12 @@ namespace Content.Client.Voting
public void UpdateData() public void UpdateData()
{ {
VoteTitle.Text = _vote.Title; VoteTitle.Text = _vote.Title;
VoteCaller.Text = Loc.GetString("{0} called a vote:", _vote.Initiator); VoteCaller.Text = Loc.GetString("ui-vote-created", ("initiator", _vote.Initiator));
for (var i = 0; i < _voteButtons.Length; i++) for (var i = 0; i < _voteButtons.Length; i++)
{ {
var entry = _vote.Entries[i]; var entry = _vote.Entries[i];
_voteButtons[i].Text = Loc.GetString("{0} ({1})", entry.Text, entry.Votes); _voteButtons[i].Text = Loc.GetString("ui-vote-button", ("text", entry.Text), ("votes", entry.Votes));
if (_vote.OurVote == i) if (_vote.OurVote == i)
_voteButtons[i].Pressed = true; _voteButtons[i].Pressed = true;

View File

@@ -1,4 +1,5 @@
using Robust.Server.Player; using System.Collections.Generic;
using Robust.Server.Player;
namespace Content.Server.Voting namespace Content.Server.Voting
{ {
@@ -10,6 +11,8 @@ namespace Content.Server.Voting
bool Finished { get; } bool Finished { get; }
bool Cancelled { get; } bool Cancelled { get; }
IReadOnlyDictionary<object, int> VotesPerOption { get; }
event VoteFinishedEventHandler OnFinished; event VoteFinishedEventHandler OnFinished;
bool IsValidOption(int optionId); bool IsValidOption(int optionId);
void CastVote(IPlayerSession session, int? optionId); void CastVote(IPlayerSession session, int? optionId);

View File

@@ -1,6 +1,7 @@
#nullable enable #nullable enable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Shared;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
@@ -15,15 +16,16 @@ namespace Content.Server.Voting
var alone = _playerManager.PlayerCount == 1 && initiator != null; var alone = _playerManager.PlayerCount == 1 && initiator != null;
var options = new VoteOptions var options = new VoteOptions
{ {
Title = Loc.GetString("Restart round"), Title = Loc.GetString("ui-vote-restart-title"),
Options = Options =
{ {
(Loc.GetString("Yes"), true), (Loc.GetString("ui-vote-restart-yes"), true),
(Loc.GetString("No"), false) (Loc.GetString("ui-vote-restart-no"), false)
}, },
Duration = alone Duration = alone
? TimeSpan.FromSeconds(10) ? TimeSpan.FromSeconds(10)
: TimeSpan.FromSeconds(30) : TimeSpan.FromSeconds(30),
InitiatorTimeout = TimeSpan.FromMinutes(3)
}; };
if (alone) if (alone)
@@ -33,23 +35,22 @@ namespace Content.Server.Voting
var vote = CreateVote(options); var vote = CreateVote(options);
vote.OnFinished += (_, args) => vote.OnFinished += (_, _) =>
{ {
if (args.Winner == null) var votesYes = vote.VotesPerOption[true];
{ var votesNo = vote.VotesPerOption[false];
_chatManager.DispatchServerAnnouncement(Loc.GetString("Restart vote failed due to tie.")); var total = votesYes + votesNo;
return;
}
var win = (bool) args.Winner; var ratioRequired = _cfg.GetCVar(CCVars.VoteRestartRequiredRatio);
if (win) if (votesYes / (float) total >= ratioRequired)
{ {
_chatManager.DispatchServerAnnouncement(Loc.GetString("Restart vote succeeded.")); _chatManager.DispatchServerAnnouncement(Loc.GetString("ui-vote-restart-succeeded"));
_ticker.RestartRound(); _ticker.RestartRound();
} }
else else
{ {
_chatManager.DispatchServerAnnouncement(Loc.GetString("Restart vote failed.")); _chatManager.DispatchServerAnnouncement(
Loc.GetString("ui-vote-restart-failed", ("ratio", ratioRequired)));
} }
}; };
@@ -64,16 +65,16 @@ namespace Content.Server.Voting
{ {
var presets = new Dictionary<string, string> var presets = new Dictionary<string, string>
{ {
["traitor"] = "Traitor", ["traitor"] = "mode-traitor",
["extended"] = "Extended", ["extended"] = "mode-extended",
["sandbox"] = "Sandbox", ["sandbox"] = "mode-sandbox",
["suspicion"] = "Suspicion" ["suspicion"] = "mode-suspicion",
}; };
var alone = _playerManager.PlayerCount == 1 && initiator != null; var alone = _playerManager.PlayerCount == 1 && initiator != null;
var options = new VoteOptions var options = new VoteOptions
{ {
Title = Loc.GetString("Next gamemode"), Title = Loc.GetString("ui-vote-gamemode-title"),
Duration = alone Duration = alone
? TimeSpan.FromSeconds(10) ? TimeSpan.FromSeconds(10)
: TimeSpan.FromSeconds(30) : TimeSpan.FromSeconds(30)
@@ -98,13 +99,13 @@ namespace Content.Server.Voting
{ {
picked = (string) IoCManager.Resolve<IRobustRandom>().Pick(args.Winners); picked = (string) IoCManager.Resolve<IRobustRandom>().Pick(args.Winners);
_chatManager.DispatchServerAnnouncement( _chatManager.DispatchServerAnnouncement(
Loc.GetString("Tie for gamemode vote! Picking... {0}", Loc.GetString(presets[picked]))); Loc.GetString("ui-vote-gamemode-tie", ("picked", Loc.GetString(presets[picked]))));
} }
else else
{ {
picked = (string) args.Winner; picked = (string) args.Winner;
_chatManager.DispatchServerAnnouncement( _chatManager.DispatchServerAnnouncement(
Loc.GetString("{0} won the gamemode vote!", Loc.GetString(presets[picked]))); Loc.GetString("ui-vote-gamemode-win", ("winner", Loc.GetString(presets[picked]))));
} }
_ticker.SetStartPreset(picked); _ticker.SetStartPreset(picked);

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
@@ -10,11 +11,13 @@ using Content.Shared.Administration;
using Content.Shared.Network.NetMessages; using Content.Shared.Network.NetMessages;
using Content.Shared.Utility; using Content.Shared.Utility;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Localization; using Robust.Shared.Localization;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility;
#nullable enable #nullable enable
@@ -23,6 +26,7 @@ namespace Content.Server.Voting
public sealed partial class VoteManager : IVoteManager public sealed partial class VoteManager : IVoteManager
{ {
[Dependency] private readonly IServerNetManager _netManager = default!; [Dependency] private readonly IServerNetManager _netManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IChatManager _chatManager = default!;
@@ -320,7 +324,7 @@ namespace Content.Server.Voting
} }
else else
{ {
options.InitiatorText = Loc.GetString("The server"); options.InitiatorText = Loc.GetString("ui-vote-initiator-server");
} }
} }
@@ -389,6 +393,8 @@ namespace Content.Server.Voting
public bool Finished => _reg.Finished; public bool Finished => _reg.Finished;
public bool Cancelled => _reg.Cancelled; public bool Cancelled => _reg.Cancelled;
public IReadOnlyDictionary<object, int> VotesPerOption { get; }
public event VoteFinishedEventHandler? OnFinished public event VoteFinishedEventHandler? OnFinished
{ {
add => _reg.OnFinished += value; add => _reg.OnFinished += value;
@@ -405,6 +411,8 @@ namespace Content.Server.Voting
{ {
_mgr = mgr; _mgr = mgr;
_reg = reg; _reg = reg;
VotesPerOption = new VoteDict(reg);
} }
public bool IsValidOption(int optionId) public bool IsValidOption(int optionId)
@@ -421,6 +429,62 @@ namespace Content.Server.Voting
{ {
_mgr.CancelVote(_reg); _mgr.CancelVote(_reg);
} }
private sealed class VoteDict : IReadOnlyDictionary<object, int>
{
private readonly VoteReg _reg;
public VoteDict(VoteReg reg)
{
_reg = reg;
}
public IEnumerator<KeyValuePair<object, int>> GetEnumerator()
{
return _reg.Entries.Select(e => KeyValuePair.Create(e.Data, e.Votes)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public int Count => _reg.Entries.Length;
public bool ContainsKey(object key)
{
return TryGetValue(key, out _);
}
public bool TryGetValue(object key, out int value)
{
var entry = _reg.Entries.FirstOrNull(a => a.Data.Equals(key));
if (entry != null)
{
value = entry.Value.Votes;
return true;
}
value = default;
return false;
}
public int this[object key]
{
get
{
if (!TryGetValue(key, out var votes))
{
throw new KeyNotFoundException();
}
return votes;
}
}
public IEnumerable<object> Keys => _reg.Entries.Select(c => c.Data);
public IEnumerable<int> Values => _reg.Entries.Select(c => c.Votes);
}
} }
#endregion #endregion

View File

@@ -275,5 +275,13 @@ namespace Content.Shared
* Context Menu Grouping Types * Context Menu Grouping Types
*/ */
public static readonly CVarDef<int> ContextMenuGroupingType = CVarDef.Create("context_menu", 0, CVar.CLIENTONLY); public static readonly CVarDef<int> ContextMenuGroupingType = CVarDef.Create("context_menu", 0, CVar.CLIENTONLY);
/*
* VOTE
*/
public static readonly CVarDef<float> VoteRestartRequiredRatio =
CVarDef.Create("vote.restart_required_ratio", 0.8f, CVar.SERVERONLY);
} }
} }

View File

@@ -0,0 +1,4 @@
author: PJB
changes:
- message: Restart votes now need an 80% majority to succeed, and have a longer cooldown for the caller.
type: Tweak

View File

@@ -0,0 +1,4 @@
mode-traitor = Traitor
mode-extended = Extended
mode-sandbox = Sandbox
mode-suspicion = Suspicion

View File

@@ -0,0 +1,30 @@
### Voting menu stuff
# Displayed as initiator of vote when no user creates the vote
ui-vote-initiator-server = The server
ui-vote-restart-title = Restart round
ui-vote-restart-succeeded = Restart vote succeeded.
ui-vote-restart-failed = Restart vote failed (need { TOSTRING($ratio, "P0") }).
ui-vote-restart-yes = Yes
ui-vote-restart-no = No
ui-vote-gamemode-title = Next gamemode
ui-vote-gamemode-tie = Tie for gamemode vote! Picking... { $picked }
ui-vote-gamemode-win = { $winner } won the gamemode vote!
ui-vote-created = { $initiator } has called a vote:
ui-vote-button = { $text } ({ $votes })
ui-vote-type-restart = Restart round
ui-vote-type-gamemode = Next gamemode
# Window title of the vote create menu
ui-vote-create-title = Call Vote
# Submit button in the vote create button
ui-vote-create-button = Call Vote
# Hue hue hue
ui-vote-fluff = Powered by Robust™ Anti-Tamper Technology
# Button text in lobby/escape menu
ui-vote-menu-button = Call vote