Skip to content

Call ert console#31

Open
CREAsTIVE wants to merge 3 commits intoTheMaidDev:mainfrom
CREAsTIVE:call-ERT-console
Open

Call ert console#31
CREAsTIVE wants to merge 3 commits intoTheMaidDev:mainfrom
CREAsTIVE:call-ERT-console

Conversation

@CREAsTIVE
Copy link
Copy Markdown

@CREAsTIVE CREAsTIVE commented Mar 1, 2026

Добавлена консоль вызова ERT (панель авторизации)
Переносил из последнего коммита: https://github.com/frosty-dev/ss14-core

Изменения

🆑 CREAsTIVE

  • add: Портирована панель вызова авторизации

TODO: Мапперам надо отредактировать Resources/Maps/_Maid/ERTStation.yml и сделать полноценную ERT станцию и полноценный шаттл (Resources/Maps/_Maid/Shuttles/ert_shuttle.yml)

Можно украсть из старого билда, но там используется много "эксклюзивных" ресурсов, у меня даже не получилось загрузить карту, надо фиксить

Как я уже упоминал в дискорде: система просто ужас какой то. Я не стал заниматься модификацией логики (т. к. это не входило в мою задачу). Однако я бы не рекомендовал "фикс" данной логики, будто легче с нуля переписать ИМХО.

Summary by CodeRabbit

  • New Features

    • Added Authentication Panel system for crew voting on actions including Emergency Response Team (ERT) recruitment
    • Implemented ERT recruitment with ghost acceptance UI
    • Added new ERT station and shuttle maps with designated spawn points for recruited personnel
    • Introduced voting approval system with confirmation counts and optional reason submission
  • Documentation & Localization

    • Added English and Russian localization strings for authentication panels and ERT recruitment flows
  • Configuration

    • Added server configuration option for ERT map loading

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 1, 2026

RSI Diff Bot; head commit 7f6bf95 merging into 995dd34
This PR makes changes to 1 or more RSIs. Here is a summary of all changes:

Resources/Textures/_Maid/Structures/Wallmounts/auth.rsi

State Old New Status
auth_off Added
auth_on Added

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 1, 2026

📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive Maid subsystem featuring an authentication panel with vote-based action confirmation, ERT (Emergency Response Team) recruitment mechanics, and ghost recruitment UI flows. New client/server systems manage button voting, state synchronization, recruitment timers, and EUI interactions, alongside supporting components, events, maps, and localization resources.

Changes

Cohort / File(s) Summary
Authentication Panel UI (Client)
Content.Client/_Maid/AuthPanel/AuthPanel... (BoundUserInterface, Menu.xaml, Menu.xaml.cs)
Client-side bound UI and menu window for auth panel with button event handlers, reason field management, and count display updates.
Authentication Panel (Shared)
Content.Shared/_Maid/AuthPanel/AuthPanelComponent.cs, SharedAuthPanel.cs
Shared component, UI keys, action enums, message types, and state definitions for auth panel serialization and networking.
Authentication Panel System (Server)
Content.Server/_Maid/AuthPanel/AuthPanelSystem.cs
Server-side system managing vote tracking, access validation, timing (delay/timeout), UI state updates, and action performance with admin logging.
Ghost Recruitment UI (Client)
Content.Client/_Maid/GhostRecruitment/GhostRecruitmentEui...
Client EUI handler and window for ghost recruitment accept/deny flow with button wiring and UI state management.
Ghost Recruitment System (Server)
Content.Server/_Maid/GhostRecruitment/GhostRecruitmentSystem.cs, Events.cs, EuiAccept.cs
Server system orchestrating ghost recruitment start/end, mind transfer, spawning, UI lifecycle, and event signaling on success.
Ghost Recruitment (Shared)
Content.Shared/_Maid/GhostRecruitment/Ghost...Component.cs, EuiAccept.cs, Events.cs, SpawnPoint...cs, RecruitedComponent.cs
Shared components (recruited, spawn points), EUI messages (accept/deny), and event definitions for recruitment flow.
ERT Recruitment System (Server)
Content.Server/_Maid/ERTRecruitment/BlockERT.cs, ERTMapComponent.cs, ERTRecruited...Event.cs, ERTRecruitment...
Server systems managing ERT rule logic, map spawning, reason propagation, player count validation, and admin blocking command.
Configuration & CVars
Content.Shared/_Maid/CVars/CVars.cs, Resources/ConfigPresets/Build/development.toml
New CVar LoadErtMap (server-only) controlling ERT map generation at round start with development preset.
Localization
Resources/Locale/.../auth-panel.ftl, recruitment.ftl
en-US and ru-RU translation files for auth panel UI labels, reasons, and ERT recruitment messages.
Maps & Station Definition
Resources/Maps/_Maid/ERTStation.yml, Shuttles/ert_shuttle.yml
Complete ERT station and shuttle map definitions with grid, entities, atmosphere systems, and component configurations.
Prototypes & Assets
Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml, Structures/.../auth.yml, events.yml, Textures/auth.rsi/meta.json
ERT role spawner definitions, auth panel wall-mount entity, ERTRecruitment game rule, and texture metadata.
Miscellaneous
Content.Server/Mapping/MappingSystem.cs, Content.Client/_White/RadialSelector/RadialSelectorMenuBUI.cs
Minor path formatting change and BOM character cleanup (no functional impact).

Sequence Diagram(s)

sequenceDiagram
    actor Player
    participant ClientUI as Client: AuthPanel UI
    participant ClientBUI as Client: BoundUserInterface
    participant Server as Server: AuthPanelSystem
    participant UI as Server: UI Update

    Player->>ClientUI: Press Action Button
    ClientUI->>ClientBUI: SendButtonPressed(action, reason)
    ClientBUI->>Server: AuthPanelButtonPressedMessage
    Server->>Server: ValidateAccess()
    Server->>Server: ValidateReason()
    Server->>Server: CheckVoteLimits()
    Server->>Server: RecordVote()
    Server->>UI: UpdateUserInterface()
    UI->>ClientUI: AuthPanelConfirmationActionState
    ClientUI->>ClientUI: UpdateCount & Reason
    alt MinCount Reached
        Server->>Server: StartTimeout
        Server->>Server: RaiseActionEvent()
    end
Loading
sequenceDiagram
    actor Ghost
    participant ClientGUI as Client: Ghost Recruitment UI
    participant ClientEUI as Client: GhostRecruitmentEUI
    participant Server as Server: GhostRecruitmentSystem
    participant Recruitment as Server: Recruitment Handler

    Ghost->>ClientGUI: View Accept/Deny Window
    alt Ghost Accepts
        Ghost->>ClientGUI: Click Accept
        ClientGUI->>ClientEUI: AcceptRecruitmentChoiceMessage(Accept)
        ClientEUI->>Server: Message Handler
        Server->>Recruitment: Recruit(ghost_uid, recruitment_name)
        Recruitment->>Server: Queue Ghost for Spawning
    else Ghost Denies
        Ghost->>ClientGUI: Click Deny
        ClientGUI->>ClientEUI: AcceptRecruitmentChoiceMessage(Deny)
        ClientEUI->>Server: Close EUI
    end
Loading
sequenceDiagram
    participant RoundStart as Round: Start Event
    participant ERTRule as Server: ERTRecruitmentRule
    participant MapSystem as Server: Map System
    participant GhostRecruitment as Server: GhostRecruitmentSystem
    participant Chat as Server: Chat System

    RoundStart->>ERTRule: OnStarting()
    ERTRule->>ERTRule: ValidateDisabled()
    ERTRule->>ERTRule: CheckPlayerCount()
    alt Map Loading Enabled
        ERTRule->>MapSystem: SpawnOutpostMap()
        MapSystem->>MapSystem: Load ERTStation.yml
        MapSystem->>ERTRule: Map Spawned
    end
    ERTRule->>GhostRecruitment: StartRecruitment()
    GhostRecruitment->>GhostRecruitment: FilterGhosts(playtime)
    GhostRecruitment->>GhostRecruitment: OpenEUI per Ghost
    ERTRule->>Chat: Broadcast Wait Message
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 Whiskers twitch with delight!
Auth panels now vote through the night,
Ghosts join ERTs with a bound,
New systems spinning all around!
Code hops forward, clean and bright!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Call ert console' clearly describes the primary change: adding an ERT (Emergency Response Team) call/authorization panel console, which is the main objective of this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 18

🧹 Nitpick comments (8)
Content.Client/_Maid/AuthPanel/AuthPanelMenu.xaml (1)

27-35: Consider removing or tracking commented-out UI sections.

The commented-out AccessContainer and BluespaceWeaponContainer blocks suggest planned features. If these are intended for future implementation, consider removing them from the codebase and tracking them as GitHub issues or TODO comments in the code-behind file instead.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Client/_Maid/AuthPanel/AuthPanelMenu.xaml` around lines 27 - 35, The
XAML contains commented-out UI blocks for AccessContainer/AccessButton and
BluespaceWeaponContainer/BluespaceWeaponButton; remove these commented sections
from AuthPanelMenu.xaml if they are not needed, or alternatively move them into
a tracked TODO (or GitHub issue) and add a short TODO comment in the code-behind
(e.g., AuthPanelMenu.xaml.cs) referencing that issue ID so the intent is
preserved; ensure any related names (AccessCount, BluespaceWeaponCount) are also
removed or documented to avoid dead identifiers.
Content.Server/_Maid/ERTRecruitment/ERTRecruitedReasonEvent.cs (1)

3-12: Consider using a constructor parameter instead of mutable setter.

The event class uses a mutable SetReason method, which deviates from the typical pattern seen in similar events like GhostRecruitmentSuccessEvent (in GhostRecruitmentEvents.cs) that uses constructor parameters. Events are generally immutable after creation.

Additionally, verify whether this event needs the [NetSerializable] attribute if it will be sent over the network. Other similar events use [Serializable, NetSerializable].

♻️ Proposed refactor using constructor parameter
 [Serializable]
 public sealed class ERTRecruitedReasonEvent : EntityEventArgs
 {
-    public string Reason = "";
+    public string Reason { get; }
 
-    public void SetReason(string reason)
+    public ERTRecruitedReasonEvent(string reason)
     {
         Reason = reason;
     }
-
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Server/_Maid/ERTRecruitment/ERTRecruitedReasonEvent.cs` around lines
3 - 12, ERTRecruitedReasonEvent currently exposes a mutable SetReason method
which breaks the usual immutable event pattern; change the class to accept the
reason via a constructor parameter and make the Reason field read-only or a
get-only property (replace SetReason with a constructor that sets Reason) so the
event is immutable after creation, and if this event is transmitted over the
network add the [NetSerializable] attribute alongside [Serializable] to match
other event types like GhostRecruitmentSuccessEvent.
Content.Shared/_Maid/CVars/CVars.cs (1)

56-60: Minor documentation grammar improvement.

The XML doc comment has a minor grammatical issue.

📝 Proposed fix
     /// <summary>
-    ///     Do generate Ert map on round start or not
+    ///     Whether to generate ERT map on round start.
     /// </summary>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Shared/_Maid/CVars/CVars.cs` around lines 56 - 60, Update the XML doc
comment for the public static readonly CVarDef<bool> LoadErtMap so the grammar
is correct and reads clearly (e.g., "Whether to generate the ERT map on round
start."). Locate the declaration of LoadErtMap
(CVarDef.Create("maid.load_ert_map", true, CVar.SERVERONLY)) and replace the
existing summary text with the corrected sentence.
Content.Server/_Maid/ERTRecruitment/ERTMapComponent.cs (1)

14-14: Make outpost map path immutable.

Line 14 exposes a mutable static path. This should be immutable to prevent accidental runtime reassignment.

♻️ Proposed fix
-    public static ResPath OutpostMap = new("/Maps/_Maid/ERTStation.yml");
+    public static readonly ResPath OutpostMap = new("/Maps/_Maid/ERTStation.yml");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Server/_Maid/ERTRecruitment/ERTMapComponent.cs` at line 14,
OutpostMap is declared as a mutable static field; change it to an immutable
static readonly field by updating the declaration of OutpostMap (the public
static ResPath OutpostMap in ERTMapComponent) to be static readonly so the
ResPath cannot be reassigned at runtime while keeping the same initialization
value.
Content.Shared/_Maid/GhostRecruitment/RecruitedComponent.cs (1)

4-10: Consolidate recruited-marker semantics to avoid split state.

RecruitedComponent (Line 6) duplicates the same recruitmentName data shape already present in GhostRecruitedComponent (Content.Shared/_Maid/GhostRecruitment/GhostRecruitedComponent.cs, Line 5-Line 9). Keeping both in the same domain makes it easy for systems/prototypes to attach or query the wrong marker.

♻️ Suggested direction
- // this for spawned prototype
- [RegisterComponent]
- public sealed partial class RecruitedComponent : Component
+ /// <summary>
+ /// Marks spawned entities created by ghost recruitment flow.
+ /// </summary>
+ [RegisterComponent]
+ public sealed partial class GhostRecruitmentSpawnedComponent : Component
 {
     [DataField("recruitmentName")]
     public string RecruitmentName = "default";
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Shared/_Maid/GhostRecruitment/RecruitedComponent.cs` around lines 4 -
10, RecruitedComponent duplicates the recruitmentName field already defined in
GhostRecruitedComponent, causing split marker semantics; consolidate by removing
the duplicate state: delete the RecruitmentName field (and/or the entire
RecruitedComponent) and standardize on GhostRecruitedComponent as the single
recruited marker, then update any systems/prototypes that construct, attach, or
query RecruitedComponent to use GhostRecruitedComponent instead so all
recruitment logic references the same component type.
Content.Shared/_Maid/GhostRecruitment/GhostRecruitmentSpawnPointComponent.cs (1)

1-19: LGTM with minor inconsistency note.

The component structure is well-defined and follows the pattern established by related components (RecruitedComponent, GhostRecruitedComponent). The use of PrototypeIdSerializer<EntityPrototype> for prototype validation is correct.

One minor inconsistency: Line 17 uses [DataField] without an explicit key name, while other fields specify keys explicitly (e.g., "prototype", "recruitmentName", "priority"). Consider adding "jobId" for consistency:

-    [DataField]
+    [DataField("jobId")]
     public string JobId = "Passenger";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Shared/_Maid/GhostRecruitment/GhostRecruitmentSpawnPointComponent.cs`
around lines 1 - 19, The DataField on JobId is missing an explicit key which is
inconsistent with other fields; update the attribute on the JobId field in
GhostRecruitmentSpawnPointComponent (the public string JobId = "Passenger") to
use [DataField("jobId")] so it matches the explicit keys used for
EntityPrototype, RecruitmentName and Priority and ensures consistent
serialization/deserialization.
Content.Client/_Maid/AuthPanel/AuthPanelMenu.xaml.cs (1)

15-53: Remove or track the large commented-out UI branches.

Keeping inactive button/container code in-place makes the real behavior harder to scan and maintain.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Client/_Maid/AuthPanel/AuthPanelMenu.xaml.cs` around lines 15 - 53,
Remove or formalize the large commented-out UI branches instead of leaving them
inline: delete the commented methods OnAccessButtonPressed and
OnBluespaceWeaponButtonPressed and the commented SetAccessCount and
SetWeaponCount blocks (and the commented visibility lines inside SetRedCount
referencing AccessContainer and BluespaceWeaponContainer), or replace each block
with a short TODO/comment referencing a tracking ticket or feature flag if the
code must be preserved for future work; ensure any preserved notes mention the
exact symbol (e.g., OnAccessButtonPressed, OnBluespaceWeaponButtonPressed,
SetAccessCount, SetWeaponCount, AccessContainer, BluespaceWeaponContainer) so
callers and reviewers can find the rationale.
Content.Server/_Maid/AuthPanel/AuthPanelSystem.cs (1)

32-34: Encapsulation: Public mutable fields expose internal state.

Counter, CardIndexes, and Reason are public fields that can be modified by any external code, which could lead to inconsistent state. Consider making these private and exposing read-only access if needed externally.

Proposed encapsulation fix
-    public Dictionary<AuthPanelAction, HashSet<EntityUid>> Counter = new();
-    public Dictionary<AuthPanelAction, HashSet<int>> CardIndexes = new();
-    public string Reason = "";
+    private Dictionary<AuthPanelAction, HashSet<EntityUid>> _counter = new();
+    private Dictionary<AuthPanelAction, HashSet<EntityUid>> _usedCards = new();
+    private string _reason = "";
+
+    public IReadOnlyDictionary<AuthPanelAction, HashSet<EntityUid>> Counter => _counter;
+    public string Reason => _reason;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Server/_Maid/AuthPanel/AuthPanelSystem.cs` around lines 32 - 34, Make
the mutable public fields Counter, CardIndexes and Reason private (e.g. rename
to _counter, _cardIndexes, _reason) and expose read-only accessors instead: add
public properties like IReadOnlyDictionary<AuthPanelAction,
IReadOnlyCollection<EntityUid>> Counter { get; } and
IReadOnlyDictionary<AuthPanelAction, IReadOnlyCollection<int>> CardIndexes {
get; } (or return IReadOnlyDictionary<AuthPanelAction, IReadOnlyCollection<T>>
by wrapping the private Dictionary), and public string Reason { get; private
set; } (or IReadOnlyProperty). Keep the internal collections as
Dictionary<AuthPanelAction, HashSet<...>> for mutation inside AuthPanelSystem
methods (use the private fields _counter/_cardIndexes) and return read-only
wrappers (e.g. .ToDictionary(kv => kv.Key, kv =>
(IReadOnlyCollection<T>)kv.Value) or ReadOnlyCollection) so external code cannot
mutate internal state directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Content.Client/_Maid/GhostRecruitment/GhostRecruitmentEuiAccept.cs`:
- Around line 15-27: The Deny flow sends duplicate messages because
DenyButton.OnPressed sends
AcceptRecruitmentChoiceMessage(AcceptRecruitmentUiButton.Deny) then calls
_window.Close(), and _window.OnClose also sends the same message; to fix,
prevent the OnClose handler from re-sending when Close() is invoked manually by
either (a) removing/unsubscribing the OnClose handler before calling
_window.Close() inside the DenyButton.OnPressed handler, or (b) add a
short-lived flag (e.g. bool _suppressCloseHandler) set to true in
DenyButton.OnPressed before calling _window.Close() and have the _window.OnClose
lambda check that flag and return without sending if set; update the
corresponding AcceptButton flow similarly if needed and reference the handlers
named DenyButton.OnPressed, _window.OnClose, AcceptRecruitmentChoiceMessage and
AcceptButton.OnPressed.

In `@Content.Server/_Maid/AuthPanel/AuthPanelSystem.cs`:
- Around line 38-43: MinCount and EarliestStart are using temporary hardcoded
values; replace them with configurable CVars or finalize production defaults.
Update AuthPanelSystem to read these settings from configuration (e.g., declare
CVars like authpanel.minCount and authpanel.earliestStart or equivalent engine
config entries), use those CVar values instead of the hardcoded MinCount and
EarliestStart, and remove the "TEMP" comments; ensure sensible defaults (e.g., 3
and 60) are set in the CVar registration so the values are configurable at
runtime and during testing.
- Around line 70-77: OnRestart currently clears Counter and CardIndexes and
resets _delay/_timeout but leaves Reason unchanged; update the OnRestart method
to reset Reason (e.g., set Reason = string.Empty) so any stale reason from a
previous round is cleared; reference the OnRestart method and the Reason
field/property along with Counter and CardIndexes when making the change.
- Around line 124-131: The aliveCount calculation and ghost threshold are too
simplistic: instead of using _playerManager.PlayerCount and aliveCount =
playerCount - ghostCount (which counts observers, lobby users, or disconnected
sessions as "alive"), compute the participant set explicitly (e.g., iterate
_playerManager.GetAllPlayers() or use a connected/active players API and filter
out admin observers/lobby/disconnected) and derive aliveCount from that filtered
list; also stop reusing MinCount for ghost threshold—introduce a distinct
constant (e.g., MinGhostsThreshold) and replace the condition ghostList.Count >
MinCount with ghostList.Count >= MinGhostsThreshold before calling
_gameTicker.AddGameRule(ERTRecruitmentRuleComponent.EventName).
- Around line 188-208: The uniqueness check currently uses access.Count (in the
OnButtonPressed flow where CardIndexes, args.Button and cardSet are referenced),
which is not a unique card identifier; change the logic to record a true unique
ID per card (e.g., the card entity's EntityUid or a hash of its access tags)
instead of access.Count: locate the place where you read the card (the code that
populates access and uses hashSet and CardIndexes), obtain the card entity UID
(or compute a stable hash of its access list), and use that UID/hash when
checking/adding to cardSet and when checking hashSet so duplicate physical cards
are detected correctly; preserve the popup behavior
(auth-panel-used-ID/auth-panel-pressed) but operate on the UID/hash rather than
access.Count.

In `@Content.Server/_Maid/ERTRecruitment/Commands/BlockERT.cs`:
- Around line 17-31: The Execute method currently sets isDisabled by toggling
ertsys.IsDisabled and then checks args[0] == "true", which misparses values like
"false" or different casing; update Execute to parse the first arg using
bool.TryParse (case-insensitive/invariant) into a local bool (e.g., parsed) and,
if parsing succeeds, set ertsys.IsDisabled = parsed, otherwise preserve the
existing toggle behavior when no valid arg is provided; apply this change in the
Execute method where args, isDisabled, and ertsys (ERTRecruitmentRule) are
handled and keep the existing shell.WriteLine and
_chatManager.SendAdminAnnouncement calls.

In `@Content.Server/_Maid/ERTRecruitment/ERTRecruitmentRule.cs`:
- Around line 76-89: The Started handler currently calls DeclineERT before
calling ForceEndSelf when component.IsBlocked/IsDisabled or when spawner count
is too low, which leads to duplicate decline behavior because ForceEndSelf
triggers Ended which also declines; remove the pre-decline calls and let
ForceEndSelf/Ended handle the DeclineERT path (i.e., delete the DeclineERT(...)
calls in the Started flow for the checks around component.IsBlocked/IsDisabled
and the spawner count so only ForceEndSelf(uid, gameRule) is invoked), ensuring
DeclineERT is called exclusively from Ended/failure handling to avoid double
announcements/logs.
- Around line 134-144: The handler OnRecruitmentSuccess currently sends ERT
messages for every recruitment event; modify it to only proceed when
args.RecruitmentName matches the ERT recruitment identifier (e.g., compare
args.RecruitmentName to the expected string like "ERT" or the defined constant),
returning early otherwise; keep the existing logic (raising
ERTRecruitedReasonEvent, checking args.PlayerSession, and dispatching messages)
unchanged but guarded by this recruitment-name check.

In `@Content.Server/_Maid/GhostRecruitment/GhostRecruitmentSystem.cs`:
- Around line 101-107: The code increments count and raises the
GhostRecruitmentSuccessEvent immediately after calling TransferMind, which can
emit false successes if transfer fails; update the GhostRecruitmentSystem to
only set the RecruitedComponent, increment count, and RaiseLocalEvent after
verifying TransferMind actually succeeded—either by using a boolean/try-return
from TransferMind (or its TryTransferMind equivalent) or by wrapping
TransferMind in a try/catch and proceeding only on success; apply this change to
the block using TransferMind(ghostUid, spawnerUid, spawnerComponent) and the
similar block in lines 132-156, so
EnsureComp<RecruitedComponent>(ghostUid).RecruitmentName, count++, and
RaiseLocalEvent(ghostUid, new GhostRecruitmentSuccessEvent(...,
actorComponent.PlayerSession)) occur only when the transfer succeeded.
- Around line 206-210: The loop over _openUis mutates the dictionary because
CloseEui (and ClearEui) remove entries, causing InvalidOperationException; fix
by materializing the collection before iterating (e.g. iterate over
_openUis.Keys or _openUis.ToList()) so you call
CloseEui(session.AttachedEntity.Value, recruitmentName) on a copied list rather
than the dictionary being enumerated; update the code referencing _openUis in
the foreach around CloseEui to use the materialized collection.
- Around line 165-168: The current check in GhostRecruitmentSystem using
HasComp<RandomHumanoidSpawnerComponent> then doing uid = new EntityUid((int) uid
+ 1) is wrong; instead call RandomHumanoidSystem.SpawnRandomHumanoid() and use
its returned EntityUid for the spawned humanoid (or store that return value on
the RandomHumanoidSpawnerComponent) and attach the player's mind to that
returned UID; if spawning fails, return null/abort recruitment rather than
guessing IDs. Ensure you reference SpawnRandomHumanoid() from
RandomHumanoidSystem and replace the uid arithmetic logic and any subsequent use
of that guessed uid with the actual spawned entity UID.

In `@Content.Shared/_Maid/GhostRecruitment/GhostRecruitmentEvents.cs`:
- Around line 18-35: Fix the typo in the XML comment for the CancelableEventArgs
class: change the summary text from "Whether this even has been cancelled." to
"Whether this event has been cancelled." and ensure the comment sits above the
Cancelled property (public bool Cancelled) in the CancelableEventArgs
definition; also scan for duplicate copies of the CancelableEventArgs class (and
its members Cancel(), Uncancel()) elsewhere in the repo and remove or
consolidate duplicates to avoid multiple definitions.
- Around line 5-14: The shared event class GhostsRecruitmentSuccessEvent has a
naming mismatch and lacks the required base class: rename
GhostsRecruitmentSuccessEvent to GhostRecruitmentSuccessEvent (to match the
server-side symbol) and change its declaration to inherit from EntityEventArgs;
update the constructor signature and any usages to the new name, keep the
[Serializable, NetSerializable] attributes and the RecruitmentName field intact,
and ensure all references (e.g., event firing/listening sites) are updated to
the new class name.

In `@Resources/Locale/en-US/_Maid/auth-panel.ftl`:
- Line 5: Replace the misspelled faction name "NanoTrisen" with the correct
"NanoTrasen" in the localized string within
Resources/Locale/en-US/_Maid/auth-panel.ftl (the line containing "belonging to
NanoTrisen, you are tasked with deploying to a distressed station") so the
player-facing copy shows the correct faction name.

In `@Resources/Locale/ru-RU/_Maid/auth-panel.ftl`:
- Line 7: Replace the awkward phrasing on Line 7 "Особое распространение может
это изменить" with a more natural Russian construction (e.g., "Однако особое
распространение может это изменить" or "Это может измениться при широком
распространении") to fit the surrounding text, and correct the typo on Line 26
by fixing "артилерии" to the intended word (e.g., "артиллерии") so spelling and
grammar are correct throughout the locale file.

In `@Resources/Locale/ru-RU/_Maid/recruitment.ftl`:
- Line 2: The Russian prompt string for the key
accept-ert-window-prompt-text-part is missing a comma for readability; update
the message value to insert a comma after "уверены" so it reads "Если вы не
уверены, то лучше скажите НЕТ!" ensuring only the punctuation change is made and
the key name accept-ert-window-prompt-text-part remains unchanged.

In `@Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml`:
- Around line 46-63: The Janitor GhostRecruitmentSpawnPoint entries are missing
an explicit priority and thus default to 5; update the two
GhostRecruitmentSpawnPoint components (the ones using prototype
RandomHumanoidSpawnerERTJanitor and RandomHumanoidSpawnerERTJanitorEVA in
entities SpawnPointERTEventERTJanitor and SpawnPointERTEventERTJanitorEVA) to
include priority: 2 so they sort alongside other core roles
(Engineer/Security/Medical) in GhostRecruitmentSystem.

In `@Resources/Textures/_Maid/Structures/Wallmounts/auth.rsi/meta.json`:
- Around line 3-4: Update the metadata to provide a proper, specific attribution
for CC-BY-SA-3.0 compliance: replace the current sloppy value in the "copyright"
field with the actual source and author (for example, "Taken from tgstation at
commit <hash>" or "Original art by <artist name> (source: <URL/handle>)"), or if
the exact author/commit is unknown, add a clear note indicating the best-known
source and that the original author is unknown; ensure the "license" and
"copyright" fields remain intact and accurate.

---

Nitpick comments:
In `@Content.Client/_Maid/AuthPanel/AuthPanelMenu.xaml`:
- Around line 27-35: The XAML contains commented-out UI blocks for
AccessContainer/AccessButton and BluespaceWeaponContainer/BluespaceWeaponButton;
remove these commented sections from AuthPanelMenu.xaml if they are not needed,
or alternatively move them into a tracked TODO (or GitHub issue) and add a short
TODO comment in the code-behind (e.g., AuthPanelMenu.xaml.cs) referencing that
issue ID so the intent is preserved; ensure any related names (AccessCount,
BluespaceWeaponCount) are also removed or documented to avoid dead identifiers.

In `@Content.Client/_Maid/AuthPanel/AuthPanelMenu.xaml.cs`:
- Around line 15-53: Remove or formalize the large commented-out UI branches
instead of leaving them inline: delete the commented methods
OnAccessButtonPressed and OnBluespaceWeaponButtonPressed and the commented
SetAccessCount and SetWeaponCount blocks (and the commented visibility lines
inside SetRedCount referencing AccessContainer and BluespaceWeaponContainer), or
replace each block with a short TODO/comment referencing a tracking ticket or
feature flag if the code must be preserved for future work; ensure any preserved
notes mention the exact symbol (e.g., OnAccessButtonPressed,
OnBluespaceWeaponButtonPressed, SetAccessCount, SetWeaponCount, AccessContainer,
BluespaceWeaponContainer) so callers and reviewers can find the rationale.

In `@Content.Server/_Maid/AuthPanel/AuthPanelSystem.cs`:
- Around line 32-34: Make the mutable public fields Counter, CardIndexes and
Reason private (e.g. rename to _counter, _cardIndexes, _reason) and expose
read-only accessors instead: add public properties like
IReadOnlyDictionary<AuthPanelAction, IReadOnlyCollection<EntityUid>> Counter {
get; } and IReadOnlyDictionary<AuthPanelAction, IReadOnlyCollection<int>>
CardIndexes { get; } (or return IReadOnlyDictionary<AuthPanelAction,
IReadOnlyCollection<T>> by wrapping the private Dictionary), and public string
Reason { get; private set; } (or IReadOnlyProperty). Keep the internal
collections as Dictionary<AuthPanelAction, HashSet<...>> for mutation inside
AuthPanelSystem methods (use the private fields _counter/_cardIndexes) and
return read-only wrappers (e.g. .ToDictionary(kv => kv.Key, kv =>
(IReadOnlyCollection<T>)kv.Value) or ReadOnlyCollection) so external code cannot
mutate internal state directly.

In `@Content.Server/_Maid/ERTRecruitment/ERTMapComponent.cs`:
- Line 14: OutpostMap is declared as a mutable static field; change it to an
immutable static readonly field by updating the declaration of OutpostMap (the
public static ResPath OutpostMap in ERTMapComponent) to be static readonly so
the ResPath cannot be reassigned at runtime while keeping the same
initialization value.

In `@Content.Server/_Maid/ERTRecruitment/ERTRecruitedReasonEvent.cs`:
- Around line 3-12: ERTRecruitedReasonEvent currently exposes a mutable
SetReason method which breaks the usual immutable event pattern; change the
class to accept the reason via a constructor parameter and make the Reason field
read-only or a get-only property (replace SetReason with a constructor that sets
Reason) so the event is immutable after creation, and if this event is
transmitted over the network add the [NetSerializable] attribute alongside
[Serializable] to match other event types like GhostRecruitmentSuccessEvent.

In `@Content.Shared/_Maid/CVars/CVars.cs`:
- Around line 56-60: Update the XML doc comment for the public static readonly
CVarDef<bool> LoadErtMap so the grammar is correct and reads clearly (e.g.,
"Whether to generate the ERT map on round start."). Locate the declaration of
LoadErtMap (CVarDef.Create("maid.load_ert_map", true, CVar.SERVERONLY)) and
replace the existing summary text with the corrected sentence.

In
`@Content.Shared/_Maid/GhostRecruitment/GhostRecruitmentSpawnPointComponent.cs`:
- Around line 1-19: The DataField on JobId is missing an explicit key which is
inconsistent with other fields; update the attribute on the JobId field in
GhostRecruitmentSpawnPointComponent (the public string JobId = "Passenger") to
use [DataField("jobId")] so it matches the explicit keys used for
EntityPrototype, RecruitmentName and Priority and ensures consistent
serialization/deserialization.

In `@Content.Shared/_Maid/GhostRecruitment/RecruitedComponent.cs`:
- Around line 4-10: RecruitedComponent duplicates the recruitmentName field
already defined in GhostRecruitedComponent, causing split marker semantics;
consolidate by removing the duplicate state: delete the RecruitmentName field
(and/or the entire RecruitedComponent) and standardize on
GhostRecruitedComponent as the single recruited marker, then update any
systems/prototypes that construct, attach, or query RecruitedComponent to use
GhostRecruitedComponent instead so all recruitment logic references the same
component type.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 995dd34 and 7f6bf95.

⛔ Files ignored due to path filters (2)
  • Resources/Textures/_Maid/Structures/Wallmounts/auth.rsi/auth_off.png is excluded by !**/*.png
  • Resources/Textures/_Maid/Structures/Wallmounts/auth.rsi/auth_on.png is excluded by !**/*.png
📒 Files selected for processing (35)
  • Content.Client/_Maid/AuthPanel/AuthPanelBoundUserInterface.cs
  • Content.Client/_Maid/AuthPanel/AuthPanelMenu.xaml
  • Content.Client/_Maid/AuthPanel/AuthPanelMenu.xaml.cs
  • Content.Client/_Maid/GhostRecruitment/GhostRecruitmentEuiAccept.cs
  • Content.Client/_Maid/GhostRecruitment/GhostRecruitmentEuiAcceptWindow.cs
  • Content.Client/_White/RadialSelector/RadialSelectorMenuBUI.cs
  • Content.Server/Mapping/MappingSystem.cs
  • Content.Server/_Maid/AuthPanel/AuthPanelSystem.cs
  • Content.Server/_Maid/ERTRecruitment/Commands/BlockERT.cs
  • Content.Server/_Maid/ERTRecruitment/ERTMapComponent.cs
  • Content.Server/_Maid/ERTRecruitment/ERTRecruitedReasonEvent.cs
  • Content.Server/_Maid/ERTRecruitment/ERTRecruitmentRule.cs
  • Content.Server/_Maid/ERTRecruitment/ERTRecruitmentRuleComponent.cs
  • Content.Server/_Maid/GhostRecruitment/GhostRecruitmentEuiAccept.cs
  • Content.Server/_Maid/GhostRecruitment/GhostRecruitmentEvents.cs
  • Content.Server/_Maid/GhostRecruitment/GhostRecruitmentSystem.cs
  • Content.Shared/_Maid/AuthPanel/AuthPanelComponent.cs
  • Content.Shared/_Maid/AuthPanel/SharedAuthPanel.cs
  • Content.Shared/_Maid/CVars/CVars.cs
  • Content.Shared/_Maid/GhostRecruitment/GhostRecruitedComponent.cs
  • Content.Shared/_Maid/GhostRecruitment/GhostRecruitmentEuiAccept.cs
  • Content.Shared/_Maid/GhostRecruitment/GhostRecruitmentEvents.cs
  • Content.Shared/_Maid/GhostRecruitment/GhostRecruitmentSpawnPointComponent.cs
  • Content.Shared/_Maid/GhostRecruitment/RecruitedComponent.cs
  • Resources/ConfigPresets/Build/development.toml
  • Resources/Locale/en-US/_Maid/auth-panel.ftl
  • Resources/Locale/en-US/_Maid/recruitment.ftl
  • Resources/Locale/ru-RU/_Maid/auth-panel.ftl
  • Resources/Locale/ru-RU/_Maid/recruitment.ftl
  • Resources/Maps/_Maid/ERTStation.yml
  • Resources/Maps/_Maid/Shuttles/ert_shuttle.yml
  • Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml
  • Resources/Prototypes/_Maid/Entities/Structures/Wallmounts/auth.yml
  • Resources/Prototypes/_Maid/events.yml
  • Resources/Textures/_Maid/Structures/Wallmounts/auth.rsi/meta.json

Comment on lines +124 to +131
var playerCount = _playerManager.PlayerCount;
var ghostCount = ghostList.Count;
var aliveCount = playerCount - ghostCount;

if (aliveCount < playerCount / 2 && ghostList.Count > MinCount)
{
_gameTicker.AddGameRule(ERTRecruitmentRuleComponent.EventName);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Verify player count calculation assumptions.

The calculation aliveCount = playerCount - ghostCount assumes all non-ghost players are "alive" game participants. This may not account for:

  • Admin observers
  • Players in lobby
  • Disconnected sessions

Also, the condition ghostList.Count > MinCount reuses MinCount (vote threshold) for ghost count, which seems like a different concept. Consider a separate threshold constant.

Suggested clarification
+    /// <summary>
+    /// Minimum ghost count required for ERT recruitment.
+    /// </summary>
+    public static int MinGhostCount = 3;
+
     // In OnPerformAction:
-            if (aliveCount < playerCount / 2 && ghostList.Count > MinCount)
+            if (aliveCount < playerCount / 2 && ghostList.Count > MinGhostCount)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Content.Server/_Maid/AuthPanel/AuthPanelSystem.cs` around lines 124 - 131,
The aliveCount calculation and ghost threshold are too simplistic: instead of
using _playerManager.PlayerCount and aliveCount = playerCount - ghostCount
(which counts observers, lobby users, or disconnected sessions as "alive"),
compute the participant set explicitly (e.g., iterate
_playerManager.GetAllPlayers() or use a connected/active players API and filter
out admin observers/lobby/disconnected) and derive aliveCount from that filtered
list; also stop reusing MinCount for ghost threshold—introduce a distinct
constant (e.g., MinGhostsThreshold) and replace the condition ghostList.Count >
MinCount with ghostList.Count >= MinGhostsThreshold before calling
_gameTicker.AddGameRule(ERTRecruitmentRuleComponent.EventName).

.desc = If something goes wrong...

ert-description = As a member of the Emergency Response Team,
belonging to NanoTrisen, you are tasked with deploying to a distressed station
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix faction name typo in localized text.

Line 5 uses NanoTrisen; this should be NanoTrasen in player-facing copy.

📝 Proposed fix
-    belonging to NanoTrisen, you are tasked with deploying to a distressed station
+    belonging to NanoTrasen, you are tasked with deploying to a distressed station
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
belonging to NanoTrisen, you are tasked with deploying to a distressed station
belonging to NanoTrasen, you are tasked with deploying to a distressed station
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Resources/Locale/en-US/_Maid/auth-panel.ftl` at line 5, Replace the
misspelled faction name "NanoTrisen" with the correct "NanoTrasen" in the
localized string within Resources/Locale/en-US/_Maid/auth-panel.ftl (the line
containing "belonging to NanoTrisen, you are tasked with deploying to a
distressed station") so the player-facing copy shows the correct faction name.

@@ -0,0 +1,6 @@
accept-ert-window-title = Набор в отряды ОБР!
accept-ert-window-prompt-text-part = Готовы ли вы стать частью отряда ОБР? Если вы не уверены то лучше скажите НЕТ!
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add missing comma in RU prompt text.

For readability, add a comma after уверены.

📝 Proposed fix
-accept-ert-window-prompt-text-part = Готовы ли вы стать частью отряда ОБР? Если вы не уверены то лучше скажите НЕТ!
+accept-ert-window-prompt-text-part = Готовы ли вы стать частью отряда ОБР? Если вы не уверены, то лучше скажите НЕТ!
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
accept-ert-window-prompt-text-part = Готовы ли вы стать частью отряда ОБР? Если вы не уверены то лучше скажите НЕТ!
accept-ert-window-prompt-text-part = Готовы ли вы стать частью отряда ОБР? Если вы не уверены, то лучше скажите НЕТ!
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Resources/Locale/ru-RU/_Maid/recruitment.ftl` at line 2, The Russian prompt
string for the key accept-ert-window-prompt-text-part is missing a comma for
readability; update the message value to insert a comma after "уверены" so it
reads "Если вы не уверены, то лучше скажите НЕТ!" ensuring only the punctuation
change is made and the key name accept-ert-window-prompt-text-part remains
unchanged.

Comment on lines +46 to +63
- type: GhostRecruitmentSpawnPoint
prototype: RandomHumanoidSpawnerERTJanitor
recruitmentName: ERTRecruitment
jobId: ERTJanitor
- type: Sprite
layers:
- state: green
- state: ertjanitor

- type: entity
parent: SpawnPointEventBase
id: SpawnPointERTEventERTJanitorEVA
name: ERTJanitorEVA
components:
- type: GhostRecruitmentSpawnPoint
prototype: RandomHumanoidSpawnerERTJanitorEVA
recruitmentName: ERTRecruitment
jobId: ERTJanitor
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the sorting logic in GhostRecruitmentSystem.cs
echo "=== Sorting logic in GhostRecruitmentSystem.cs ==="
rg -n "OrderBy.*Priority" Content.Server/_Maid/GhostRecruitment/GhostRecruitmentSystem.cs

echo ""
echo "=== GhostRecruitmentSpawnPointComponent definition ==="
rg -A 10 "class GhostRecruitmentSpawnPointComponent" -g '*.cs'

echo ""
echo "=== Priority field in component ==="
rg -B 2 -A 2 "public.*Priority|Priority.*=" -g '*.cs' | grep -A 2 -B 2 "GhostRecruitmentSpawnPoint"

echo ""
echo "=== YAML spawner entries (context around priority) ==="
rg -n -C 3 "SpawnPointERTEventERTJanitor\|priority:" Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml

Repository: TheMaidDev/Maid-14

Length of output: 2249


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# List the file to confirm it exists
ls -lh Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml

echo ""
echo "=== Full YAML file content ==="
cat -n Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml

echo ""
echo "=== Search for all priority fields ==="
rg -n "priority:" Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml

echo ""
echo "=== Search for all SpawnPointERT entries ==="
rg -n "id: SpawnPointERT" Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml

Repository: TheMaidDev/Maid-14

Length of output: 5859


Janitor spawners missing explicit priority default to 5, placing them after other core roles.

Spawners are sorted by ascending priority in Content.Server/_Maid/GhostRecruitment/GhostRecruitmentSystem.cs (Line 77). Leader roles have priority: 1, and Engineer/Security/Medical all have priority: 2, but Janitor entries on Lines 46 and 60 lack explicit priority, causing them to default to 5, which places them at the lowest priority. Add priority: 2 to make the assignment order consistent with other core roles.

🔧 Proposed fix
 - type: entity
   parent: SpawnPointEventBase
   id: SpawnPointERTEventERTJanitor
   name: ERTJanitor
   components:
     - type: GhostRecruitmentSpawnPoint
       prototype: RandomHumanoidSpawnerERTJanitor
       recruitmentName: ERTRecruitment
+      priority: 2
       jobId: ERTJanitor
 - type: entity
   parent: SpawnPointEventBase
   id: SpawnPointERTEventERTJanitorEVA
   name: ERTJanitorEVA
   components:
     - type: GhostRecruitmentSpawnPoint
       prototype: RandomHumanoidSpawnerERTJanitorEVA
       recruitmentName: ERTRecruitment
+      priority: 2
       jobId: ERTJanitor
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- type: GhostRecruitmentSpawnPoint
prototype: RandomHumanoidSpawnerERTJanitor
recruitmentName: ERTRecruitment
jobId: ERTJanitor
- type: Sprite
layers:
- state: green
- state: ertjanitor
- type: entity
parent: SpawnPointEventBase
id: SpawnPointERTEventERTJanitorEVA
name: ERTJanitorEVA
components:
- type: GhostRecruitmentSpawnPoint
prototype: RandomHumanoidSpawnerERTJanitorEVA
recruitmentName: ERTRecruitment
jobId: ERTJanitor
- type: GhostRecruitmentSpawnPoint
prototype: RandomHumanoidSpawnerERTJanitor
recruitmentName: ERTRecruitment
priority: 2
jobId: ERTJanitor
- type: Sprite
layers:
- state: green
- state: ertjanitor
- type: entity
parent: SpawnPointEventBase
id: SpawnPointERTEventERTJanitorEVA
name: ERTJanitorEVA
components:
- type: GhostRecruitmentSpawnPoint
prototype: RandomHumanoidSpawnerERTJanitorEVA
recruitmentName: ERTRecruitment
priority: 2
jobId: ERTJanitor
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml` around lines 46
- 63, The Janitor GhostRecruitmentSpawnPoint entries are missing an explicit
priority and thus default to 5; update the two GhostRecruitmentSpawnPoint
components (the ones using prototype RandomHumanoidSpawnerERTJanitor and
RandomHumanoidSpawnerERTJanitorEVA in entities SpawnPointERTEventERTJanitor and
SpawnPointERTEventERTJanitorEVA) to include priority: 2 so they sort alongside
other core roles (Engineer/Security/Medical) in GhostRecruitmentSystem.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All good, that's how it should be

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RedBurningPhoenix, understood! The Janitor spawners intentionally use the default priority: 5, placing them at a lower recruitment priority than other core ERT roles. I'll keep this in mind for future reviews.

( 🐰 nods thoughtfully


✏️ Learnings added
Learnt from: RedBurningPhoenix
Repo: TheMaidDev/Maid-14 PR: 31
File: Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml:46-63
Timestamp: 2026-03-11T10:18:43.655Z
Learning: In `Resources/Prototypes/_Maid/Entities/Markers/Spawners/ert.yml` (repo: TheMaidDev/Maid-14), ERT Janitor spawners (`SpawnPointERTEventERTJanitor` and `SpawnPointERTEventERTJanitorEVA`) intentionally omit an explicit `priority` field, defaulting to `5`. This is by design — Janitor roles have lower ghost recruitment priority than other core ERT roles (Leader: 1, Engineer/Security/Medical: 2).

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +3 to +4
"license": "CC-BY-SA-3.0",
"copyright": "from TG ya hui znaet",
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Clarify the copyright attribution for license compliance.

The copyright field "from TG ya hui znaet" (colloquial Russian for "from TG, who knows") does not provide proper attribution. CC-BY-SA-3.0 requires clear attribution to the original author. Please update with the actual source and author information (e.g., "Taken from tgstation at commit X" or the original artist's name).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Resources/Textures/_Maid/Structures/Wallmounts/auth.rsi/meta.json` around
lines 3 - 4, Update the metadata to provide a proper, specific attribution for
CC-BY-SA-3.0 compliance: replace the current sloppy value in the "copyright"
field with the actual source and author (for example, "Taken from tgstation at
commit <hash>" or "Original art by <artist name> (source: <URL/handle>)"), or if
the exact author/commit is unknown, add a clear note indicating the best-known
source and that the original author is unknown; ensure the "license" and
"copyright" fields remain intact and accurate.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поменяем панель

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RedBurningPhoenix, понял — если панель будет заменена на собственный ассет, проблема с атрибуцией исчезнет сама собой. Если же будет использоваться ассет из другого репозитория (например, tgstation), не забудьте указать корректную атрибуцию в copyright поле, например:

"copyright": "Taken from tgstation, original author <name> (https://github.com/tgstation/tgstation/commit/<hash>)"

@RedBurningPhoenix
Copy link
Copy Markdown
Collaborator

Linter в параллели отвечает что нет определённых полей

_resMan.UserData.CreateDir(new ResPath(saveDir).ToRootedPath());

var path = new ResPath(Path.Combine(saveDir, $"{DateTime.Now:yyyy-M-dd_HH.mm.ss}-AUTO.yml"));
var path = new ResPath($"{saveDir}/{DateTime.Now:yyyy-M-dd_HH.mm.ss}-AUTO.yml");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

???

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А, забыл про это, у меня крашалась игра из за этого, убрал


_menu.OnRedButtonPressed(_ => SendButtonPressed(AuthPanelAction.ERTRecruit));
// _menu.OnAccessButtonPressed(_ => SendButtonPressed(AuthPanelAction.AddAccess));
// _menu.OnBluespaceWeaponButtonPressed(_ => SendButtonPressed(AuthPanelAction.BluespaceWeapon));
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Блюспейс арты точно не планируется, вырезать и всё.

Comment on lines +45 to +46
// if (action.Action is AuthPanelAction.BluespaceWeapon)
// _menu?.SetWeaponCount(action.ConfirmedPeopleCount, action.MaxConfirmedPeopleCount);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аналогично про блюспейс

Comment on lines +27 to +35
<!-- <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>

<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> -->
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вырезать

Comment on lines +15 to +23
// public void OnAccessButtonPressed(Action<BaseButton.ButtonEventArgs> func)
// {
// AccessButton.OnPressed += func;
// }

// public void OnBluespaceWeaponButtonPressed(Action<BaseButton.ButtonEventArgs> func)
// {
// BluespaceWeaponButton.OnPressed += func;
// }
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Аналогично

Comment on lines +31 to +37
public void SetRedCount(int conf, int maxconf)
{
SetCount(RedCount, conf, maxconf);
RedButton.Disabled = conf >= maxconf;
// AccessContainer.Visible = false;
// BluespaceWeaponContainer.Visible = false;
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Самой красной кнопки нет, под корень

Comment on lines +39 to +53
// 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;
// }
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Сюда же

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants