Role Types (#33420)

* mindcomponent namespace

* wip MindRole stuff

* admin player tab

* mindroletype comment

* mindRolePrototype redesign

* broken param

* wip RoleType implementation

* basic role type switching for antags

* traitor fix

* fix AdminPanel update

* the renameningTM

* cleanup

* feature uncreeping

* roletypes on mind roles

* update MindComponent.RoleType when MindRoles change

* ghostrole configuration

* ghostrole config improvements

* live update of roleType on the character window

* logging stuff and notes

* remove thing no one asked for

* weh

* Mind Role Entities wip

* headrev count fix

* silicon stuff, cleanup

* exclusive antag config, cleanup

* jobroleadd overwerite

* logging stuff

* MindHasRole cleanup, admin log stuff

* last second cleanup

* ocd

* move roletypeprototype to its own file, minor note stuff

* remove Roletype.Created

* log stuff

* roletype setup for ghostroles and autotraitor reinforcements

* ghostrole type configs

* adjustable admin overlay

* cleanup

* fix this in its own PR

* silicon antagonist

* borg stuff

* mmi roletype handling

* spawnable borg roletype handling

* weh

* ghost role cleanup

* weh

* RoleEvent update

* polish

* log stuff

* admin overlay config

* ghostrolecomponent cleanup

* weh

* admin overlay code cleanup

* minor cleanup

* Obsolete MindRoleAddedEvent

* comment

* minor code cleanup

* MindOnDoGreeting fix

* Role update message

* fix duplicate job greeting for cyborgs

* fix emag job message dupe

* nicer-looking role type update

* crew aligned

* syndicate assault borg role fix

* fix test fail

* fix a merge mistake

* fix LoneOp role type

* Update Content.Client/Administration/AdminNameOverlay.cs

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Update Content.Shared/Roles/SharedRoleSystem.cs

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* comment formatting

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* change logging category

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* fix a space

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* use MindAddRoles

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* get MindComponent from TryGetMind

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* move var declaration outside loop

* remove TryComp

* take RoleEnum behind the barn

* don't use ensurecomp unnecessarily

* cvar comments

* toggleableghostrolecomponent documentation

* skrek

* use EntProtoId

* mindrole config

* merge baserolecomponent into basemindrolecomponent

* ai and borg silicon role tweaks

* formatting

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* I will end you (the color)

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* use LocId type for a locale id

* update RoleEvent documentation

* update RoleEvent documentation

* remove obsolete MindRoleAddedEvent

* refine MindRolesUpdate()

* use dependency

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* inject dependency

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* roleType.Name no longer required

* reformatted draw code logic

* GhostRoleMarkerRoleComponent comment

* minor SharedRoleSystem cleanup

* StartingMindRoleComponent, unhardcode roundstart silicon

* Update Content.Shared/Roles/SharedRoleSystem.cs

* remove a whitespace

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
This commit is contained in:
Errant
2025-01-11 22:17:26 +01:00
committed by GitHub
parent 3da354304d
commit 46d58bf22a
65 changed files with 744 additions and 106 deletions

View File

@@ -1,16 +1,21 @@
using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Client.Administration.Systems; using Content.Client.Administration.Systems;
using Content.Shared.CCVar;
using Content.Shared.Mind;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Shared;
using Robust.Shared.Enums;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
namespace Content.Client.Administration; namespace Content.Client.Administration;
internal sealed class AdminNameOverlay : Overlay internal sealed class AdminNameOverlay : Overlay
{ {
[Dependency] private readonly IConfigurationManager _config = default!;
private readonly AdminSystem _system; private readonly AdminSystem _system;
private readonly IEntityManager _entityManager; private readonly IEntityManager _entityManager;
private readonly IEyeManager _eyeManager; private readonly IEyeManager _eyeManager;
@@ -18,8 +23,16 @@ internal sealed class AdminNameOverlay : Overlay
private readonly IUserInterfaceManager _userInterfaceManager; private readonly IUserInterfaceManager _userInterfaceManager;
private readonly Font _font; private readonly Font _font;
//TODO make this adjustable via GUI
private readonly ProtoId<RoleTypePrototype>[] _filter =
["SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"];
private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic");
private readonly Color _antagColorClassic = Color.OrangeRed;
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup, IUserInterfaceManager userInterfaceManager) public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup, IUserInterfaceManager userInterfaceManager)
{ {
IoCManager.InjectDependencies(this);
_system = system; _system = system;
_entityManager = entityManager; _entityManager = entityManager;
_eyeManager = eyeManager; _eyeManager = eyeManager;
@@ -35,6 +48,9 @@ internal sealed class AdminNameOverlay : Overlay
{ {
var viewport = args.WorldAABB; var viewport = args.WorldAABB;
//TODO make this adjustable via GUI
var classic = _config.GetCVar(CCVars.AdminOverlayClassic);
foreach (var playerInfo in _system.PlayerList) foreach (var playerInfo in _system.PlayerList)
{ {
var entity = _entityManager.GetEntity(playerInfo.NetEntity); var entity = _entityManager.GetEntity(playerInfo.NetEntity);
@@ -64,12 +80,20 @@ internal sealed class AdminNameOverlay : Overlay
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center + var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec( new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f); aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
if (playerInfo.Antag)
if (classic && playerInfo.Antag)
{ {
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), "ANTAG", uiScale, Color.OrangeRed); args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), _antagLabelClassic, uiScale, _antagColorClassic);
;
} }
args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White); else if (!classic && _filter.Contains(playerInfo.RoleProto.ID))
{
var label = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
var color = playerInfo.RoleProto.Color;
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), label, uiScale, color);
}
args.ScreenHandle.DrawString(_font, screenCoordinates + lineoffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White); args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
} }
} }

View File

@@ -197,6 +197,7 @@ public sealed partial class PlayerTab : Control
Header.Character => Compare(x.CharacterName, y.CharacterName), Header.Character => Compare(x.CharacterName, y.CharacterName),
Header.Job => Compare(x.StartingJob, y.StartingJob), Header.Job => Compare(x.StartingJob, y.StartingJob),
Header.Antagonist => x.Antag.CompareTo(y.Antag), Header.Antagonist => x.Antag.CompareTo(y.Antag),
Header.RoleType => Compare(x.RoleProto.Name , y.RoleProto.Name),
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default), Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
_ => 1 _ => 1
}; };

View File

@@ -24,6 +24,11 @@
HorizontalExpand="True" HorizontalExpand="True"
ClipText="True"/> ClipText="True"/>
<customControls:VSeparator/> <customControls:VSeparator/>
<Label Name="RoleTypeLabel"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Label Name="OverallPlaytimeLabel" <Label Name="OverallPlaytimeLabel"
SizeFlagsStretchRatio="1" SizeFlagsStretchRatio="1"
HorizontalExpand="True" HorizontalExpand="True"

View File

@@ -23,6 +23,8 @@ public sealed partial class PlayerTabEntry : PanelContainer
if (player.IdentityName != player.CharacterName) if (player.IdentityName != player.CharacterName)
CharacterLabel.Text += $" [{player.IdentityName}]"; CharacterLabel.Text += $" [{player.IdentityName}]";
AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no"); AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no");
RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name);
RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
BackgroundColorPanel.PanelOverride = styleBoxFlat; BackgroundColorPanel.PanelOverride = styleBoxFlat;
OverallPlaytimeLabel.Text = player.PlaytimeString; OverallPlaytimeLabel.Text = player.PlaytimeString;
PlayerEntity = player.NetEntity; PlayerEntity = player.NetEntity;

View File

@@ -32,6 +32,13 @@
Text="{Loc player-tab-antagonist}" Text="{Loc player-tab-antagonist}"
MouseFilter="Pass"/> MouseFilter="Pass"/>
<cc:VSeparator/> <cc:VSeparator/>
<Label Name="RoleTypeLabel"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"
ClipText="True"
Text="{Loc player-tab-roletype}"
MouseFilter="Pass"/>
<cc:VSeparator/>
<Label Name="PlaytimeLabel" <Label Name="PlaytimeLabel"
SizeFlagsStretchRatio="1" SizeFlagsStretchRatio="1"
HorizontalExpand="True" HorizontalExpand="True"

View File

@@ -19,6 +19,7 @@ public sealed partial class PlayerTabHeader : Control
CharacterLabel.OnKeyBindDown += CharacterClicked; CharacterLabel.OnKeyBindDown += CharacterClicked;
JobLabel.OnKeyBindDown += JobClicked; JobLabel.OnKeyBindDown += JobClicked;
AntagonistLabel.OnKeyBindDown += AntagonistClicked; AntagonistLabel.OnKeyBindDown += AntagonistClicked;
RoleTypeLabel.OnKeyBindDown += RoleTypeClicked;
PlaytimeLabel.OnKeyBindDown += PlaytimeClicked; PlaytimeLabel.OnKeyBindDown += PlaytimeClicked;
} }
@@ -30,6 +31,7 @@ public sealed partial class PlayerTabHeader : Control
Header.Character => CharacterLabel, Header.Character => CharacterLabel,
Header.Job => JobLabel, Header.Job => JobLabel,
Header.Antagonist => AntagonistLabel, Header.Antagonist => AntagonistLabel,
Header.RoleType => RoleTypeLabel,
Header.Playtime => PlaytimeLabel, Header.Playtime => PlaytimeLabel,
_ => throw new ArgumentOutOfRangeException(nameof(header), header, null) _ => throw new ArgumentOutOfRangeException(nameof(header), header, null)
}; };
@@ -41,6 +43,7 @@ public sealed partial class PlayerTabHeader : Control
CharacterLabel.Text = Loc.GetString("player-tab-character"); CharacterLabel.Text = Loc.GetString("player-tab-character");
JobLabel.Text = Loc.GetString("player-tab-job"); JobLabel.Text = Loc.GetString("player-tab-job");
AntagonistLabel.Text = Loc.GetString("player-tab-antagonist"); AntagonistLabel.Text = Loc.GetString("player-tab-antagonist");
RoleTypeLabel.Text = Loc.GetString("player-tab-roletype");
PlaytimeLabel.Text = Loc.GetString("player-tab-playtime"); PlaytimeLabel.Text = Loc.GetString("player-tab-playtime");
} }
@@ -75,6 +78,11 @@ public sealed partial class PlayerTabHeader : Control
HeaderClicked(args, Header.Antagonist); HeaderClicked(args, Header.Antagonist);
} }
private void RoleTypeClicked(GUIBoundKeyEventArgs args)
{
HeaderClicked(args, Header.RoleType);
}
private void PlaytimeClicked(GUIBoundKeyEventArgs args) private void PlaytimeClicked(GUIBoundKeyEventArgs args)
{ {
HeaderClicked(args, Header.Playtime); HeaderClicked(args, Header.Playtime);
@@ -90,6 +98,7 @@ public sealed partial class PlayerTabHeader : Control
CharacterLabel.OnKeyBindDown -= CharacterClicked; CharacterLabel.OnKeyBindDown -= CharacterClicked;
JobLabel.OnKeyBindDown -= JobClicked; JobLabel.OnKeyBindDown -= JobClicked;
AntagonistLabel.OnKeyBindDown -= AntagonistClicked; AntagonistLabel.OnKeyBindDown -= AntagonistClicked;
RoleTypeLabel.OnKeyBindDown -= RoleTypeClicked;
PlaytimeLabel.OnKeyBindDown -= PlaytimeClicked; PlaytimeLabel.OnKeyBindDown -= PlaytimeClicked;
} }
} }
@@ -100,6 +109,7 @@ public sealed partial class PlayerTabHeader : Control
Character, Character,
Job, Job,
Antagonist, Antagonist,
RoleType,
Playtime Playtime
} }
} }

View File

@@ -7,7 +7,9 @@ using Content.Client.UserInterface.Systems.Character.Controls;
using Content.Client.UserInterface.Systems.Character.Windows; using Content.Client.UserInterface.Systems.Character.Windows;
using Content.Client.UserInterface.Systems.Objectives.Controls; using Content.Client.UserInterface.Systems.Objectives.Controls;
using Content.Shared.Input; using Content.Shared.Input;
using Content.Shared.Objectives.Systems; using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Roles;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Player; using Robust.Client.Player;
@@ -15,6 +17,7 @@ using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers; using Robust.Client.UserInterface.Controllers;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input.Binding; using Robust.Shared.Input.Binding;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using static Content.Client.CharacterInfo.CharacterInfoSystem; using static Content.Client.CharacterInfo.CharacterInfoSystem;
using static Robust.Client.UserInterface.Controls.BaseButton; using static Robust.Client.UserInterface.Controls.BaseButton;
@@ -24,10 +27,25 @@ namespace Content.Client.UserInterface.Systems.Character;
[UsedImplicitly] [UsedImplicitly]
public sealed class CharacterUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>, IOnSystemChanged<CharacterInfoSystem> public sealed class CharacterUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>, IOnSystemChanged<CharacterInfoSystem>
{ {
[Dependency] private readonly IEntityManager _ent = default!;
[Dependency] private readonly ILogManager _logMan = default!;
[Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[UISystemDependency] private readonly CharacterInfoSystem _characterInfo = default!; [UISystemDependency] private readonly CharacterInfoSystem _characterInfo = default!;
[UISystemDependency] private readonly SpriteSystem _sprite = default!; [UISystemDependency] private readonly SpriteSystem _sprite = default!;
private ISawmill _sawmill = default!;
public override void Initialize()
{
base.Initialize();
_sawmill = _logMan.GetSawmill("character");
SubscribeNetworkEvent<MindRoleTypeChangedEvent>(OnRoleTypeChanged);
}
private CharacterWindow? _window; private CharacterWindow? _window;
private MenuButton? CharacterButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.CharacterButton; private MenuButton? CharacterButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.CharacterButton;
@@ -110,6 +128,9 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
var (entity, job, objectives, briefing, entityName) = data; var (entity, job, objectives, briefing, entityName) = data;
_window.SpriteView.SetEntity(entity); _window.SpriteView.SetEntity(entity);
UpdateRoleType();
_window.NameLabel.Text = entityName; _window.NameLabel.Text = entityName;
_window.SubText.Text = job; _window.SubText.Text = job;
_window.Objectives.RemoveAllChildren(); _window.Objectives.RemoveAllChildren();
@@ -173,6 +194,37 @@ public sealed class CharacterUIController : UIController, IOnStateEntered<Gamepl
_window.RolePlaceholder.Visible = briefing == null && !controls.Any() && !objectives.Any(); _window.RolePlaceholder.Visible = briefing == null && !controls.Any() && !objectives.Any();
} }
private void OnRoleTypeChanged(MindRoleTypeChangedEvent ev, EntitySessionEventArgs _)
{
UpdateRoleType();
}
private void UpdateRoleType()
{
if (_window == null || !_window.IsOpen)
return;
if (!_ent.TryGetComponent<MindContainerComponent>(_player.LocalEntity, out var container)
|| container.Mind is null)
return;
if (!_ent.TryGetComponent<MindComponent>(container.Mind.Value, out var mind))
return;
var roleText = Loc.GetString("role-type-crew-aligned-name");
var color = Color.White;
if (_prototypeManager.TryIndex(mind.RoleType, out var proto))
{
roleText = Loc.GetString(proto.Name);
color = proto.Color;
}
else
_sawmill.Error($"{_player.LocalEntity} has invalid Role Type '{mind.RoleType}'. Displaying '{roleText}' instead");
_window.RoleType.Text = roleText;
_window.RoleType.FontColorOverride = color;
}
private void CharacterDetached(EntityUid uid) private void CharacterDetached(EntityUid uid)
{ {
CloseWindow(); CloseWindow();

View File

@@ -7,6 +7,7 @@
MinHeight="545"> MinHeight="545">
<ScrollContainer> <ScrollContainer>
<BoxContainer Orientation="Vertical"> <BoxContainer Orientation="Vertical">
<Label Name="RoleType" VerticalAlignment="Top" Margin="0 6 0 10" HorizontalAlignment="Center" StyleClasses="LabelHeading" Access="Public"/>
<BoxContainer Orientation="Horizontal"> <BoxContainer Orientation="Horizontal">
<SpriteView OverrideDirection="South" Scale="2 2" Name="SpriteView" Access="Public" SetSize="64 64"/> <SpriteView OverrideDirection="South" Scale="2 2" Name="SpriteView" Access="Public" SetSize="64 64"/>
<BoxContainer Orientation="Vertical" VerticalAlignment="Top"> <BoxContainer Orientation="Vertical" VerticalAlignment="Top">

View File

@@ -17,7 +17,6 @@ using Content.Shared.IdentityManagement;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.PDA; using Content.Shared.PDA;
using Content.Shared.Players;
using Content.Shared.Players.PlayTimeTracking; using Content.Shared.Players.PlayTimeTracking;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Roles; using Content.Shared.Roles;
@@ -32,6 +31,7 @@ using Robust.Shared.Configuration;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Server.Administration.Systems; namespace Content.Server.Administration.Systems;
@@ -48,6 +48,7 @@ public sealed class AdminSystem : EntitySystem
[Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly PhysicsSystem _physics = default!; [Dependency] private readonly PhysicsSystem _physics = default!;
[Dependency] private readonly PlayTimeTrackingManager _playTime = default!; [Dependency] private readonly PlayTimeTrackingManager _playTime = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedRoleSystem _role = default!; [Dependency] private readonly SharedRoleSystem _role = default!;
[Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
@@ -165,7 +166,8 @@ public sealed class AdminSystem : EntitySystem
private void OnRoleEvent(RoleEvent ev) private void OnRoleEvent(RoleEvent ev)
{ {
var session = _minds.GetSession(ev.Mind); var session = _minds.GetSession(ev.Mind);
if (!ev.Antagonist || session == null)
if (!ev.RoleTypeUpdate || session == null)
return; return;
UpdatePlayerList(session); UpdatePlayerList(session);
@@ -239,9 +241,16 @@ public sealed class AdminSystem : EntitySystem
} }
var antag = false; var antag = false;
RoleTypePrototype roleType = new();
var startingRole = string.Empty; var startingRole = string.Empty;
if (_minds.TryGetMind(session, out var mindId, out _)) if (_minds.TryGetMind(session, out var mindId, out var mindComp))
{ {
if (_proto.TryIndex(mindComp.RoleType, out var role))
roleType = role;
else
Log.Error($"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(roleType.Name)}' instead");
antag = _role.MindIsAntagonist(mindId); antag = _role.MindIsAntagonist(mindId);
startingRole = _jobs.MindTryGetJobName(mindId); startingRole = _jobs.MindTryGetJobName(mindId);
} }
@@ -255,7 +264,7 @@ public sealed class AdminSystem : EntitySystem
overallPlaytime = playTime; overallPlaytime = playTime;
} }
return new PlayerInfo(name, entityName, identityName, startingRole, antag, GetNetEntity(session?.AttachedEntity), data.UserId, return new PlayerInfo(name, entityName, identityName, startingRole, antag, roleType, GetNetEntity(session?.AttachedEntity), data.UserId,
connected, _roundActivePlayers.Contains(data.UserId), overallPlaytime); connected, _roundActivePlayers.Contains(data.UserId), overallPlaytime);
} }

View File

@@ -153,7 +153,7 @@ public partial struct AntagSelectionDefinition()
/// List of Mind Role Prototypes to be added to the player's mind. /// List of Mind Role Prototypes to be added to the player's mind.
/// </summary> /// </summary>
[DataField] [DataField]
public List<ProtoId<EntityPrototype>>? MindRoles; public List<EntProtoId>? MindRoles;
/// <summary> /// <summary>
/// A set of starting gear that's equipped to the player. /// A set of starting gear that's equipped to the player.

View File

@@ -222,8 +222,6 @@ namespace Content.Server.GameTicking
_mind.SetUserId(newMind, data.UserId); _mind.SetUserId(newMind, data.UserId);
var jobPrototype = _prototypeManager.Index<JobPrototype>(jobId); var jobPrototype = _prototypeManager.Index<JobPrototype>(jobId);
_roles.MindAddJobRole(newMind, silent: silent, jobPrototype:jobId);
var jobName = _jobs.MindTryGetJobName(newMind);
_playTimeTrackings.PlayerRolesChanged(player); _playTimeTrackings.PlayerRolesChanged(player);
@@ -233,6 +231,9 @@ namespace Content.Server.GameTicking
_mind.TransferTo(newMind, mob); _mind.TransferTo(newMind, mob);
_roles.MindAddJobRole(newMind, silent: silent, jobPrototype:jobId);
var jobName = _jobs.MindTryGetJobName(newMind);
if (lateJoin && !silent) if (lateJoin && !silent)
{ {
if (jobPrototype.JoinNotifyCrew) if (jobPrototype.JoinNotifyCrew)

View File

@@ -1,10 +1,12 @@
namespace Content.Server.Ghost; using Content.Shared.Roles;
namespace Content.Server.Ghost;
/// <summary> /// <summary>
/// This is used to mark Observers properly, as they get Minds /// This is used to mark Observers properly, as they get Minds
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
public sealed partial class ObserverRoleComponent : Component public sealed partial class ObserverRoleComponent : BaseMindRoleComponent
{ {
public string Name => Loc.GetString("observer-role-name"); public string Name => Loc.GetString("observer-role-name");
} }

View File

@@ -72,12 +72,16 @@ public sealed partial class GhostRoleComponent : Component
} }
} }
[DataField("allowSpeech")] /// <summary>
[ViewVariables(VVAccess.ReadWrite)] /// The mind roles that will be added to the mob's mind entity
/// </summary>
[DataField, Access(typeof(GhostRoleSystem), Other = AccessPermissions.ReadWriteExecute)] // Don't make eye contact
public List<EntProtoId> MindRoles = new() { "MindRoleGhostRoleNeutral" };
[DataField]
public bool AllowSpeech { get; set; } = true; public bool AllowSpeech { get; set; } = true;
[DataField("allowMovement")] [DataField]
[ViewVariables(VVAccess.ReadWrite)]
public bool AllowMovement { get; set; } public bool AllowMovement { get; set; }
[ViewVariables(VVAccess.ReadOnly)] [ViewVariables(VVAccess.ReadOnly)]
@@ -107,3 +111,4 @@ public sealed partial class GhostRoleComponent : Component
[Access(typeof(GhostRoleSystem), Other = AccessPermissions.ReadWriteExecute)] // also FIXME Friends [Access(typeof(GhostRoleSystem), Other = AccessPermissions.ReadWriteExecute)] // also FIXME Friends
public ProtoId<JobPrototype>? JobProto = null; public ProtoId<JobPrototype>? JobProto = null;
} }

View File

@@ -9,39 +9,81 @@ namespace Content.Server.Ghost.Roles.Components;
[RegisterComponent, Access(typeof(ToggleableGhostRoleSystem))] [RegisterComponent, Access(typeof(ToggleableGhostRoleSystem))]
public sealed partial class ToggleableGhostRoleComponent : Component public sealed partial class ToggleableGhostRoleComponent : Component
{ {
[DataField("examineTextMindPresent")] /// <summary>
/// The text shown on the entity's Examine when it is controlled by a player
/// </summary>
[DataField]
public string ExamineTextMindPresent = string.Empty; public string ExamineTextMindPresent = string.Empty;
[DataField("examineTextMindSearching")] /// <summary>
/// The text shown on the entity's Examine when it is waiting for a controlling player
/// </summary>
[DataField]
public string ExamineTextMindSearching = string.Empty; public string ExamineTextMindSearching = string.Empty;
[DataField("examineTextNoMind")] /// <summary>
/// The text shown on the entity's Examine when it has no controlling player
/// </summary>
[DataField]
public string ExamineTextNoMind = string.Empty; public string ExamineTextNoMind = string.Empty;
[DataField("beginSearchingText")] /// <summary>
/// The popup text when the entity (PAI/positronic brain) it is activated to seek a controlling player
/// </summary>
[DataField]
public string BeginSearchingText = string.Empty; public string BeginSearchingText = string.Empty;
[DataField("roleName")] /// <summary>
/// The name shown on the Ghost Role list
/// </summary>
[DataField]
public string RoleName = string.Empty; public string RoleName = string.Empty;
[DataField("roleDescription")] /// <summary>
/// The description shown on the Ghost Role list
/// </summary>
[DataField]
public string RoleDescription = string.Empty; public string RoleDescription = string.Empty;
[DataField("roleRules")] /// <summary>
/// The introductory message shown when trying to take the ghost role/join the raffle
/// </summary>
[DataField]
public string RoleRules = string.Empty; public string RoleRules = string.Empty;
[DataField("wipeVerbText")] /// <summary>
/// A list of mind roles that will be added to the entity's mind
/// </summary>
[DataField]
public List<EntProtoId> MindRoles;
/// <summary>
/// The displayed name of the verb to wipe the controlling player
/// </summary>
[DataField]
public string WipeVerbText = string.Empty; public string WipeVerbText = string.Empty;
[DataField("wipeVerbPopup")] /// /// <summary>
/// The popup message when wiping the controlling player
/// </summary>
[DataField]
public string WipeVerbPopup = string.Empty; public string WipeVerbPopup = string.Empty;
[DataField("stopSearchVerbText")] /// <summary>
/// The displayed name of the verb to stop searching for a controlling player
/// </summary>
[DataField]
public string StopSearchVerbText = string.Empty; public string StopSearchVerbText = string.Empty;
[DataField("stopSearchVerbPopup")] /// /// <summary>
/// The popup message when stopping to search for a controlling player
/// </summary>
[DataField]
public string StopSearchVerbPopup = string.Empty; public string StopSearchVerbPopup = string.Empty;
/// /// <summary>
/// The prototype ID of the job that will be given to the controlling mind
/// </summary>
[DataField("job")] [DataField("job")]
public ProtoId<JobPrototype>? JobProto = null; public ProtoId<JobPrototype>? JobProto;
} }

View File

@@ -3,11 +3,13 @@
namespace Content.Server.Ghost.Roles; namespace Content.Server.Ghost.Roles;
/// <summary> /// <summary>
/// This is used for round end display of ghost roles. /// Added to mind role entities to tag that they are a ghostrole.
/// It may also be used to ensure some ghost roles count as antagonists in future. /// It also holds the name for the round end display
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
public sealed partial class GhostRoleMarkerRoleComponent : BaseMindRoleComponent public sealed partial class GhostRoleMarkerRoleComponent : BaseMindRoleComponent
{ {
[DataField("name")] public string? Name; //TODO does anything still use this? It gets populated by GhostRolesystem but I don't see anything ever reading it
[DataField] public string? Name;
} }

View File

@@ -33,7 +33,6 @@ using Content.Server.Popups;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Collections; using Robust.Shared.Collections;
using Content.Shared.Ghost.Roles.Components; using Content.Shared.Ghost.Roles.Components;
using Content.Shared.Roles.Jobs;
namespace Content.Server.Ghost.Roles; namespace Content.Server.Ghost.Roles;
@@ -514,13 +513,13 @@ public sealed class GhostRoleSystem : EntitySystem
var newMind = _mindSystem.CreateMind(player.UserId, var newMind = _mindSystem.CreateMind(player.UserId,
EntityManager.GetComponent<MetaDataComponent>(mob).EntityName); EntityManager.GetComponent<MetaDataComponent>(mob).EntityName);
_roleSystem.MindAddRole(newMind, "MindRoleGhostMarker");
if(_roleSystem.MindHasRole<GhostRoleMarkerRoleComponent>(newMind!, out var markerRole))
markerRole.Value.Comp2.Name = role.RoleName;
_mindSystem.SetUserId(newMind, player.UserId); _mindSystem.SetUserId(newMind, player.UserId);
_mindSystem.TransferTo(newMind, mob); _mindSystem.TransferTo(newMind, mob);
_roleSystem.MindAddRoles(newMind.Owner, role.MindRoles, newMind.Comp);
if (_roleSystem.MindHasRole<GhostRoleMarkerRoleComponent>(newMind!, out var markerRole))
markerRole.Value.Comp2.Name = role.RoleName;
} }
/// <summary> /// <summary>

View File

@@ -51,10 +51,13 @@ public sealed class ToggleableGhostRoleSystem : EntitySystem
var ghostRole = EnsureComp<GhostRoleComponent>(uid); var ghostRole = EnsureComp<GhostRoleComponent>(uid);
EnsureComp<GhostTakeoverAvailableComponent>(uid); EnsureComp<GhostTakeoverAvailableComponent>(uid);
//GhostRoleComponent inherits custom settings from the ToggleableGhostRoleComponent
ghostRole.RoleName = Loc.GetString(component.RoleName); ghostRole.RoleName = Loc.GetString(component.RoleName);
ghostRole.RoleDescription = Loc.GetString(component.RoleDescription); ghostRole.RoleDescription = Loc.GetString(component.RoleDescription);
ghostRole.RoleRules = Loc.GetString(component.RoleRules); ghostRole.RoleRules = Loc.GetString(component.RoleRules);
ghostRole.JobProto = component.JobProto; ghostRole.JobProto = component.JobProto;
ghostRole.MindRoles = component.MindRoles;
} }
private void OnExamined(EntityUid uid, ToggleableGhostRoleComponent component, ExaminedEvent args) private void OnExamined(EntityUid uid, ToggleableGhostRoleComponent component, ExaminedEvent args)

View File

@@ -49,8 +49,8 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundEnd); SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundEnd);
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached); SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached); SubscribeLocalEvent<PlayerDetachedEvent>(OnPlayerDetached);
SubscribeLocalEvent<RoleAddedEvent>(OnRoleAdd); SubscribeLocalEvent<RoleAddedEvent>(OnRoleEvent);
SubscribeLocalEvent<RoleRemovedEvent>(OnRoleRemove); SubscribeLocalEvent<RoleRemovedEvent>(OnRoleEvent);
SubscribeLocalEvent<AFKEvent>(OnAFK); SubscribeLocalEvent<AFKEvent>(OnAFK);
SubscribeLocalEvent<UnAFKEvent>(OnUnAFK); SubscribeLocalEvent<UnAFKEvent>(OnUnAFK);
SubscribeLocalEvent<MobStateChangedEvent>(OnMobStateChanged); SubscribeLocalEvent<MobStateChangedEvent>(OnMobStateChanged);
@@ -121,13 +121,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
return GetTimedRoles(contentData.Mind.Value); return GetTimedRoles(contentData.Mind.Value);
} }
private void OnRoleRemove(RoleRemovedEvent ev) private void OnRoleEvent(RoleEvent ev)
{
if (_minds.TryGetSession(ev.Mind, out var session))
_tracking.QueueRefreshTrackers(session);
}
private void OnRoleAdd(RoleAddedEvent ev)
{ {
if (_minds.TryGetSession(ev.Mind, out var session)) if (_minds.TryGetSession(ev.Mind, out var session))
_tracking.QueueRefreshTrackers(session); _tracking.QueueRefreshTrackers(session);

View File

@@ -19,10 +19,25 @@ public sealed class JobSystem : SharedJobSystem
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<MindComponent, MindRoleAddedEvent>(MindOnDoGreeting); SubscribeLocalEvent<RoleAddedEvent>(OnRoleAddedEvent);
SubscribeLocalEvent<RoleRemovedEvent>(OnRoleRemovedEvent);
} }
private void MindOnDoGreeting(EntityUid mindId, MindComponent component, ref MindRoleAddedEvent args) private void OnRoleAddedEvent(RoleAddedEvent args)
{
MindOnDoGreeting(args.MindId, args.Mind, args);
if (args.RoleTypeUpdate)
_roles.RoleUpdateMessage(args.Mind);
}
private void OnRoleRemovedEvent(RoleRemovedEvent args)
{
if (args.RoleTypeUpdate)
_roles.RoleUpdateMessage(args.Mind);
}
private void MindOnDoGreeting(EntityUid mindId, MindComponent component, RoleAddedEvent args)
{ {
if (args.Silent) if (args.Silent)
return; return;

View File

@@ -1,11 +1,13 @@
namespace Content.Server.Roles; using Content.Shared.Roles;
namespace Content.Server.Roles;
/// <summary> /// <summary>
/// Adds a briefing to the character info menu, does nothing else. /// Adds a briefing to the character info menu, does nothing else.
/// </summary> /// </summary>
[RegisterComponent] [RegisterComponent]
public sealed partial class RoleBriefingComponent : Component public sealed partial class RoleBriefingComponent : BaseMindRoleComponent
{ {
[DataField("briefing"), ViewVariables(VVAccess.ReadWrite)] [DataField]
public string Briefing; public string Briefing;
} }

View File

@@ -1,10 +1,16 @@
using Content.Server.Chat.Managers;
using Content.Shared.Chat;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Shared.Prototypes;
namespace Content.Server.Roles; namespace Content.Server.Roles;
public sealed class RoleSystem : SharedRoleSystem public sealed class RoleSystem : SharedRoleSystem
{ {
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
public string? MindGetBriefing(EntityUid? mindId) public string? MindGetBriefing(EntityUid? mindId)
{ {
if (mindId == null) if (mindId == null)
@@ -37,6 +43,32 @@ public sealed class RoleSystem : SharedRoleSystem
return ev.Briefing; return ev.Briefing;
} }
public void RoleUpdateMessage(MindComponent mind)
{
if (mind.Session is null)
return;
if (!_proto.TryIndex(mind.RoleType, out var proto))
return;
var roleText = Loc.GetString(proto.Name);
var color = proto.Color;
var session = mind.Session;
//TODO add audio? Would need to be optional so it does not play on role changes that already come with their own audio
// _audio.PlayGlobal(Sound, session);
var message = Loc.GetString("role-type-update-message", ("color", color), ("role", roleText));
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
_chat.ChatMessageToOne(ChatChannel.Server,
message,
wrappedMessage,
default,
false,
session.Channel);
}
} }
/// <summary> /// <summary>

View File

@@ -1,5 +1,7 @@
using Content.Shared.Containers.ItemSlots; using Content.Server.Roles;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Mind.Components; using Content.Shared.Mind.Components;
using Content.Shared.Roles;
using Content.Shared.Silicons.Borgs.Components; using Content.Shared.Silicons.Borgs.Components;
using Robust.Shared.Containers; using Robust.Shared.Containers;
@@ -8,6 +10,9 @@ namespace Content.Server.Silicons.Borgs;
/// <inheritdoc/> /// <inheritdoc/>
public sealed partial class BorgSystem public sealed partial class BorgSystem
{ {
[Dependency] private readonly SharedRoleSystem _roles = default!;
public void InitializeMMI() public void InitializeMMI()
{ {
SubscribeLocalEvent<MMIComponent, ComponentInit>(OnMMIInit); SubscribeLocalEvent<MMIComponent, ComponentInit>(OnMMIInit);
@@ -41,8 +46,13 @@ public sealed partial class BorgSystem
Dirty(uid, component); Dirty(uid, component);
if (_mind.TryGetMind(ent, out var mindId, out var mind)) if (_mind.TryGetMind(ent, out var mindId, out var mind))
{
_mind.TransferTo(mindId, uid, true, mind: mind); _mind.TransferTo(mindId, uid, true, mind: mind);
if (!_roles.MindHasRole<SiliconBrainRoleComponent>(mindId))
_roles.MindAddRole(mindId, "MindRoleSiliconBrain", silent: true);
}
_appearance.SetData(uid, MMIVisuals.BrainPresent, true); _appearance.SetData(uid, MMIVisuals.BrainPresent, true);
} }
@@ -75,7 +85,12 @@ public sealed partial class BorgSystem
RemComp(uid, component); RemComp(uid, component);
if (_mind.TryGetMind(linked, out var mindId, out var mind)) if (_mind.TryGetMind(linked, out var mindId, out var mind))
{
if (_roles.MindHasRole<SiliconBrainRoleComponent>(mindId))
_roles.MindRemoveRole<SiliconBrainRoleComponent>(mindId);
_mind.TransferTo(mindId, uid, true, mind: mind); _mind.TransferTo(mindId, uid, true, mind: mind);
}
_appearance.SetData(linked, MMIVisuals.BrainPresent, false); _appearance.SetData(linked, MMIVisuals.BrainPresent, false);
} }

View File

@@ -5,7 +5,6 @@ using Content.Server.DeviceNetwork.Systems;
using Content.Server.Explosion.EntitySystems; using Content.Server.Explosion.EntitySystems;
using Content.Server.Hands.Systems; using Content.Server.Hands.Systems;
using Content.Server.PowerCell; using Content.Server.PowerCell;
using Content.Shared.Access.Systems;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;

View File

@@ -193,7 +193,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
private void EnsureSubvertedSiliconRole(EntityUid mindId) private void EnsureSubvertedSiliconRole(EntityUid mindId)
{ {
if (!_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId)) if (!_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId))
_roles.MindAddRole(mindId, "MindRoleSubvertedSilicon"); _roles.MindAddRole(mindId, "MindRoleSubvertedSilicon", silent: true);
} }
private void RemoveSubvertedSiliconRole(EntityUid mindId) private void RemoveSubvertedSiliconRole(EntityUid mindId)

View File

@@ -1,3 +1,4 @@
using Content.Shared.Mind;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -10,6 +11,7 @@ public sealed record PlayerInfo(
string IdentityName, string IdentityName,
string StartingJob, string StartingJob,
bool Antag, bool Antag,
RoleTypePrototype RoleProto,
NetEntity? NetEntity, NetEntity? NetEntity,
NetUserId SessionId, NetUserId SessionId,
bool Connected, bool Connected,

View File

@@ -4,21 +4,42 @@ namespace Content.Shared.CCVar;
public sealed partial class CCVars public sealed partial class CCVars
{ {
/// <summary>
/// The sound played when clicking a UI button
/// </summary>
public static readonly CVarDef<string> UIClickSound = public static readonly CVarDef<string> UIClickSound =
CVarDef.Create("interface.click_sound", "/Audio/UserInterface/click.ogg", CVar.REPLICATED); CVarDef.Create("interface.click_sound", "/Audio/UserInterface/click.ogg", CVar.REPLICATED);
/// <summary>
/// The sound played when the mouse hovers over a clickable UI element
/// </summary>
public static readonly CVarDef<string> UIHoverSound = public static readonly CVarDef<string> UIHoverSound =
CVarDef.Create("interface.hover_sound", "/Audio/UserInterface/hover.ogg", CVar.REPLICATED); CVarDef.Create("interface.hover_sound", "/Audio/UserInterface/hover.ogg", CVar.REPLICATED);
/// <summary>
/// The layout style of the UI
/// </summary>
public static readonly CVarDef<string> UILayout = public static readonly CVarDef<string> UILayout =
CVarDef.Create("ui.layout", "Default", CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.layout", "Default", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// The dimensions for the chat window in Default UI mode
/// </summary>
public static readonly CVarDef<string> DefaultScreenChatSize = public static readonly CVarDef<string> DefaultScreenChatSize =
CVarDef.Create("ui.default_chat_size", "", CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.default_chat_size", "", CVar.CLIENTONLY | CVar.ARCHIVE);
/// <summary>
/// The width of the chat panel in Separated UI mode
/// </summary>
public static readonly CVarDef<string> SeparatedScreenChatSize = public static readonly CVarDef<string> SeparatedScreenChatSize =
CVarDef.Create("ui.separated_chat_size", "0.6,0", CVar.CLIENTONLY | CVar.ARCHIVE); CVarDef.Create("ui.separated_chat_size", "0.6,0", CVar.CLIENTONLY | CVar.ARCHIVE);
public static readonly CVarDef<bool> OutlineEnabled = public static readonly CVarDef<bool> OutlineEnabled =
CVarDef.Create("outline.enabled", true, CVar.CLIENTONLY); CVarDef.Create("outline.enabled", true, CVar.CLIENTONLY);
/// <summary>
/// If true, the admin overlay will be displayed in the old style (showing only "ANTAG")
/// </summary>
public static readonly CVarDef<bool> AdminOverlayClassic =
CVarDef.Create("ui.admin_overlay_classic", false, CVar.CLIENTONLY | CVar.ARCHIVE);
} }

View File

@@ -3,6 +3,7 @@ using Content.Shared.Mind.Components;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Shared.Mind; namespace Content.Shared.Mind;
@@ -101,6 +102,12 @@ public sealed partial class MindComponent : Component
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public List<EntityUid> MindRoles = new List<EntityUid>(); public List<EntityUid> MindRoles = new List<EntityUid>();
/// <summary>
/// The mind's current antagonist/special role, or lack thereof;
/// </summary>
[DataField, AutoNetworkedField]
public ProtoId<RoleTypePrototype> RoleType = "Neutral";
/// <summary> /// <summary>
/// The session of the player owning this mind. /// The session of the player owning this mind.
/// Can be null, in which case the player is currently not logged in. /// Can be null, in which case the player is currently not logged in.

View File

@@ -0,0 +1,25 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.Mind;
/// <summary>
/// The core properties of Role Types
/// </summary>
[Prototype, Serializable]
public sealed class RoleTypePrototype : IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;
/// <summary>
/// The role's name as displayed on the UI.
/// </summary>
[DataField]
public LocId Name = "role-type-crew-aligned-name";
/// <summary>
/// The role's displayed color.
/// </summary>
[DataField]
public Color Color { get; private set; } = Color.FromHex("#eeeeee");
}

View File

@@ -5,6 +5,6 @@ namespace Content.Shared.Roles;
/// depending on their roles. /// depending on their roles.
/// </summary> /// </summary>
/// <param name="IsAntagonist">Whether or not the player is an antagonist.</param> /// <param name="IsAntagonist">Whether or not the player is an antagonist.</param>
/// <param name="IsExclusiveAntagonist">Whether or not AntagSelectionSystem should exclude this player from other antag roles</param /// <param name="IsExclusiveAntagonist">Whether or not AntagSelectionSystem should exclude this player from other antag roles</param>
[ByRefEvent] [ByRefEvent]
public record struct MindIsAntagonistEvent(bool IsAntagonist, bool IsExclusiveAntagonist); public record struct MindIsAntagonistEvent(bool IsAntagonist, bool IsExclusiveAntagonist);

View File

@@ -1,8 +0,0 @@
namespace Content.Shared.Roles;
/// <summary>
/// Raised on mind entities when a role is added to them.
/// <see cref="RoleAddedEvent"/> for the one raised on player entities.
/// </summary>
[ByRefEvent]
public readonly record struct MindRoleAddedEvent(bool Silent);

View File

@@ -1,5 +1,4 @@
using Content.Shared.Mind; using Content.Shared.Mind;
using JetBrains.Annotations;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -18,6 +17,12 @@ public sealed partial class MindRoleComponent : BaseMindRoleComponent
[DataField] [DataField]
public bool Antag { get; set; } = false; public bool Antag { get; set; } = false;
/// <summary>
/// The mind's current antagonist/special role, or lack thereof;
/// </summary>
[DataField]
public ProtoId<RoleTypePrototype>? RoleType;
/// <summary> /// <summary>
/// True if this mindrole is an exclusive antagonist. Antag setting is not checked if this is True. /// True if this mindrole is an exclusive antagonist. Antag setting is not checked if this is True.
/// </summary> /// </summary>
@@ -43,6 +48,7 @@ public sealed partial class MindRoleComponent : BaseMindRoleComponent
} }
// Why does this base component actually exist? It does make auto-categorization easy, but before that it was useless? // Why does this base component actually exist? It does make auto-categorization easy, but before that it was useless?
// I used it for easy organisation/bookkeeping of what components are for mindroles
[EntityCategory("Roles")] [EntityCategory("Roles")]
public abstract partial class BaseMindRoleComponent : Component public abstract partial class BaseMindRoleComponent : Component
{ {

View File

@@ -3,10 +3,10 @@
namespace Content.Shared.Roles; namespace Content.Shared.Roles;
/// <summary> /// <summary>
/// Raised on player entities when a role is added to them. /// Raised on mind entities when a mind role is added to them.
/// <see cref="RoleAddedEvent"/> for the one raised on mind entities.
/// </summary> /// </summary>
/// <param name="MindId">The mind id associated with the player.</param> /// <param name="MindId">The mind id associated with the player.</param>
/// <param name="Mind">The mind component associated with the mind id.</param> /// <param name="Mind">The mind component associated with the mind id.</param>
/// <param name="Antagonist">Whether or not the role makes the player an antagonist.</param> /// <param name="RoleTypeUpdate">True if this update has changed the mind's role type</param>
public sealed record RoleAddedEvent(EntityUid MindId, MindComponent Mind, bool Antagonist, bool Silent = false) : RoleEvent(MindId, Mind, Antagonist); /// <param name="Silent">If true, Job greeting/intro will not be sent to the player's chat</param>
public sealed record RoleAddedEvent(EntityUid MindId, MindComponent Mind, bool RoleTypeUpdate, bool Silent = false) : RoleEvent(MindId, Mind, RoleTypeUpdate);

View File

@@ -3,9 +3,9 @@
namespace Content.Shared.Roles; namespace Content.Shared.Roles;
/// <summary> /// <summary>
/// Base event raised on player entities to indicate that something changed about one of their roles. /// Base event raised on mind entities to indicate that a mind role was either added or removed.
/// </summary> /// </summary>
/// <param name="MindId">The mind id associated with the player.</param> /// <param name="MindId">The mind id associated with the player.</param>
/// <param name="Mind">The mind component associated with the mind id.</param> /// <param name="Mind">The mind component associated with the mind id.</param>
/// <param name="Antagonist">Whether or not the role makes the player an antagonist.</param> /// <param name="RoleTypeUpdate">True if this update has changed the mind's role type</param>
public abstract record RoleEvent(EntityUid MindId, MindComponent Mind, bool Antagonist); public abstract record RoleEvent(EntityUid MindId, MindComponent Mind, bool RoleTypeUpdate);

View File

@@ -3,12 +3,9 @@
namespace Content.Shared.Roles; namespace Content.Shared.Roles;
/// <summary> /// <summary>
/// Event raised on player entities to indicate that a role was removed from their mind. /// Raised on mind entities when a mind role is removed from them.
/// </summary> /// </summary>
/// <param name="MindId">The mind id associated with the player.</param> /// <param name="MindId">The mind id associated with the player.</param>
/// <param name="Mind">The mind component associated with the mind id.</param> /// <param name="Mind">The mind component associated with the mind id.</param>
/// <param name="Antagonist"> /// <param name="RoleTypeUpdate">True if this update has changed the mind's role type</param>
/// Whether or not the role made the player an antagonist. public sealed record RoleRemovedEvent(EntityUid MindId, MindComponent Mind, bool RoleTypeUpdate) : RoleEvent(MindId, Mind, RoleTypeUpdate);
/// They may still be one due to one of their other roles.
/// </param>
public sealed record RoleRemovedEvent(EntityUid MindId, MindComponent Mind, bool Antagonist) : RoleEvent(MindId, Mind, Antagonist);

View File

@@ -1,14 +1,18 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Administration.Logs; using Content.Shared.Administration.Logs;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.GameTicking;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Roles.Jobs; using Content.Shared.Roles.Jobs;
using Content.Shared.Silicons.Borgs.Components;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Shared.Roles; namespace Content.Shared.Roles;
@@ -19,6 +23,7 @@ public abstract class SharedRoleSystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly SharedMindSystem _minds = default!;
[Dependency] private readonly IPrototypeManager _prototypes = default!; [Dependency] private readonly IPrototypeManager _prototypes = default!;
private JobRequirementOverridePrototype? _requirementOverride; private JobRequirementOverridePrototype? _requirementOverride;
@@ -28,6 +33,15 @@ public abstract class SharedRoleSystem : EntitySystem
Subs.CVar(_cfg, CCVars.GameRoleTimerOverride, SetRequirementOverride, true); Subs.CVar(_cfg, CCVars.GameRoleTimerOverride, SetRequirementOverride, true);
SubscribeLocalEvent<MindRoleComponent, ComponentShutdown>(OnComponentShutdown); SubscribeLocalEvent<MindRoleComponent, ComponentShutdown>(OnComponentShutdown);
SubscribeLocalEvent<StartingMindRoleComponent, PlayerSpawnCompleteEvent>(OnSpawn);
}
private void OnSpawn(EntityUid uid, StartingMindRoleComponent component, PlayerSpawnCompleteEvent args)
{
if (!_minds.TryGetMind(uid, out var mindId, out var mindComp))
return;
MindAddRole(mindId, component.MindRole, mind: mindComp, silent: component.Silent);
} }
private void SetRequirementOverride(string value) private void SetRequirementOverride(string value)
@@ -50,7 +64,7 @@ public abstract class SharedRoleSystem : EntitySystem
/// <param name="mind">If the mind component is provided, it will be checked if it belongs to the mind entity</param> /// <param name="mind">If the mind component is provided, it will be checked if it belongs to the mind entity</param>
/// <param name="silent">If true, no briefing will be generated upon receiving the mind role</param> /// <param name="silent">If true, no briefing will be generated upon receiving the mind role</param>
public void MindAddRoles(EntityUid mindId, public void MindAddRoles(EntityUid mindId,
List<ProtoId<EntityPrototype>>? roles, List<EntProtoId>? roles,
MindComponent? mind = null, MindComponent? mind = null,
bool silent = false) bool silent = false)
{ {
@@ -71,7 +85,7 @@ public abstract class SharedRoleSystem : EntitySystem
/// <param name="mind">If the mind component is provided, it will be checked if it belongs to the mind entity</param> /// <param name="mind">If the mind component is provided, it will be checked if it belongs to the mind entity</param>
/// <param name="silent">If true, no briefing will be generated upon receiving the mind role</param> /// <param name="silent">If true, no briefing will be generated upon receiving the mind role</param>
public void MindAddRole(EntityUid mindId, public void MindAddRole(EntityUid mindId,
ProtoId<EntityPrototype> protoId, EntProtoId protoId,
MindComponent? mind = null, MindComponent? mind = null,
bool silent = false) bool silent = false)
{ {
@@ -114,22 +128,20 @@ public abstract class SharedRoleSystem : EntitySystem
/// Creates a Mind Role /// Creates a Mind Role
/// </summary> /// </summary>
private void MindAddRoleDo(EntityUid mindId, private void MindAddRoleDo(EntityUid mindId,
ProtoId<EntityPrototype> protoId, EntProtoId protoId,
MindComponent? mind = null, MindComponent? mind = null,
bool silent = false, bool silent = false,
string? jobPrototype = null) string? jobPrototype = null)
{ {
if (!Resolve(mindId, ref mind)) if (!Resolve(mindId, ref mind))
{ {
Log.Error($"Failed to add role {protoId} to mind {mindId} : Mind does not match provided mind component"); Log.Error($"Failed to add role {protoId} to {ToPrettyString(mindId)} : Mind does not match provided mind component");
return; return;
} }
var antagonist = false;
if (!_prototypes.TryIndex(protoId, out var protoEnt)) if (!_prototypes.TryIndex(protoId, out var protoEnt))
{ {
Log.Error($"Failed to add role {protoId} to mind {mindId} : Role prototype does not exist"); Log.Error($"Failed to add role {protoId} to {ToPrettyString(mindId)} : Role prototype does not exist");
return; return;
} }
@@ -151,15 +163,14 @@ public abstract class SharedRoleSystem : EntitySystem
DebugTools.Assert(!mindRoleComp.ExclusiveAntag); DebugTools.Assert(!mindRoleComp.ExclusiveAntag);
} }
antagonist |= mindRoleComp.Antag;
mind.MindRoles.Add(mindRoleId); mind.MindRoles.Add(mindRoleId);
var mindEv = new MindRoleAddedEvent(silent); var update = MindRolesUpdate((mindId, mind));
RaiseLocalEvent(mindId, ref mindEv);
var message = new RoleAddedEvent(mindId, mind, antagonist, silent); // RoleType refresh, Role time tracking, Update Admin playerlist
if (mind.OwnedEntity != null) if (mind.OwnedEntity != null)
{ {
var message = new RoleAddedEvent(mindId, mind, update, silent);
RaiseLocalEvent(mind.OwnedEntity.Value, message, true); RaiseLocalEvent(mind.OwnedEntity.Value, message, true);
} }
@@ -174,12 +185,91 @@ public abstract class SharedRoleSystem : EntitySystem
{ {
//TODO: This is not tied to the player on the Admin Log filters. //TODO: This is not tied to the player on the Admin Log filters.
//Probably only happens when Job Role is added on initial spawn, before the mind entity is put in a mob //Probably only happens when Job Role is added on initial spawn, before the mind entity is put in a mob
Log.Error($"{ToPrettyString(mindId)} does not have an OwnedEntity!");
_adminLogger.Add(LogType.Mind, _adminLogger.Add(LogType.Mind,
LogImpact.Low, LogImpact.Low,
$"{name} added to {ToPrettyString(mindId)}"); $"{name} added to {ToPrettyString(mindId)}");
} }
} }
/// <summary>
/// Select the mind's currently "active" mind role entity, and update the mind's role type, if necessary
/// </summary>
/// <returns>
/// True if this changed the mind's role type
/// </returns>>
private bool MindRolesUpdate(Entity<MindComponent?> ent)
{
if(!Resolve(ent.Owner, ref ent.Comp))
return false;
//get the most important/latest mind role
var roleType = GetRoleTypeByTime(ent.Comp);
if (ent.Comp.RoleType == roleType)
return false;
SetRoleType(ent.Owner, roleType);
return true;
}
private ProtoId<RoleTypePrototype> GetRoleTypeByTime(MindComponent mind)
{
// If any Mind Roles specify a Role Type, return the most recent. Otherwise return Neutral
var roles = new List<ProtoId<RoleTypePrototype>>();
foreach (var role in mind.MindRoles)
{
var comp = Comp<MindRoleComponent>(role);
if (comp.RoleType is not null)
roles.Add(comp.RoleType.Value);
}
ProtoId<RoleTypePrototype> result = (roles.Count > 0) ? roles.LastOrDefault() : "Neutral";
return (result);
}
private void SetRoleType(EntityUid mind, ProtoId<RoleTypePrototype> roleTypeId)
{
if (!TryComp<MindComponent>(mind, out var comp))
{
Log.Error($"Failed to update Role Type of mind entity {ToPrettyString(mind)} to {roleTypeId}. MindComponent not found.");
return;
}
if (!_prototypes.HasIndex(roleTypeId))
{
Log.Error($"Failed to change Role Type of {_minds.MindOwnerLoggingString(comp)} to {roleTypeId}. Invalid role");
return;
}
comp.RoleType = roleTypeId;
Dirty(mind, comp);
// Update player character window
if (_minds.TryGetSession(mind, out var session))
RaiseNetworkEvent(new MindRoleTypeChangedEvent(), session.Channel);
else
{
var error = $"The Character Window of {_minds.MindOwnerLoggingString(comp)} potentially did not update immediately : session error";
_adminLogger.Add(LogType.Mind, LogImpact.High, $"{error}");
}
if (comp.OwnedEntity is null)
{
Log.Error($"{ToPrettyString(mind)} does not have an OwnedEntity!");
_adminLogger.Add(LogType.Mind,
LogImpact.High,
$"Role Type of {ToPrettyString(mind)} changed to {roleTypeId}");
return;
}
_adminLogger.Add(LogType.Mind,
LogImpact.High,
$"Role Type of {ToPrettyString(comp.OwnedEntity)} changed to {roleTypeId}");
}
/// <summary> /// <summary>
/// Removes all instances of a specific role from this mind. /// Removes all instances of a specific role from this mind.
/// </summary> /// </summary>
@@ -195,20 +285,18 @@ public abstract class SharedRoleSystem : EntitySystem
return false; return false;
var found = false; var found = false;
var antagonist = false;
var delete = new List<EntityUid>(); var delete = new List<EntityUid>();
foreach (var role in mind.Comp.MindRoles) foreach (var role in mind.Comp.MindRoles)
{ {
if (!HasComp<T>(role)) if (!HasComp<T>(role))
continue; continue;
if (!TryComp(role, out MindRoleComponent? roleComp)) if (!HasComp<MindRoleComponent>(role))
{ {
Log.Error($"Encountered mind role entity {ToPrettyString(role)} without a {nameof(MindRoleComponent)}"); Log.Error($"Encountered mind role entity {ToPrettyString(role)} without a {nameof(MindRoleComponent)}");
continue; continue;
} }
antagonist |= roleComp.Antag | roleComp.ExclusiveAntag;
delete.Add(role); delete.Add(role);
found = true; found = true;
} }
@@ -221,9 +309,11 @@ public abstract class SharedRoleSystem : EntitySystem
_entityManager.DeleteEntity(role); _entityManager.DeleteEntity(role);
} }
var update = MindRolesUpdate(mind);
if (mind.Comp.OwnedEntity != null) if (mind.Comp.OwnedEntity != null)
{ {
var message = new RoleRemovedEvent(mind.Owner, mind.Comp, antagonist); var message = new RoleRemovedEvent(mind.Owner, mind.Comp, update);
RaiseLocalEvent(mind.Comp.OwnedEntity.Value, message, true); RaiseLocalEvent(mind.Comp.OwnedEntity.Value, message, true);
} }
@@ -267,10 +357,9 @@ public abstract class SharedRoleSystem : EntitySystem
/// Finds the first mind role of a specific T type on a mind entity. /// Finds the first mind role of a specific T type on a mind entity.
/// Outputs entity components for the mind role's MindRoleComponent and for T /// Outputs entity components for the mind role's MindRoleComponent and for T
/// </summary> /// </summary>
/// <param name="mindId">The mind entity</param> /// <param name="mind">The mind entity</param>
/// <typeparam name="T">The type of the role to find.</typeparam> /// <typeparam name="T">The type of the role to find.</typeparam>
/// <param name="role">The Mind Role entity component</param> /// <param name="role">The Mind Role entity component</param>
/// <param name="roleT">The Mind Role's entity component for T</param>
/// <returns>True if the role is found</returns> /// <returns>True if the role is found</returns>
public bool MindHasRole<T>(Entity<MindComponent?> mind, public bool MindHasRole<T>(Entity<MindComponent?> mind,
[NotNullWhen(true)] out Entity<MindRoleComponent, T>? role) where T : IComponent [NotNullWhen(true)] out Entity<MindRoleComponent, T>? role) where T : IComponent
@@ -467,7 +556,7 @@ public abstract class SharedRoleSystem : EntitySystem
return CheckAntagonistStatus(mindId.Value).ExclusiveAntag; return CheckAntagonistStatus(mindId.Value).ExclusiveAntag;
} }
public (bool Antag, bool ExclusiveAntag) CheckAntagonistStatus(Entity<MindComponent?> mind) private (bool Antag, bool ExclusiveAntag) CheckAntagonistStatus(Entity<MindComponent?> mind)
{ {
if (!Resolve(mind.Owner, ref mind.Comp)) if (!Resolve(mind.Owner, ref mind.Comp))
return (false, false); return (false, false);
@@ -537,3 +626,12 @@ public abstract class SharedRoleSystem : EntitySystem
return antag.Requirements; return antag.Requirements;
} }
} }
/// <summary>
/// Raised on the client to update Role Type on the character window, in case it happened to be open.
/// </summary>
[Serializable, NetSerializable]
public sealed class MindRoleTypeChangedEvent : EntityEventArgs
{
}

View File

@@ -0,0 +1,9 @@
namespace Content.Shared.Roles;
/// <summary>
/// Used on Silicon's minds to get the appropriate mind role
/// </summary>
[RegisterComponent]
public sealed partial class SiliconBrainRoleComponent : BaseMindRoleComponent
{
}

View File

@@ -0,0 +1,27 @@
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Roles;
/// <summary>
/// This is most likely not the component you are looking for, almost nothing should be using this.
/// Consider using GhostRoleComponent or AntagSelectionComponent instead.
///
/// The specified mind role will be added to the mob on spawn.
///
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class StartingMindRoleComponent : Component
{
/// <summary>
/// The ID of the mind role to add
/// </summary>
[DataField(required: true)]
public EntProtoId MindRole;
/// <summary>
/// Add the mind role silently
/// </summary>
[DataField]
public bool Silent = true;
}

View File

@@ -14,3 +14,5 @@ admin-verb-text-make-nuclear-operative = Make Nuclear Operative
admin-verb-text-make-pirate = Make Pirate admin-verb-text-make-pirate = Make Pirate
admin-verb-text-make-head-rev = Make Head Rev admin-verb-text-make-head-rev = Make Head Rev
admin-verb-text-make-thief = Make Thief admin-verb-text-make-thief = Make Thief
admin-overlay-antag-classic = ANTAG

View File

@@ -3,6 +3,7 @@ player-tab-username = Username
player-tab-character = Character player-tab-character = Character
player-tab-job = Job player-tab-job = Job
player-tab-antagonist = Antagonist player-tab-antagonist = Antagonist
player-tab-roletype = Role Type
player-tab-playtime = Playtime player-tab-playtime = Playtime
player-tab-show-disconnected = Show Disconnected player-tab-show-disconnected = Show Disconnected
player-tab-overlay = Overlay player-tab-overlay = Overlay

View File

@@ -0,0 +1,9 @@
role-type-crew-aligned-name = Crew Aligned
role-type-solo-antagonist-name = Solo Antagonist
role-type-team-antagonist-name = Team Antagonist
role-type-free-agent-name = Free Agent
role-type-familiar-name = Familiar
role-type-silicon-name = Silicon
role-type-silicon-antagonist-name = Altered Silicon
role-type-update-message = Your role is [color = {$color}]{$role}[/color]

View File

@@ -19,6 +19,8 @@
name: ghost-role-information-rat-king-name name: ghost-role-information-rat-king-name
description: ghost-role-information-rat-king-description description: ghost-role-information-rat-king-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
raffle: raffle:
settings: default settings: default
- type: GhostRoleMobSpawner - type: GhostRoleMobSpawner
@@ -40,6 +42,8 @@
name: ghost-role-information-remilia-name name: ghost-role-information-remilia-name
description: ghost-role-information-remilia-description description: ghost-role-information-remilia-description
rules: ghost-role-information-familiar-rules rules: ghost-role-information-familiar-rules
mindRoles:
- MindRoleGhostRoleFamiliar
raffle: raffle:
settings: short settings: short
- type: GhostRoleMobSpawner - type: GhostRoleMobSpawner
@@ -61,6 +65,8 @@
name: ghost-role-information-cerberus-name name: ghost-role-information-cerberus-name
description: ghost-role-information-cerberus-description description: ghost-role-information-cerberus-description
rules: ghost-role-information-familiar-rules rules: ghost-role-information-familiar-rules
mindRoles:
- MindRoleGhostRoleFamiliar
raffle: raffle:
settings: default settings: default
- type: GhostRoleMobSpawner - type: GhostRoleMobSpawner
@@ -81,6 +87,8 @@
components: components:
- type: GhostRole - type: GhostRole
rules: ghost-role-information-rules-default-team-antagonist rules: ghost-role-information-rules-default-team-antagonist
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: default settings: default
- type: GhostRoleMobSpawner - type: GhostRoleMobSpawner
@@ -101,6 +109,8 @@
name: ghost-role-information-loneop-name name: ghost-role-information-loneop-name
description: ghost-role-information-loneop-description description: ghost-role-information-loneop-description
rules: ghost-role-information-loneop-rules rules: ghost-role-information-loneop-rules
mindRoles:
- MindRoleGhostRoleSoloAntagonist
- type: Sprite - type: Sprite
sprite: Markers/jobs.rsi sprite: Markers/jobs.rsi
layers: layers:
@@ -117,6 +127,8 @@
name: roles-antag-nuclear-operative-commander-name name: roles-antag-nuclear-operative-commander-name
description: roles-antag-nuclear-operative-commander-objective description: roles-antag-nuclear-operative-commander-objective
rules: ghost-role-information-rules-default-team-antagonist rules: ghost-role-information-rules-default-team-antagonist
mindRoles:
- MindRoleGhostRoleTeamAntagonist
- type: entity - type: entity
categories: [ HideSpawnMenu, Spawner ] categories: [ HideSpawnMenu, Spawner ]
@@ -127,6 +139,8 @@
name: roles-antag-nuclear-operative-agent-name name: roles-antag-nuclear-operative-agent-name
description: roles-antag-nuclear-operative-agent-objective description: roles-antag-nuclear-operative-agent-objective
rules: ghost-role-information-rules-default-team-antagonist rules: ghost-role-information-rules-default-team-antagonist
mindRoles:
- MindRoleGhostRoleTeamAntagonist
- type: entity - type: entity
categories: [ HideSpawnMenu, Spawner ] categories: [ HideSpawnMenu, Spawner ]
@@ -137,6 +151,8 @@
name: roles-antag-nuclear-operative-name name: roles-antag-nuclear-operative-name
description: roles-antag-nuclear-operative-objective description: roles-antag-nuclear-operative-objective
rules: ghost-role-information-rules-default-team-antagonist rules: ghost-role-information-rules-default-team-antagonist
mindRoles:
- MindRoleGhostRoleTeamAntagonist
- type: entity - type: entity
categories: [ HideSpawnMenu, Spawner ] categories: [ HideSpawnMenu, Spawner ]
@@ -147,6 +163,8 @@
name: ghost-role-information-space-dragon-name name: ghost-role-information-space-dragon-name
description: ghost-role-information-space-dragon-description description: ghost-role-information-space-dragon-description
rules: ghost-role-information-space-dragon-rules rules: ghost-role-information-space-dragon-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
- type: Sprite - type: Sprite
layers: layers:
- state: green - state: green
@@ -162,6 +180,8 @@
name: ghost-role-information-space-ninja-name name: ghost-role-information-space-ninja-name
description: ghost-role-information-space-ninja-description description: ghost-role-information-space-ninja-description
rules: ghost-role-information-antagonist-rules rules: ghost-role-information-antagonist-rules
mindRoles:
- MindRoleGhostRoleSoloAntagonist
raffle: raffle:
settings: default settings: default
- type: Sprite - type: Sprite

View File

@@ -313,6 +313,7 @@
access: [["SyndicateAgent"], ["NuclearOperative"]] access: [["SyndicateAgent"], ["NuclearOperative"]]
- type: SiliconLawProvider - type: SiliconLawProvider
laws: SyndicateStatic laws: SyndicateStatic
subverted: true
- type: IntrinsicRadioTransmitter - type: IntrinsicRadioTransmitter
channels: channels:
- Binary - Binary

View File

@@ -443,6 +443,8 @@
name: ghost-role-information-mothroach-name name: ghost-role-information-mothroach-name
description: ghost-role-information-mothroach-description description: ghost-role-information-mothroach-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
- type: Fixtures - type: Fixtures
fixtures: fixtures:
fix1: fix1:
@@ -1403,6 +1405,9 @@
name: ghost-role-information-monkey-name name: ghost-role-information-monkey-name
description: ghost-role-information-monkey-description description: ghost-role-information-monkey-description
rules: ghost-role-information-syndicate-reinforcement-rules rules: ghost-role-information-syndicate-reinforcement-rules
mindRoles:
# This is for syndicate monkeys that randomly gain sentience, thus have no summoner to team with
- MindRoleGhostRoleSoloAntagonist
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
@@ -1610,6 +1615,8 @@
name: ghost-role-information-mouse-name name: ghost-role-information-mouse-name
description: ghost-role-information-mouse-description description: ghost-role-information-mouse-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
- type: Speech - type: Speech
speechSounds: Squeak speechSounds: Squeak
@@ -2397,6 +2404,8 @@
name: ghost-role-information-giant-spider-name name: ghost-role-information-giant-spider-name
description: ghost-role-information-giant-spider-description description: ghost-role-information-giant-spider-description
rules: ghost-role-information-giant-spider-rules rules: ghost-role-information-giant-spider-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: short settings: short
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
@@ -2968,6 +2977,8 @@
allowMovement: true allowMovement: true
description: ghost-role-information-SyndiCat-description description: ghost-role-information-SyndiCat-description
rules: ghost-role-information-SyndiCat-rules rules: ghost-role-information-SyndiCat-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable

View File

@@ -11,6 +11,8 @@
name: ghost-role-information-behonker-name name: ghost-role-information-behonker-name
description: ghost-role-information-behonker-description description: ghost-role-information-behonker-description
rules: ghost-role-information-antagonist-rules rules: ghost-role-information-antagonist-rules
mindRoles:
- MindRoleGhostRoleSoloAntagonist
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable

View File

@@ -170,6 +170,8 @@
name: ghost-role-information-sentient-carp-name name: ghost-role-information-sentient-carp-name
description: ghost-role-information-sentient-carp-description description: ghost-role-information-sentient-carp-description
rules: ghost-role-information-space-dragon-summoned-carp-rules rules: ghost-role-information-space-dragon-summoned-carp-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: short settings: short
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable

View File

@@ -232,6 +232,8 @@
prob: 0 prob: 0
description: ghost-role-information-angry-slimes-description description: ghost-role-information-angry-slimes-description
rules: ghost-role-information-angry-slimes-rules rules: ghost-role-information-angry-slimes-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: short settings: short
- type: NpcFactionMember - type: NpcFactionMember

View File

@@ -13,6 +13,8 @@
name: ghost-role-information-hellspawn-name name: ghost-role-information-hellspawn-name
description: ghost-role-information-hellspawn-description description: ghost-role-information-hellspawn-description
rules: ghost-role-information-antagonist-rules rules: ghost-role-information-antagonist-rules
mindRoles:
- MindRoleGhostRoleSoloAntagonist
raffle: raffle:
settings: default settings: default
- type: RotationVisuals - type: RotationVisuals

View File

@@ -91,6 +91,8 @@
name: ghost-role-information-rat-king-name name: ghost-role-information-rat-king-name
description: ghost-role-information-rat-king-description description: ghost-role-information-rat-king-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable

View File

@@ -38,6 +38,8 @@
name: ghost-role-information-revenant-name name: ghost-role-information-revenant-name
description: ghost-role-information-revenant-description description: ghost-role-information-revenant-description
rules: ghost-role-information-antagonist-rules rules: ghost-role-information-antagonist-rules
mindRoles:
- MindRoleGhostRoleSoloAntagonist
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable

View File

@@ -204,6 +204,8 @@
name: ghost-role-information-honkbot-name name: ghost-role-information-honkbot-name
description: ghost-role-information-honkbot-description description: ghost-role-information-honkbot-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
@@ -236,6 +238,8 @@
name: ghost-role-information-jonkbot-name name: ghost-role-information-jonkbot-name
description: ghost-role-information-jonkbot-description description: ghost-role-information-jonkbot-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
raffle: raffle:
settings: default settings: default
- type: InteractionPopup - type: InteractionPopup
@@ -370,6 +374,8 @@
name: ghost-role-information-mimebot-name name: ghost-role-information-mimebot-name
description: ghost-role-information-mimebot-description description: ghost-role-information-mimebot-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable

View File

@@ -167,6 +167,8 @@
- type: GhostRole - type: GhostRole
description: ghost-role-information-angry-slimes-description description: ghost-role-information-angry-slimes-description
rules: ghost-role-information-angry-slimes-rules rules: ghost-role-information-angry-slimes-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: short settings: short
@@ -205,6 +207,8 @@
- type: GhostRole - type: GhostRole
description: ghost-role-information-angry-slimes-description description: ghost-role-information-angry-slimes-description
rules: ghost-role-information-angry-slimes-rules rules: ghost-role-information-angry-slimes-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: short settings: short
@@ -242,5 +246,7 @@
- type: GhostRole - type: GhostRole
description: ghost-role-information-angry-slimes-description description: ghost-role-information-angry-slimes-description
rules: ghost-role-information-angry-slimes-rules rules: ghost-role-information-angry-slimes-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: short settings: short

View File

@@ -385,6 +385,8 @@
name: ghost-role-information-snail-name name: ghost-role-information-snail-name
description: ghost-role-information-snail-description description: ghost-role-information-snail-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
- type: Emoting - type: Emoting
- type: Sprite - type: Sprite

View File

@@ -15,6 +15,8 @@
name: ghost-role-information-space-dragon-name name: ghost-role-information-space-dragon-name
description: ghost-role-information-space-dragon-description description: ghost-role-information-space-dragon-description
rules: ghost-role-information-space-dragon-rules rules: ghost-role-information-space-dragon-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable

View File

@@ -11,6 +11,8 @@
name: ghost-role-information-remilia-name name: ghost-role-information-remilia-name
description: ghost-role-information-remilia-description description: ghost-role-information-remilia-description
rules: ghost-role-information-familiar-rules rules: ghost-role-information-familiar-rules
mindRoles:
- MindRoleGhostRoleFamiliar
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
- type: Grammar - type: Grammar
attributes: attributes:
@@ -44,6 +46,8 @@
name: ghost-role-information-cerberus-name name: ghost-role-information-cerberus-name
description: ghost-role-information-cerberus-description description: ghost-role-information-cerberus-description
rules: ghost-role-information-familiar-rules rules: ghost-role-information-familiar-rules
mindRoles:
- MindRoleGhostRoleFamiliar
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable

View File

@@ -14,6 +14,8 @@
name: ghost-role-information-guardian-name name: ghost-role-information-guardian-name
description: ghost-role-information-guardian-description description: ghost-role-information-guardian-description
rules: ghost-role-information-familiar-rules rules: ghost-role-information-familiar-rules
mindRoles:
- MindRoleGhostRoleFamiliar
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable

View File

@@ -42,6 +42,8 @@
rules: ghost-role-information-Death-Squad-rules rules: ghost-role-information-Death-Squad-rules
raffle: raffle:
settings: short settings: short
mindRoles:
- MindRoleGhostRoleFamiliar
- type: Loadout - type: Loadout
prototypes: [ DeathSquadGear ] prototypes: [ DeathSquadGear ]
roleLoadout: [ RoleSurvivalEVA ] roleLoadout: [ RoleSurvivalEVA ]

View File

@@ -442,6 +442,9 @@
- HideContextMenu - HideContextMenu
- StationAi - StationAi
- NoConsoleSound - NoConsoleSound
- type: StartingMindRole
mindRole: "MindRoleSiliconBrain"
silent: true
# Hologram projection that the AI's eye tracks. # Hologram projection that the AI's eye tracks.
- type: entity - type: entity
@@ -505,6 +508,9 @@
cell_slot: cell_slot:
name: power-cell-slot-component-slot-name-default name: power-cell-slot-component-slot-name-default
startingItem: PowerCellMedium startingItem: PowerCellMedium
- type: StartingMindRole
mindRole: "MindRoleSiliconBrain"
silent: true
- type: entity - type: entity
id: PlayerBorgSyndicateAssaultBattery id: PlayerBorgSyndicateAssaultBattery

View File

@@ -18,6 +18,8 @@
name: ghost-role-information-skeleton-pirate-name name: ghost-role-information-skeleton-pirate-name
description: ghost-role-information-skeleton-pirate-description description: ghost-role-information-skeleton-pirate-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
@@ -35,6 +37,8 @@
name: ghost-role-information-skeleton-biker-name name: ghost-role-information-skeleton-biker-name
description: ghost-role-information-skeleton-biker-description description: ghost-role-information-skeleton-biker-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable
@@ -51,6 +55,8 @@
name: ghost-role-information-closet-skeleton-name name: ghost-role-information-closet-skeleton-name
description: ghost-role-information-closet-skeleton-description description: ghost-role-information-closet-skeleton-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable

View File

@@ -26,6 +26,8 @@
name: ghost-role-information-syndicate-reinforcement-spy-name name: ghost-role-information-syndicate-reinforcement-spy-name
description: ghost-role-information-syndicate-reinforcement-spy-description description: ghost-role-information-syndicate-reinforcement-spy-description
rules: ghost-role-information-syndicate-reinforcement-rules rules: ghost-role-information-syndicate-reinforcement-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: default settings: default
- type: GhostRoleMobSpawner - type: GhostRoleMobSpawner
@@ -43,6 +45,8 @@
name: ghost-role-information-nukeop-reinforcement-name name: ghost-role-information-nukeop-reinforcement-name
description: ghost-role-information-nukeop-reinforcement-description description: ghost-role-information-nukeop-reinforcement-description
rules: ghost-role-information-nukeop-reinforcement-rules rules: ghost-role-information-nukeop-reinforcement-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: default settings: default
- type: GhostRoleMobSpawner - type: GhostRoleMobSpawner
@@ -58,6 +62,8 @@
name: ghost-role-information-syndicate-monkey-reinforcement-name name: ghost-role-information-syndicate-monkey-reinforcement-name
description: ghost-role-information-syndicate-monkey-reinforcement-description description: ghost-role-information-syndicate-monkey-reinforcement-description
rules: ghost-role-information-syndicate-reinforcement-rules rules: ghost-role-information-syndicate-reinforcement-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: default settings: default
- type: GhostRoleMobSpawner - type: GhostRoleMobSpawner
@@ -85,6 +91,8 @@
name: ghost-role-information-SyndiCat-name name: ghost-role-information-SyndiCat-name
description: ghost-role-information-SyndiCat-description description: ghost-role-information-SyndiCat-description
rules: ghost-role-information-syndicate-reinforcement-rules rules: ghost-role-information-syndicate-reinforcement-rules
mindRoles:
- MindRoleGhostRoleTeamAntagonist
raffle: raffle:
settings: default settings: default
- type: GhostRoleMobSpawner - type: GhostRoleMobSpawner
@@ -103,6 +111,8 @@
name: ghost-role-information-syndicate-cyborg-assault-name name: ghost-role-information-syndicate-cyborg-assault-name
description: ghost-role-information-syndicate-cyborg-description description: ghost-role-information-syndicate-cyborg-description
rules: ghost-role-information-silicon-rules rules: ghost-role-information-silicon-rules
mindRoles:
- MindRoleGhostRoleSilicon
raffle: raffle:
settings: default settings: default
- type: GhostRoleMobSpawner - type: GhostRoleMobSpawner

View File

@@ -38,6 +38,8 @@
roleName: pai-system-role-name roleName: pai-system-role-name
roleDescription: pai-system-role-description roleDescription: pai-system-role-description
roleRules: ghost-role-information-familiar-rules roleRules: ghost-role-information-familiar-rules
mindRoles:
- MindRoleGhostRoleFamiliar
wipeVerbText: pai-system-wipe-device-verb-text wipeVerbText: pai-system-wipe-device-verb-text
wipeVerbPopup: pai-system-wiped-device wipeVerbPopup: pai-system-wiped-device
stopSearchVerbText: pai-system-stop-searching-verb-text stopSearchVerbText: pai-system-stop-searching-verb-text
@@ -91,6 +93,8 @@
roleName: pai-system-role-name-syndicate roleName: pai-system-role-name-syndicate
roleDescription: pai-system-role-description-syndicate roleDescription: pai-system-role-description-syndicate
roleRules: ghost-role-information-familiar-rules roleRules: ghost-role-information-familiar-rules
mindRoles:
- MindRoleGhostRoleFamiliar
- type: IntrinsicRadioTransmitter - type: IntrinsicRadioTransmitter
channels: channels:
- Syndicate - Syndicate
@@ -123,6 +127,8 @@
roleName: pai-system-role-name-potato roleName: pai-system-role-name-potato
roleDescription: pai-system-role-description-potato roleDescription: pai-system-role-description-potato
roleRules: ghost-role-information-familiar-rules roleRules: ghost-role-information-familiar-rules
mindRoles:
- MindRoleGhostRoleFamiliar
- type: Appearance - type: Appearance
- type: GenericVisualizer - type: GenericVisualizer
visuals: visuals:

View File

@@ -89,6 +89,8 @@
roleName: positronic-brain-role-name roleName: positronic-brain-role-name
roleDescription: positronic-brain-role-description roleDescription: positronic-brain-role-description
roleRules: ghost-role-information-silicon-rules roleRules: ghost-role-information-silicon-rules
mindRoles:
- MindRoleGhostRoleSilicon
wipeVerbText: positronic-brain-wipe-device-verb-text wipeVerbText: positronic-brain-wipe-device-verb-text
wipeVerbPopup: positronic-brain-wiped-device wipeVerbPopup: positronic-brain-wiped-device
stopSearchVerbText: positronic-brain-stop-searching-verb-text stopSearchVerbText: positronic-brain-stop-searching-verb-text

View File

@@ -201,7 +201,7 @@
definitions: definitions:
- prefRoles: [ Traitor ] - prefRoles: [ Traitor ]
mindRoles: mindRoles:
- MindRoleTraitor - MindRoleTraitorReinforcement
- type: entity - type: entity
id: Revolutionary id: Revolutionary

View File

@@ -22,14 +22,70 @@
components: components:
- type: ObserverRole - type: ObserverRole
#Ghostrole Marker #Ghost Roles
- type: entity - type: entity
parent: BaseMindRole parent: BaseMindRole
id: MindRoleGhostMarker id: MindRoleGhostRoleNeutral
name: Ghost Role name: Ghost Role
components: components:
- type: GhostRoleMarkerRole - type: GhostRoleMarkerRole
- type: entity
parent: BaseMindRole
id: MindRoleGhostRoleFamiliar
name: Ghost Role (Familiar)
components:
- type: MindRole
roleType: Familiar
- type: GhostRoleMarkerRole
- type: entity
parent: BaseMindRole
id: MindRoleGhostRoleFreeAgent
name: Ghost Role (Free Agent)
components:
- type: MindRole
roleType: FreeAgent
- type: GhostRoleMarkerRole
- type: entity
parent: BaseMindRole
id: MindRoleGhostRoleSilicon
name: Ghost Role (Silicon)
components:
- type: MindRole
roleType: Silicon
- type: GhostRoleMarkerRole
- type: entity
parent: BaseMindRole
id: MindRoleGhostRoleSiliconAntagonist
name: Ghost Role (Silicon Antagonist)
components:
- type: MindRole
roleType: SiliconAntagonist
- type: GhostRoleMarkerRole
- type: entity
parent: BaseMindRole
id: MindRoleGhostRoleSoloAntagonist
name: Ghost Role (Solo Antagonist)
components:
- type: MindRole
roleType: SoloAntagonist
- type: GhostRoleMarkerRole
- type: entity
parent: BaseMindRole
id: MindRoleGhostRoleTeamAntagonist
name: Ghost Role (Team Antagonist)
components:
- type: MindRole
roleType: TeamAntagonist
- type: GhostRoleMarkerRole
# The Job MindRole holds the mob's Job prototype # The Job MindRole holds the mob's Job prototype
- type: entity - type: entity
parent: BaseMindRole parent: BaseMindRole
@@ -38,16 +94,26 @@
# description: # description:
# MindRoleComponent.JobPrototype is filled by SharedJobSystem # MindRoleComponent.JobPrototype is filled by SharedJobSystem
# Subverted Silicon # Silicon
- type: entity
parent: BaseMindRole
id: MindRoleSiliconBrain
name: Borg Brain Role
components:
- type: MindRole
roleType: Silicon
- type: SiliconBrainRole
- type: entity - type: entity
parent: BaseMindRoleAntag parent: BaseMindRoleAntag
id: MindRoleSubvertedSilicon id: MindRoleSubvertedSilicon
name: Subverted Silicon Role name: Subverted Silicon Role
description: description:
components: components:
- type: SubvertedSiliconRole
- type: MindRole - type: MindRole
antagPrototype: SubvertedSilicon antagPrototype: SubvertedSilicon
roleType: SiliconAntagonist
- type: SubvertedSiliconRole
# Dragon # Dragon
- type: entity - type: entity
@@ -58,6 +124,7 @@
components: components:
- type: MindRole - type: MindRole
antagPrototype: Dragon antagPrototype: Dragon
roleType: TeamAntagonist
exclusiveAntag: true exclusiveAntag: true
- type: DragonRole - type: DragonRole
- type: RoleBriefing - type: RoleBriefing
@@ -72,6 +139,7 @@
components: components:
- type: MindRole - type: MindRole
antagPrototype: SpaceNinja antagPrototype: SpaceNinja
roleType: SoloAntagonist
exclusiveAntag: true exclusiveAntag: true
- type: NinjaRole - type: NinjaRole
@@ -83,6 +151,7 @@
# description: mind-role-nukeops-description # description: mind-role-nukeops-description
components: components:
- type: MindRole - type: MindRole
roleType: TeamAntagonist
exclusiveAntag: true exclusiveAntag: true
antagPrototype: Nukeops antagPrototype: Nukeops
- type: NukeopsRole - type: NukeopsRole
@@ -115,6 +184,7 @@
- type: MindRole - type: MindRole
antagPrototype: HeadRev antagPrototype: HeadRev
exclusiveAntag: true exclusiveAntag: true
roleType: TeamAntagonist
- type: RevolutionaryRole - type: RevolutionaryRole
- type: entity - type: entity
@@ -135,6 +205,7 @@
components: components:
- type: MindRole - type: MindRole
antagPrototype: Thief antagPrototype: Thief
roleType: SoloAntagonist
- type: ThiefRole - type: ThiefRole
# Traitors # Traitors
@@ -147,6 +218,7 @@
- type: MindRole - type: MindRole
antagPrototype: Traitor antagPrototype: Traitor
exclusiveAntag: true exclusiveAntag: true
roleType: SoloAntagonist
- type: TraitorRole - type: TraitorRole
- type: entity - type: entity
@@ -158,6 +230,15 @@
- type: MindRole - type: MindRole
antagPrototype: TraitorSleeper antagPrototype: TraitorSleeper
- type: entity
parent: MindRoleTraitor
id: MindRoleTraitorReinforcement
name: Syndicate Reinforcement Role
# description: mind-role-syndicate-reinforcement-description
components:
- type: MindRole
roleType: TeamAntagonist
# Zombie Squad # Zombie Squad
- type: entity - type: entity
parent: BaseMindRoleAntag parent: BaseMindRoleAntag
@@ -168,6 +249,7 @@
- type: MindRole - type: MindRole
antagPrototype: InitialInfected antagPrototype: InitialInfected
exclusiveAntag: true exclusiveAntag: true
roleType: TeamAntagonist
- type: InitialInfectedRole - type: InitialInfectedRole
- type: entity - type: entity
@@ -179,4 +261,5 @@
- type: MindRole - type: MindRole
antagPrototype: Zombie antagPrototype: Zombie
exclusiveAntag: true exclusiveAntag: true
roleType: TeamAntagonist
- type: ZombieRole - type: ZombieRole

View File

@@ -0,0 +1,37 @@
# For use by Role Types
# Do not touch these
- type: roleType
id: Neutral
name: role-type-crew-aligned-name
color: '#eeeeee'
- type: roleType
id: SoloAntagonist
name: role-type-solo-antagonist-name
color: '#d82000'
- type: roleType
id: TeamAntagonist
name: role-type-team-antagonist-name
color: '#d82000'
- type: roleType
id: FreeAgent
name: role-type-free-agent-name
color: '#ffff00'
- type: roleType
id: Familiar
name: role-type-familiar-name
color: '#6495ed'
- type: roleType
id: Silicon
name: role-type-silicon-name
color: '#6495ed'
- type: roleType
id: SiliconAntagonist
name: role-type-silicon-antagonist-name
color: '#c832e6'

View File

@@ -193,6 +193,8 @@
name: ghost-role-information-artifact-name name: ghost-role-information-artifact-name
description: ghost-role-information-artifact-description description: ghost-role-information-artifact-description
rules: ghost-role-information-freeagent-rules rules: ghost-role-information-freeagent-rules
mindRoles:
- MindRoleGhostRoleFreeAgent
raffle: raffle:
settings: default settings: default
- type: GhostTakeoverAvailable - type: GhostTakeoverAvailable