ОБР работает

This commit is contained in:
BIGZi0348
2024-12-26 00:10:19 +03:00
parent dc570dcdb5
commit bd9c8a75c0
12 changed files with 164 additions and 86 deletions

View File

@@ -17,9 +17,9 @@ public sealed class AuthPanelBoundUserInterface : BoundUserInterface
_menu = new AuthPanelMenu();
_menu.OnRedButtonPressed(_=>SendButtonPressed(AuthPanelAction.ERTRecruit));
_menu.OnAccessButtonPressed(_=>SendButtonPressed(AuthPanelAction.AddAccess));
_menu.OnBluespaceWeaponButtonPressed(_=>SendButtonPressed(AuthPanelAction.BluespaceWeapon));
_menu.OnRedButtonPressed(_ => SendButtonPressed(AuthPanelAction.ERTRecruit));
// _menu.OnAccessButtonPressed(_ => SendButtonPressed(AuthPanelAction.AddAccess));
// _menu.OnBluespaceWeaponButtonPressed(_ => SendButtonPressed(AuthPanelAction.BluespaceWeapon));
_menu.OnClose += Close;
_menu.OpenCentered();
@@ -27,26 +27,27 @@ public sealed class AuthPanelBoundUserInterface : BoundUserInterface
public void SendButtonPressed(AuthPanelAction button)
{
SendMessage(new AuthPanelButtonPressedMessage(button,_menu?.GetReason()));
SendMessage(new AuthPanelButtonPressedMessage(button, _menu?.GetReason()));
}
protected override void UpdateState(BoundUserInterfaceState state)
{
if(state is not AuthPanelConfirmationActionState confirmationActionState)
if (state is not AuthPanelConfirmationActionState confirmationActionState)
return;
var action = confirmationActionState.Action;
if(action.Action is AuthPanelAction.AddAccess)
_menu?.SetAccessCount(action.ConfirmedPeopleCount,action.MaxConfirmedPeopleCount);
if(action.Action is AuthPanelAction.ERTRecruit)
_menu?.SetRedCount(action.ConfirmedPeopleCount,action.MaxConfirmedPeopleCount);
if(action.Action is AuthPanelAction.BluespaceWeapon)
_menu?.SetWeaponCount(action.ConfirmedPeopleCount,action.MaxConfirmedPeopleCount);
// if (action.Action is AuthPanelAction.AddAccess)
// _menu?.SetAccessCount(action.ConfirmedPeopleCount, action.MaxConfirmedPeopleCount);
if (action.Action is AuthPanelAction.ERTRecruit)
_menu?.SetRedCount(action.ConfirmedPeopleCount, action.MaxConfirmedPeopleCount);
// if (action.Action is AuthPanelAction.BluespaceWeapon)
// _menu?.SetWeaponCount(action.ConfirmedPeopleCount, action.MaxConfirmedPeopleCount);
_menu?.SetReason(action.Reason);
if (action.ConfirmedPeopleCount == 0)
_menu?.UnlockReason();
}
protected override void Dispose(bool disposing)

View File

@@ -1,9 +1,9 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
Title="Auth panel"
MinSize="500 350"
SetSize="500 350">
Title="{Loc 'auth-panel-name'}"
MinSize="500 300"
SetSize="500 300">
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True"
@@ -21,10 +21,10 @@
<BoxContainer Orientation="Vertical">
<BoxContainer HorizontalExpand="True" Name="RedContainer">
<Button Name="RedButton" MinWidth="410" Margin="0 5 0 0" HorizontalAlignment="Left" Text="{Loc 'auth-panel-red-button'}"/>
<Label Name="RedCount" Margin="25 0 4 0" HorizontalAlignment="Right" Visible="False"/>
<Label Name="RedCount" Margin="25 0 4 0" HorizontalAlignment="Right" Visible="true"/>
</BoxContainer>
<BoxContainer HorizontalExpand="True" Name="AccessContainer">
<!-- <BoxContainer HorizontalExpand="True" Name="AccessContainer">
<Button Name="AccessButton" MinWidth="410" Margin="0 5 0 0" HorizontalAlignment="Left" Text="{Loc 'auth-panel-access-button'}" Disabled="True"/>
<Label Name="AccessCount" Margin="25 0 4 0" HorizontalAlignment="Right" Visible="False"/>
</BoxContainer>
@@ -32,7 +32,7 @@
<BoxContainer HorizontalExpand="True" Name="BluespaceWeaponContainer">
<Button Name="BluespaceWeaponButton" MinWidth="410" Margin="0 5 0 5" HorizontalAlignment="Left" Text="{Loc 'auth-panel-unlock-weapon'}" Disabled="True"/>
<Label Name="BluespaceWeaponCount" Margin="25 0 4 0" HorizontalAlignment="Right" Visible="False"/>
</BoxContainer>
</BoxContainer> -->
</BoxContainer>
</controls:StripeBack>

View File

@@ -7,23 +7,22 @@ namespace Content.Client._White.AuthPanel;
[GenerateTypedNameReferences]
public sealed partial class AuthPanelMenu : FancyWindow
{
public void OnRedButtonPressed(Action<BaseButton.ButtonEventArgs> func)
{
RedButton.OnPressed += func;
}
public void OnAccessButtonPressed(Action<BaseButton.ButtonEventArgs> func)
{
AccessButton.OnPressed += func;
}
// public void OnAccessButtonPressed(Action<BaseButton.ButtonEventArgs> func)
// {
// AccessButton.OnPressed += func;
// }
public void OnBluespaceWeaponButtonPressed(Action<BaseButton.ButtonEventArgs> func)
{
BluespaceWeaponButton.OnPressed += func;
}
// public void OnBluespaceWeaponButtonPressed(Action<BaseButton.ButtonEventArgs> func)
// {
// BluespaceWeaponButton.OnPressed += func;
// }
public void SetCount(Label label,int conf, int maxconf)
public void SetCount(Label label, int conf, int maxconf)
{
label.Visible = conf != 0;
label.Text = conf + "/" + maxconf;
@@ -31,27 +30,27 @@ public sealed partial class AuthPanelMenu : FancyWindow
public void SetRedCount(int conf, int maxconf)
{
SetCount(RedCount,conf,maxconf);
SetCount(RedCount, conf, maxconf);
RedButton.Disabled = conf >= maxconf;
AccessContainer.Visible = false;
BluespaceWeaponContainer.Visible = false;
// AccessContainer.Visible = false;
// BluespaceWeaponContainer.Visible = false;
}
public void SetAccessCount(int conf, int maxconf)
{
SetCount(AccessCount,conf,maxconf);
AccessButton.Disabled = conf >= maxconf;
RedContainer.Visible = false;
BluespaceWeaponContainer.Visible = false;
}
// public void SetAccessCount(int conf, int maxconf)
// {
// SetCount(AccessCount, conf, maxconf);
// AccessButton.Disabled = conf >= maxconf;
// RedContainer.Visible = false;
// BluespaceWeaponContainer.Visible = false;
// }
public void SetWeaponCount(int conf, int maxconf)
{
SetCount(BluespaceWeaponCount,conf,maxconf);
BluespaceWeaponButton.Disabled = conf >= maxconf;
RedContainer.Visible = false;
AccessContainer.Visible = false;
}
// public void SetWeaponCount(int conf, int maxconf)
// {
// SetCount(BluespaceWeaponCount, conf, maxconf);
// BluespaceWeaponButton.Disabled = conf >= maxconf;
// RedContainer.Visible = false;
// AccessContainer.Visible = false;
// }
public string GetReason()
{
@@ -63,4 +62,9 @@ public sealed partial class AuthPanelMenu : FancyWindow
Reason.Text = reason;
Reason.Editable = false;
}
public void UnlockReason()
{
Reason.Editable = true;
}
}

View File

@@ -2,7 +2,6 @@ using Content.Server.GameTicking;
using Content.Server.Popups;
using Content.Server.Station.Systems;
using Content.Server._White.ERTRecruitment;
using Content.Server._White.JoinQueue;
using Content.Shared.Access.Systems;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
@@ -12,7 +11,6 @@ using Content.Shared._White.GhostRecruitment;
using Content.Shared.Ghost;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Player;
@@ -25,7 +23,6 @@ public sealed class AuthPanelSystem : EntitySystem
[Dependency] private readonly AccessReaderSystem _access = default!;
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly ERTRecruitmentRule _ert = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
@@ -36,10 +33,27 @@ public sealed class AuthPanelSystem : EntitySystem
public Dictionary<AuthPanelAction, HashSet<EntityUid>> Counter = new();
public Dictionary<AuthPanelAction, HashSet<int>> CardIndexes = new();
public string Reason = "";
public static int MaxCount = 1;
public static int DelayNextAction = 10;
/// <summary>
/// Minimal Amount of votes needed for action.
/// </summary>
public static int MinCount = 3;
/// <summary>
/// Amount of minutes before action can be called. Counting from round start.
/// </summary>
public static int EarliestStart = 45;
/// <summary>
/// Amount of seconds before action can be called. Counting from previous vote.
/// </summary>
public static int DelayDuration = 5;
/// <summary>
/// Amount of minutes before action can be called. Counting from previous call for vote.
/// </summary>
public static int TimeoutDuration = 10;
private TimeSpan? _delay;
private TimeSpan? _timeout;
public override void Initialize()
{
SubscribeLocalEvent<AuthPanelComponent, AuthPanelButtonPressedMessage>(OnButtonPressed);
@@ -56,7 +70,11 @@ public sealed class AuthPanelSystem : EntitySystem
private void OnRestart(RoundRestartCleanupEvent ev)
{
ClearPanel();
Counter.Clear();
CardIndexes.Clear();
_delay = null;
_timeout = null;
}
private void ClearPanel()
@@ -65,12 +83,36 @@ public sealed class AuthPanelSystem : EntitySystem
CardIndexes.Clear();
_delay = null;
_timeout = null;
var action = new AuthPanelConfirmationAction(AuthPanelAction.ERTRecruit, 0, MinCount, "");
var query = EntityQueryEnumerator<AuthPanelComponent>();
while (query.MoveNext(out var uid, out _))
{
if (!_ui.HasUi(uid, AuthPanelUiKey.Key))
continue;
var state = new AuthPanelConfirmationActionState(action);
_ui.SetUiState(uid, AuthPanelUiKey.Key, state);
_appearance.SetData(uid, AuthPanelVisualLayers.Confirm, false);
}
}
private void OnPerformAction(EntityUid uid, AuthPanelComponent component, AuthPanelPerformActionEvent args)
{
if (args.Action is AuthPanelAction.ERTRecruit)
{
if (_ticker.RoundDuration() < TimeSpan.FromMinutes(EarliestStart))
{
var station = _station.GetStationInMap(Transform(uid).MapID);
if (station != null)
_ert.DeclineERT(station.Value);
_adminLogger.Add(LogType.EventStarted, LogImpact.High, $"ERT Declined - Not enough time passed");
return;
}
var query = EntityQueryEnumerator<GhostComponent, ActorComponent>();
var ghostList = new List<EntityUid>();
while (query.MoveNext(out var ghost, out _, out _))
@@ -78,19 +120,8 @@ public sealed class AuthPanelSystem : EntitySystem
ghostList.Add(ghost);
}
// if (_ticker.RoundDuration() < TimeSpan.FromMinutes(EarliestStart))
// {
// var station = _station.GetStationInMap(Transform(uid).MapID);
// if (station != null)
// _ert.DeclineERT(station.Value);
// _adminLogger.Add(LogType.EventStarted, LogImpact.High, $"ERT Declined - Not enough time passed");
// return;
// }
var playerCount = _playerManager.PlayerCount;
//if (playerCount - ghostList.Count > playerCount / 2 && ghostList.Count > 3)
if (true)
if (playerCount - ghostList.Count > playerCount / 2 && ghostList.Count > MinCount)
{
_gameTicker.AddGameRule(ERTRecruitmentRuleComponent.EventName);
}
@@ -112,8 +143,6 @@ public sealed class AuthPanelSystem : EntitySystem
}
}
}
Timer.Spawn(TimeSpan.FromSeconds(DelayNextAction), () => ClearPanel());
}
private void OnButtonPressed(EntityUid uid, AuthPanelComponent component, AuthPanelButtonPressedMessage args)
@@ -147,7 +176,7 @@ public sealed class AuthPanelSystem : EntitySystem
Counter.Add(args.Button, hashSet);
}
if (hashSet.Count == MaxCount)
if (hashSet.Count >= MinCount)
return;
if (!CardIndexes.TryGetValue(args.Button, out var cardSet))
@@ -171,16 +200,17 @@ public sealed class AuthPanelSystem : EntitySystem
}
cardSet.Add(access.Count);
_delay = _timing.CurTime + TimeSpan.FromSeconds(5);
_delay = _timing.CurTime + TimeSpan.FromSeconds(DelayDuration);
Reason = args.Reason;
UpdateUserInterface(args.Button);
_adminLogger.Add(LogType.EventStarted, LogImpact.High, $"{ToPrettyString(args.Actor):player} vote for {args.Button}. Reason: {Reason}");
if (hashSet.Count == MaxCount)
if (hashSet.Count >= MinCount)
{
var ev = new AuthPanelPerformActionEvent(args.Button);
RaiseLocalEvent(uid, ev);
_timeout = _timing.CurTime + TimeSpan.FromMinutes(TimeoutDuration);
}
}
@@ -189,13 +219,13 @@ public sealed class AuthPanelSystem : EntitySystem
if (!Counter.TryGetValue(rawaction, out var hashSet))
return;
var action = new AuthPanelConfirmationAction(rawaction, hashSet.Count, MaxCount, Reason);
var action = new AuthPanelConfirmationAction(rawaction, hashSet.Count, MinCount, Reason);
var query = EntityQueryEnumerator<AuthPanelComponent>();
while (query.MoveNext(out var uid, out _))
{
if (!_ui.HasUi(uid, AuthPanelUiKey.Key))
return;
continue;
var state = new AuthPanelConfirmationActionState(action);
@@ -206,12 +236,14 @@ public sealed class AuthPanelSystem : EntitySystem
public override void Update(float frameTime)
{
if (_delay == null)
return;
if (_timing.CurTime >= _delay)
if (_delay != null && _timing.CurTime >= _delay)
{
_delay = null;
}
if (_timeout != null && _timing.CurTime >= _timeout)
{
ClearPanel();
}
}
}

View File

@@ -1,15 +1,12 @@
using System.Linq;
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Events;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.StationEvents.Events;
using Content.Server._White.GhostRecruitment;
using Content.Server.GameTicking.Components;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared._White;
using Content.Shared._White.GhostRecruitment;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
@@ -73,7 +70,6 @@ public sealed class ERTRecruitmentRule : StationEventSystem<ERTRecruitmentRuleCo
GameRuleStartedEvent args)
{
base.Started(uid, component, gameRule, args);
_logger.Debug("Event is started");
if (component.TargetStation == null || component.IsBlocked || IsDisabled)
{
@@ -82,7 +78,7 @@ public sealed class ERTRecruitmentRule : StationEventSystem<ERTRecruitmentRuleCo
return;
}
if (_recruitment.GetEventSpawners(ERTRecruitmentRuleComponent.EventName).Count() < component.MinPlayer)
if (_recruitment.GetEventSpawners(ERTRecruitmentRuleComponent.EventName).Count() < component.MinPlayers)
{
DeclineERT(component.TargetStation.Value);
_adminLogger.Add(LogType.EventStarted, LogImpact.High, $"ERT Declined - Not enough spawners");
@@ -108,7 +104,7 @@ public sealed class ERTRecruitmentRule : StationEventSystem<ERTRecruitmentRuleCo
var check1 = component.IsBlocked || ertsys.IsDisabled;
var check2 = _recruitment.GetAllRecruited(ERTRecruitmentRuleComponent.EventName).Count() < component.MinPlayer;
var check2 = _recruitment.GetAllRecruited(ERTRecruitmentRuleComponent.EventName).Count() < component.MinPlayers;
if (check1)
{

View File

@@ -11,7 +11,10 @@ public sealed partial class ERTRecruitmentRuleComponent : Component
[ViewVariables]
public MapId? MapId = null;
[DataField("minPlayer")] public int MinPlayer = 1;
/// <summary>
/// Minimal amount of players, who will become ERT recruits.
/// </summary>
[DataField] public int MinPlayers = 3;
public static SoundSpecifier ERTYes = new SoundPathSpecifier("/Audio/Announcements/ert_yes.ogg");
public static SoundSpecifier ERTNo = new SoundPathSpecifier("/Audio/Announcements/ert_no.ogg");

View File

@@ -1,4 +1,4 @@
ent-AuthPanel = Панель авторизации
ent-AuthPanel = панель авторизации
.desc = Если что-то пойдёт не так...
ert-description = Как член Отряда Быстрого Реагирования,
@@ -8,6 +8,8 @@ ert-description = Как член Отряда Быстрого Реагиров
ert-reason = Причина вызова: { $reason }
auth-panel-name = Панель Авторизации
auth-panel-no-reason = Пожалуйста, заполните причину
auth-panel-no-access = Нет доступа
auth-panel-wait = Пожалуйста, подождите прежде чем активировать кнопку
@@ -19,6 +21,6 @@ auth-panel-critical-only = Воспользуйтесь данной панел
auth-panel-reason-write = Снизу опишите причину ваших действий.
auth-panel-reason = Причина:
auth-panel-red-button = Красная кнопка
auth-panel-access-button = Аварийный доступ к тех. тоннелям
auth-panel-unlock-weapon = Разблокировка блюспейс артилерии
auth-panel-red-button = Вызвать ОБР
# auth-panel-access-button = Аварийный доступ к тех. тоннелям
# auth-panel-unlock-weapon = Разблокировка блюспейс артилерии

View File

@@ -73,3 +73,4 @@ guide-entry-writing = Разметка письма
guide-entry-glossary = Словарь терминов
guide-entry-weapon-modules = Оружейные модули
guide-entry-auth-panel = Панель авторизации

View File

@@ -7,6 +7,7 @@
- Defusal
- CriminalRecords
- WeaponModules # WD
- AuthPanel # WD
- type: guideEntry
id: Forensics
@@ -27,3 +28,8 @@
id: WeaponModules
name: guide-entry-weapon-modules
text: "/ServerInfo/Guidebook/Security/WeaponModules.xml"
- type: guideEntry # WD
id: AuthPanel
name: guide-entry-auth-panel
text: "/ServerInfo/Guidebook/Security/AuthPanel.xml"

View File

@@ -13,6 +13,9 @@
- type: InteractionOutline
- type: Appearance
- type: AuthPanel
- type: GuideHelp
guides:
- AuthPanel
- type: Sprite
noRot: false
sprite: White/Structures/Machines/auth.rsi

View File

@@ -7,4 +7,3 @@
weight: 0
duration: 30
- type: ERTRecruitmentRule
minPlayer: 3

View File

@@ -0,0 +1,31 @@
<Document>
# Панель авторизации
<Box>
<GuideEntityEmbed Entity="AuthPanel" Scale="2.0" Caption="Если что-то пойдёт не так..."/>
</Box>
Жизнь и работа на космической станции предлагает огромный перечень возможностей. Часть из этих возможностей даже могут быть вам по душе.
Несмотря на крайне позитивную статистику выживаемости станции в условиях агрессивной конкуренции, НаноТрейзен установила специальные передатчики дальнего действия для вызова Отряда Быстрого Реагирования (далее ОБР).
В случае чрезвычайной ситуации командование станции имеет право (в ущерб своей премии) запросить вызов ОБР для устранения широкого спектра проблем.
Интерфейс панели обязывает указать причину вызова, согласно которому ОБР будет подготовлено надлежащим обмундированием.
Ввиду дорогостоимости отправки подразделения, панель авторизации имеет следующие механизмы защиты:
- Инициализировать голосование за вызов ОБР и участвовать в нём могут только командование станции, что подтверждается сканером ID карт.
- Одна персона может проголосовать только один раз, что подтверждается сканером лица.
- Для обработки достоверности введённых данных между вводом голосов предусмотрена задержка в 5 секунд.
- При инициализации голосования, несмотря на его исход, создаётся задержка в 10 минут до возможности инициализировать следующее голосование.
- Голосование считается положительным при вводе 3 голосов.
При положительном исходе голосования панель авторизации отправит запрос на ближайшую станцию ОБР, системы которой в течение 30 секунд отправят ответ.
Факторы, которые принимаются во внимание при составлении ответа:
- Если станция ОБР находится не в надлежащем состоянии для реализации операции - запрос будет отклонён.
- При недостаточном количестве недееспособного персонала на станции запрашивающей помощь - запрос будет отклонён.
- Все запросы, отправленные раньше 45 минут с начала работы станции - автоматически отклоняются.
- Запросы станции, которая уже получила помощь ОБР - автоматически отклоняются.
- Станция ОБР имеет право отклонить или одобрить запрос на своё усмотрение.
</Document>