Merge remote-tracking branch 'upstream/stable' into ed-15-10-2024-upstream

# Conflicts:
#	Content.Server/Station/Systems/StationSpawningSystem.cs
This commit is contained in:
Ed
2024-10-15 15:25:44 +03:00
146 changed files with 11990 additions and 20002 deletions

View File

@@ -67,7 +67,7 @@ namespace Content.Server.Access.Systems
if (!TryComp<IdCardComponent>(uid, out var idCard))
return;
var state = new AgentIDCardBoundUserInterfaceState(idCard.FullName ?? "", idCard.JobTitle ?? "", idCard.JobIcon);
var state = new AgentIDCardBoundUserInterfaceState(idCard.FullName ?? "", idCard.LocalizedJobTitle ?? "", idCard.JobIcon);
_uiSystem.SetUiState(uid, AgentIDCardUiKey.Key, state);
}

View File

@@ -96,7 +96,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
PrivilegedIdIsAuthorized(uid, component),
true,
targetIdComponent.FullName,
targetIdComponent.JobTitle,
targetIdComponent.LocalizedJobTitle,
targetAccessComponent.Tags.ToList(),
possibleAccess,
jobProto,

View File

@@ -22,11 +22,17 @@ public sealed class TechAnomalySystem : EntitySystem
{
base.Initialize();
SubscribeLocalEvent<TechAnomalyComponent, MapInitEvent>(OnTechMapInit);
SubscribeLocalEvent<TechAnomalyComponent, AnomalyPulseEvent>(OnPulse);
SubscribeLocalEvent<TechAnomalyComponent, AnomalySupercriticalEvent>(OnSupercritical);
SubscribeLocalEvent<TechAnomalyComponent, AnomalyStabilityChangedEvent>(OnStabilityChanged);
}
private void OnTechMapInit(Entity<TechAnomalyComponent> ent, ref MapInitEvent args)
{
ent.Comp.NextTimer = _timing.CurTime;
}
public override void Update(float frameTime)
{
base.Update(frameTime);

View File

@@ -11,7 +11,6 @@ using Content.Server.Preferences.Managers;
using Content.Server.Roles;
using Content.Server.Roles.Jobs;
using Content.Server.Shuttles.Components;
using Content.Server.Station.Systems;
using Content.Shared.Antag;
using Content.Shared.Clothing;
using Content.Shared.GameTicking;
@@ -20,7 +19,6 @@ using Content.Shared.Ghost;
using Content.Shared.Humanoid;
using Content.Shared.Mind;
using Content.Shared.Players;
using Content.Shared.Preferences.Loadouts;
using Content.Shared.Roles;
using Content.Shared.Whitelist;
using Robust.Server.Audio;
@@ -37,14 +35,14 @@ namespace Content.Server.Antag;
public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelectionComponent>
{
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IServerPreferencesManager _pref = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly GhostRoleSystem _ghostRole = default!;
[Dependency] private readonly JobSystem _jobs = default!;
[Dependency] private readonly LoadoutSystem _loadout = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IServerPreferencesManager _pref = default!;
[Dependency] private readonly RoleSystem _role = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
@@ -193,6 +191,9 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
/// <summary>
/// Chooses antagonists from the given selection of players
/// </summary>
/// <param name="ent">The antagonist rule entity</param>
/// <param name="pool">The players to choose from</param>
/// <param name="midround">Disable picking players for pre-spawn antags in the middle of a round</param>
public void ChooseAntags(Entity<AntagSelectionComponent> ent, IList<ICommonSession> pool, bool midround = false)
{
if (ent.Comp.SelectionsComplete)
@@ -209,8 +210,14 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
/// <summary>
/// Chooses antagonists from the given selection of players for the given antag definition.
/// </summary>
/// <param name="ent">The antagonist rule entity</param>
/// <param name="pool">The players to choose from</param>
/// <param name="def">The antagonist selection parameters and criteria</param>
/// <param name="midround">Disable picking players for pre-spawn antags in the middle of a round</param>
public void ChooseAntags(Entity<AntagSelectionComponent> ent, IList<ICommonSession> pool, AntagSelectionDefinition def, bool midround = false)
public void ChooseAntags(Entity<AntagSelectionComponent> ent,
IList<ICommonSession> pool,
AntagSelectionDefinition def,
bool midround = false)
{
var playerPool = GetPlayerPool(ent, pool, def);
var count = GetTargetAntagCount(ent, GetTotalPlayerCount(pool), def);
@@ -331,7 +338,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
EntityManager.AddComponents(player, def.Components);
// Equip the entity's RoleLoadout and LoadoutGroup
List<ProtoId<StartingGearPrototype>>? gear = new();
List<ProtoId<StartingGearPrototype>> gear = new();
if (def.StartingGear is not null)
gear.Add(def.StartingGear.Value);
@@ -340,8 +347,8 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
if (session != null)
{
var curMind = session.GetMind();
if (curMind == null ||
if (curMind == null ||
!TryComp<MindComponent>(curMind.Value, out var mindComp) ||
mindComp.OwnedEntity != antagEnt)
{
@@ -350,7 +357,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem<AntagSelection
}
_mind.TransferTo(curMind.Value, antagEnt, ghostCheckOverride: true);
_role.MindAddRoles(curMind.Value, def.MindComponents, null, true);
_role.MindAddRoles(curMind.Value, def.MindRoles, null, true);
ent.Comp.SelectedMinds.Add((curMind.Value, Name(player)));
SendBriefing(session, def.Briefing);
}

View File

@@ -3,7 +3,6 @@ using Content.Shared.Antag;
using Content.Shared.Destructible.Thresholds;
using Content.Shared.Preferences.Loadouts;
using Content.Shared.Roles;
using Content.Shared.Storage;
using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Robust.Shared.Player;
@@ -145,10 +144,17 @@ public partial struct AntagSelectionDefinition()
/// <summary>
/// Components added to the player's mind.
/// Do NOT use this to add role-type components. Add those as MindRoles instead
/// </summary>
[DataField]
public ComponentRegistry MindComponents = new();
/// <summary>
/// List of Mind Role Prototypes to be added to the player's mind.
/// </summary>
[DataField]
public List<ProtoId<EntityPrototype>>? MindRoles;
/// <summary>
/// A set of starting gear that's equipped to the player.
/// </summary>

View File

@@ -239,7 +239,7 @@ public sealed class CryostorageSystem : SharedCryostorageSystem
Loc.GetString(
"earlyleave-cryo-announcement",
("character", name),
("entity", ent.Owner),
("entity", ent.Owner), // gender things for supporting downstreams with other languages
("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(jobName))
), Loc.GetString("earlyleave-cryo-sender"),
playDefaultSound: false

View File

@@ -420,6 +420,13 @@ public sealed partial class CargoSystem
return false;
_nameIdentifier.GenerateUniqueName(uid, BountyNameIdentifierGroup, out var randomVal);
var newBounty = new CargoBountyData(bounty, randomVal);
// This bounty id already exists! Probably because NameIdentifierSystem ran out of ids.
if (component.Bounties.Any(b => b.Id == newBounty.Id))
{
Log.Error("Failed to add bounty {ID} because another one with the same ID already existed!", newBounty.Id);
return false;
}
component.Bounties.Add(new CargoBountyData(bounty, randomVal));
_adminLogger.Add(LogType.Action, LogImpact.Low, $"Added bounty \"{bounty.ID}\" (id:{component.TotalBounties}) to station {ToPrettyString(uid)}");
component.TotalBounties++;

View File

@@ -231,7 +231,7 @@ namespace Content.Server.Cloning
// TODO: Ideally, components like this should be components on the mind entity so this isn't necessary.
// Add on special job components to the mob.
if (_jobs.MindTryGetJob(mindEnt, out _, out var prototype))
if (_jobs.MindTryGetJob(mindEnt, out var prototype))
{
foreach (var special in prototype.Special)
{

View File

@@ -0,0 +1,10 @@
namespace Content.Server.Destructible.Thresholds.Behaviors;
[DataDefinition]
public sealed partial class TimerStartBehavior : IThresholdBehavior
{
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
{
system.TriggerSystem.StartTimer(owner, cause);
}
}

View File

@@ -1,49 +1,45 @@
using System.Linq;
using Content.Shared.EntityEffects;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Localizations;
using Robust.Shared.Prototypes;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Roles;
using Content.Shared.Roles.Jobs;
using Content.Shared.Station;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Server.EntityEffects.EffectConditions;
public sealed partial class JobCondition : EntityEffectCondition
{
[DataField(required: true)] public List<ProtoId<JobPrototype>> Job;
public override bool Condition(EntityEffectBaseArgs args)
{
{
args.EntityManager.TryGetComponent<MindContainerComponent>(args.TargetEntity, out var mindContainer);
if (mindContainer != null && mindContainer.Mind != null)
if ( mindContainer is null
|| !args.EntityManager.TryGetComponent<MindComponent>(mindContainer.Mind, out var mind))
return false;
foreach (var roleId in mind.MindRoles)
{
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
if (args.EntityManager.TryGetComponent<JobComponent>(mindContainer?.Mind, out var comp) && prototypeManager.TryIndex(comp.Prototype, out var prototype))
{
foreach (var jobId in Job)
{
if (prototype.ID == jobId)
{
return true;
}
}
}
if(!args.EntityManager.HasComponent<JobRoleComponent>(roleId))
continue;
if(!args.EntityManager.TryGetComponent<MindRoleComponent>(roleId, out var mindRole)
|| mindRole.JobPrototype is null)
continue;
if (Job.Contains(mindRole.JobPrototype.Value))
return true;
}
return false;
}
public override string GuidebookExplanation(IPrototypeManager prototype)
{
var localizedNames = Job.Select(jobId => prototype.Index(jobId).LocalizedName).ToList();
return Loc.GetString("reagent-effect-condition-guidebook-job-condition", ("job", ContentLocalizationManager.FormatListToOr(localizedNames)));
}
}

View File

@@ -0,0 +1,15 @@
using Content.Shared.DeviceLinking;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Explosion.Components
{
/// <summary>
/// Sends a trigger when signal is received.
/// </summary>
[RegisterComponent]
public sealed partial class TimerStartOnSignalComponent : Component
{
[DataField("port", customTypeSerializer: typeof(PrototypeIdSerializer<SinkPortPrototype>))]
public string Port = "Timer";
}
}

View File

@@ -488,9 +488,12 @@ public sealed partial class ExplosionSystem
&& physics.BodyType == BodyType.Dynamic)
{
var pos = _transformSystem.GetWorldPosition(xform);
var dir = pos - epicenter.Position;
if (dir.IsLengthZero())
dir = _robustRandom.NextVector2().Normalized();
_throwingSystem.TryThrow(
uid,
pos - epicenter.Position,
dir,
physics,
xform,
_projectileQuery,

View File

@@ -11,6 +11,9 @@ namespace Content.Server.Explosion.EntitySystems
{
SubscribeLocalEvent<TriggerOnSignalComponent,SignalReceivedEvent>(OnSignalReceived);
SubscribeLocalEvent<TriggerOnSignalComponent,ComponentInit>(OnInit);
SubscribeLocalEvent<TimerStartOnSignalComponent,SignalReceivedEvent>(OnTimerSignalReceived);
SubscribeLocalEvent<TimerStartOnSignalComponent,ComponentInit>(OnTimerSignalInit);
}
private void OnSignalReceived(EntityUid uid, TriggerOnSignalComponent component, ref SignalReceivedEvent args)
@@ -24,5 +27,17 @@ namespace Content.Server.Explosion.EntitySystems
{
_signalSystem.EnsureSinkPorts(uid, component.Port);
}
private void OnTimerSignalReceived(EntityUid uid, TimerStartOnSignalComponent component, ref SignalReceivedEvent args)
{
if (args.Port != component.Port)
return;
StartTimer(uid, args.Trigger);
}
private void OnTimerSignalInit(EntityUid uid, TimerStartOnSignalComponent component, ComponentInit args)
{
_signalSystem.EnsureSinkPorts(uid, component.Port);
}
}
}

View File

@@ -4,6 +4,7 @@ using Content.Server.Discord;
using Content.Server.GameTicking.Events;
using Content.Server.Ghost;
using Content.Server.Maps;
using Content.Server.Roles;
using Content.Shared.CCVar;
using Content.Shared.Database;
using Content.Shared.GameTicking;
@@ -26,6 +27,7 @@ namespace Content.Server.GameTicking
public sealed partial class GameTicker
{
[Dependency] private readonly DiscordWebhook _discord = default!;
[Dependency] private readonly RoleSystem _role = default!;
[Dependency] private readonly ITaskManager _taskManager = default!;
private static readonly Counter RoundNumberMetric = Metrics.CreateCounter(
@@ -339,8 +341,23 @@ namespace Content.Server.GameTicking
RunLevel = GameRunLevel.PostRound;
ShowRoundEndScoreboard(text);
SendRoundEndDiscordMessage();
try
{
ShowRoundEndScoreboard(text);
}
catch (Exception e)
{
Log.Error($"Error while showing round end scoreboard: {e}");
}
try
{
SendRoundEndDiscordMessage();
}
catch (Exception e)
{
Log.Error($"Error while sending round end Discord message: {e}");
}
}
public void ShowRoundEndScoreboard(string text = "")
@@ -373,7 +390,7 @@ namespace Content.Server.GameTicking
var userId = mind.UserId ?? mind.OriginalOwnerUserId;
var connected = false;
var observer = HasComp<ObserverRoleComponent>(mindId);
var observer = _role.MindHasRole<ObserverRoleComponent>(mindId);
// Continuing
if (userId != null && _playerManager.ValidSessionId(userId.Value))
{
@@ -400,7 +417,7 @@ namespace Content.Server.GameTicking
_pvsOverride.AddGlobalOverride(GetNetEntity(entity.Value), recursive: true);
}
var roles = _roles.MindGetAllRoles(mindId);
var roles = _roles.MindGetAllRoleInfo(mindId);
var playerEndRoundInfo = new RoundEndMessageEvent.RoundEndPlayerInfo()
{

View File

@@ -3,8 +3,6 @@ using System.Linq;
using System.Numerics;
using Content.Server.Administration.Managers;
using Content.Server.GameTicking.Events;
using Content.Server.Ghost;
using Content.Server.Shuttles.Components;
using Content.Server.Spawners.Components;
using Content.Server.Speech.Components;
using Content.Server.Station.Components;
@@ -224,13 +222,12 @@ namespace Content.Server.GameTicking
_mind.SetUserId(newMind, data.UserId);
var jobPrototype = _prototypeManager.Index<JobPrototype>(jobId);
var job = new JobComponent {Prototype = jobId};
_roles.MindAddRole(newMind, job, silent: silent);
_roles.MindAddJobRole(newMind, silent: silent, jobPrototype:jobId);
var jobName = _jobs.MindTryGetJobName(newMind);
_playTimeTrackings.PlayerRolesChanged(player);
var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, job, character);
var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, jobId, character);
DebugTools.AssertNotNull(mobMaybe);
var mob = mobMaybe!.Value;
@@ -271,13 +268,17 @@ namespace Content.Server.GameTicking
_stationJobs.TryAssignJob(station, jobPrototype, player.UserId);
if (lateJoin)
{
_adminLogger.Add(LogType.LateJoin,
LogImpact.Medium,
$"Player {player.Name} late joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}.");
}
else
{
_adminLogger.Add(LogType.RoundStartJoin,
LogImpact.Medium,
$"Player {player.Name} joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}.");
}
// Make sure they're aware of extended access.
if (Comp<StationJobsComponent>(station).ExtendedAccess
@@ -363,7 +364,7 @@ namespace Content.Server.GameTicking
var (mindId, mindComp) = _mind.CreateMind(player.UserId, name);
mind = (mindId, mindComp);
_mind.SetUserId(mind.Value, player.UserId);
_roles.MindAddRole(mind.Value, new ObserverRoleComponent());
_roles.MindAddRole(mind.Value, "MindRoleObserver");
}
var ghost = _ghost.SpawnGhost(mind.Value);

View File

@@ -9,7 +9,6 @@ using Content.Server.RoundEnd;
using Content.Server.Shuttles.Events;
using Content.Server.Shuttles.Systems;
using Content.Server.Station.Components;
using Content.Server.Store.Components;
using Content.Server.Store.Systems;
using Content.Shared.GameTicking.Components;
using Content.Shared.Mobs;
@@ -31,9 +30,9 @@ namespace Content.Server.GameTicking.Rules;
public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
{
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly EmergencyShuttleSystem _emergency = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly RoundEndSystem _roundEndSystem = default!;
[Dependency] private readonly StoreSystem _store = default!;
@@ -57,6 +56,8 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
SubscribeLocalEvent<NukeOperativeComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<NukeOperativeComponent, EntityZombifiedEvent>(OnOperativeZombified);
SubscribeLocalEvent<NukeopsRoleComponent, GetBriefingEvent>(OnGetBriefing);
SubscribeLocalEvent<ConsoleFTLAttemptEvent>(OnShuttleFTLAttempt);
SubscribeLocalEvent<WarDeclaredEvent>(OnWarDeclared);
SubscribeLocalEvent<CommunicationConsoleCallShuttleAttemptEvent>(OnShuttleCallAttempt);
@@ -65,7 +66,9 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
SubscribeLocalEvent<NukeopsRuleComponent, RuleLoadedGridsEvent>(OnRuleLoadedGrids);
}
protected override void Started(EntityUid uid, NukeopsRuleComponent component, GameRuleComponent gameRule,
protected override void Started(EntityUid uid,
NukeopsRuleComponent component,
GameRuleComponent gameRule,
GameRuleStartedEvent args)
{
var eligible = new List<Entity<StationEventEligibleComponent, NpcFactionMemberComponent>>();
@@ -85,7 +88,9 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
}
#region Event Handlers
protected override void AppendRoundEndText(EntityUid uid, NukeopsRuleComponent component, GameRuleComponent gameRule,
protected override void AppendRoundEndText(EntityUid uid,
NukeopsRuleComponent component,
GameRuleComponent gameRule,
ref RoundEndTextAppendEvent args)
{
var winText = Loc.GetString($"nukeops-{component.WinType.ToString().ToLower()}");
@@ -227,7 +232,8 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
// If the disk is currently at Central Command, the crew wins - just slightly.
// This also implies that some nuclear operatives have died.
SetWinType(ent, diskAtCentCom
SetWinType(ent,
diskAtCentCom
? WinType.CrewMinor
: WinType.OpsMinor);
ent.Comp.WinConditions.Add(diskAtCentCom
@@ -456,8 +462,11 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
: WinCondition.AllNukiesDead);
SetWinType(ent, WinType.CrewMajor, false);
_roundEndSystem.DoRoundEndBehavior(
nukeops.RoundEndBehavior, nukeops.EvacShuttleTime, nukeops.RoundEndTextSender, nukeops.RoundEndTextShuttleCall, nukeops.RoundEndTextAnnouncement);
_roundEndSystem.DoRoundEndBehavior(nukeops.RoundEndBehavior,
nukeops.EvacShuttleTime,
nukeops.RoundEndTextSender,
nukeops.RoundEndTextShuttleCall,
nukeops.RoundEndTextAnnouncement);
// prevent it called multiple times
nukeops.RoundEndBehavior = RoundEndBehavior.Nothing;
@@ -465,16 +474,22 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
private void OnAfterAntagEntSelected(Entity<NukeopsRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
{
if (ent.Comp.TargetStation is not { } station)
return;
var target = (ent.Comp.TargetStation is not null) ? Name(ent.Comp.TargetStation.Value) : "the target";
_antag.SendBriefing(args.Session, Loc.GetString("nukeops-welcome",
("station", station),
_antag.SendBriefing(args.Session,
Loc.GetString("nukeops-welcome",
("station", target),
("name", Name(ent))),
Color.Red,
ent.Comp.GreetSoundNotification);
}
private void OnGetBriefing(Entity<NukeopsRoleComponent> role, ref GetBriefingEvent args)
{
// TODO Different character screen briefing for the 3 nukie types
args.Append(Loc.GetString("nukeops-briefing"));
}
/// <remarks>
/// Is this method the shitty glue holding together the last of my sanity? yes.
/// Do i have a better solution? not presently.

View File

@@ -15,7 +15,6 @@ using Content.Shared.Database;
using Content.Shared.GameTicking.Components;
using Content.Shared.Humanoid;
using Content.Shared.IdentityManagement;
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Mindshield.Components;
using Content.Shared.Mobs;
@@ -38,8 +37,8 @@ namespace Content.Server.GameTicking.Rules;
public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleComponent>
{
[Dependency] private readonly IAdminLogManager _adminLogManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!;
[Dependency] private readonly EuiManager _euiMan = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
@@ -49,7 +48,7 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
[Dependency] private readonly SharedStunSystem _stun = default!;
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!;
[Dependency] private readonly IGameTiming _timing = default!;
//Used in OnPostFlash, no reference to the rule component is available
public readonly ProtoId<NpcFactionPrototype> RevolutionaryNpcFaction = "Revolutionary";
@@ -59,9 +58,12 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
{
base.Initialize();
SubscribeLocalEvent<CommandStaffComponent, MobStateChangedEvent>(OnCommandMobStateChanged);
SubscribeLocalEvent<HeadRevolutionaryComponent, MobStateChangedEvent>(OnHeadRevMobStateChanged);
SubscribeLocalEvent<RevolutionaryRoleComponent, GetBriefingEvent>(OnGetBriefing);
SubscribeLocalEvent<HeadRevolutionaryComponent, AfterFlashedEvent>(OnPostFlash);
SubscribeLocalEvent<HeadRevolutionaryComponent, MobStateChangedEvent>(OnHeadRevMobStateChanged);
SubscribeLocalEvent<RevolutionaryRoleComponent, GetBriefingEvent>(OnGetBriefing);
}
protected override void Started(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
@@ -85,7 +87,9 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
}
}
protected override void AppendRoundEndText(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule,
protected override void AppendRoundEndText(EntityUid uid,
RevolutionaryRuleComponent component,
GameRuleComponent gameRule,
ref RoundEndTextAppendEvent args)
{
base.AppendRoundEndText(uid, component, gameRule, ref args);
@@ -101,7 +105,9 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
args.AddLine(Loc.GetString("rev-headrev-count", ("initialCount", sessionData.Count)));
foreach (var (mind, data, name) in sessionData)
{
var count = CompOrNull<RevolutionaryRoleComponent>(mind)?.ConvertedCount ?? 0;
_role.MindHasRole<RevolutionaryRoleComponent>(mind, out var role);
var count = CompOrNull<RevolutionaryRoleComponent>(role)?.ConvertedCount ?? 0;
args.AddLine(Loc.GetString("rev-headrev-name-user",
("name", name),
("username", data.UserName),
@@ -113,10 +119,8 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
private void OnGetBriefing(EntityUid uid, RevolutionaryRoleComponent comp, ref GetBriefingEvent args)
{
if (!TryComp<MindComponent>(uid, out var mind) || mind.OwnedEntity == null)
return;
var head = HasComp<HeadRevolutionaryComponent>(mind.OwnedEntity);
var ent = args.Mind.Comp.OwnedEntity;
var head = HasComp<HeadRevolutionaryComponent>(ent);
args.Append(Loc.GetString(head ? "head-rev-briefing" : "rev-briefing"));
}
@@ -145,15 +149,20 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
if (ev.User != null)
{
_adminLogManager.Add(LogType.Mind, LogImpact.Medium, $"{ToPrettyString(ev.User.Value)} converted {ToPrettyString(ev.Target)} into a Revolutionary");
_adminLogManager.Add(LogType.Mind,
LogImpact.Medium,
$"{ToPrettyString(ev.User.Value)} converted {ToPrettyString(ev.Target)} into a Revolutionary");
if (_mind.TryGetRole<RevolutionaryRoleComponent>(ev.User.Value, out var headrev))
headrev.ConvertedCount++;
if (_mind.TryGetMind(ev.User.Value, out var revMindId, out _))
{
if (_role.MindHasRole<RevolutionaryRoleComponent>(revMindId, out _, out var role))
role.Value.Comp.ConvertedCount++;
}
}
if (mindId == default || !_role.MindHasRole<RevolutionaryRoleComponent>(mindId))
{
_role.MindAddRole(mindId, new RevolutionaryRoleComponent { PrototypeId = RevPrototypeId });
_role.MindAddRole(mindId, "MindRoleRevolutionary");
}
if (mind?.Session != null)

View File

@@ -1,18 +1,12 @@
using Content.Server.Antag;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Mind;
using Content.Server.Objectives;
using Content.Server.Roles;
using Content.Shared.Humanoid;
using Content.Shared.Mind;
using Content.Shared.Objectives.Components;
using Robust.Shared.Random;
namespace Content.Server.GameTicking.Rules;
public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
{
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly AntagSelectionSystem _antag = default!;
public override void Initialize()
@@ -24,32 +18,33 @@ public sealed class ThiefRuleSystem : GameRuleSystem<ThiefRuleComponent>
SubscribeLocalEvent<ThiefRoleComponent, GetBriefingEvent>(OnGetBriefing);
}
private void AfterAntagSelected(Entity<ThiefRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
// Greeting upon thief activation
private void AfterAntagSelected(Entity<ThiefRuleComponent> mindId, ref AfterAntagEntitySelectedEvent args)
{
if (!_mindSystem.TryGetMind(args.EntityUid, out var mindId, out var mind))
return;
//Generate objectives
_antag.SendBriefing(args.EntityUid, MakeBriefing(args.EntityUid), null, null);
var ent = args.EntityUid;
_antag.SendBriefing(ent, MakeBriefing(ent), null, null);
}
//Add mind briefing
private void OnGetBriefing(Entity<ThiefRoleComponent> thief, ref GetBriefingEvent args)
// Character screen briefing
private void OnGetBriefing(Entity<ThiefRoleComponent> role, ref GetBriefingEvent args)
{
if (!TryComp<MindComponent>(thief.Owner, out var mind) || mind.OwnedEntity == null)
return;
var ent = args.Mind.Comp.OwnedEntity;
args.Append(MakeBriefing(mind.OwnedEntity.Value));
if (ent is null)
return;
args.Append(MakeBriefing(ent.Value));
}
private string MakeBriefing(EntityUid thief)
private string MakeBriefing(EntityUid ent)
{
var isHuman = HasComp<HumanoidAppearanceComponent>(thief);
var isHuman = HasComp<HumanoidAppearanceComponent>(ent);
var briefing = isHuman
? Loc.GetString("thief-role-greeting-human")
: Loc.GetString("thief-role-greeting-animal");
briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n";
if (isHuman)
briefing += "\n \n" + Loc.GetString("thief-role-greeting-equipment") + "\n";
return briefing;
}
}

View File

@@ -1,3 +1,4 @@
using Content.Server.Administration.Logs;
using Content.Server.Antag;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Mind;
@@ -5,12 +6,11 @@ using Content.Server.Objectives;
using Content.Server.PDA.Ringer;
using Content.Server.Roles;
using Content.Server.Traitor.Uplink;
using Content.Shared.Database;
using Content.Shared.GameTicking.Components;
using Content.Shared.Mind;
using Content.Shared.NPC.Systems;
using Content.Shared.Objectives.Components;
using Content.Shared.PDA;
using Content.Shared.Radio;
using Content.Shared.Roles;
using Content.Shared.Roles.Jobs;
using Content.Shared.Roles.RoleCodeword;
@@ -25,29 +25,29 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
{
private static readonly Color TraitorCodewordColor = Color.FromHex("#cc3b3b");
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly SharedJobSystem _jobs = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly UplinkSystem _uplink = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly SharedRoleSystem _roleSystem = default!;
[Dependency] private readonly SharedJobSystem _jobs = default!;
[Dependency] private readonly SharedRoleCodewordSystem _roleCodewordSystem = default!;
[Dependency] private readonly SharedRoleSystem _roleSystem = default!;
[Dependency] private readonly UplinkSystem _uplink = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TraitorRuleComponent, AfterAntagEntitySelectedEvent>(AfterEntitySelected);
SubscribeLocalEvent<TraitorRuleComponent, ObjectivesTextPrependEvent>(OnObjectivesTextPrepend);
}
protected override void Added(EntityUid uid, TraitorRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
{
base.Added(uid, component, gameRule, args);
SetCodewords(component);
SetCodewords(component, args.RuleEntity);
}
private void AfterEntitySelected(Entity<TraitorRuleComponent> ent, ref AfterAntagEntitySelectedEvent args)
@@ -55,9 +55,10 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
MakeTraitor(args.EntityUid, ent);
}
private void SetCodewords(TraitorRuleComponent component)
private void SetCodewords(TraitorRuleComponent component, EntityUid ruleEntity)
{
component.Codewords = GenerateTraitorCodewords(component);
_adminLogger.Add(LogType.EventStarted, LogImpact.Low, $"Codewords generated for game rule {ToPrettyString(ruleEntity)}: {string.Join(", ", component.Codewords)}");
}
public string[] GenerateTraitorCodewords(TraitorRuleComponent component)
@@ -76,7 +77,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true)
{
//Grab the mind if it wasnt provided
//Grab the mind if it wasn't provided
if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind))
return false;
@@ -88,7 +89,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
{
// Calculate the amount of currency on the uplink.
var startingBalance = component.StartingBalance;
if (_jobs.MindTryGetJob(mindId, out _, out var prototype))
if (_jobs.MindTryGetJob(mindId, out var prototype))
startingBalance = Math.Max(startingBalance - prototype.AntagAdvantage, 0);
// creadth: we need to create uplink for the antag.
@@ -107,14 +108,18 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
_antag.SendBriefing(traitor, GenerateBriefing(component.Codewords, code, issuer), null, component.GreetSoundNotification);
component.TraitorMinds.Add(mindId);
// Assign briefing
_roleSystem.MindAddRole(mindId, new RoleBriefingComponent
//Since this provides neither an antag/job prototype, nor antag status/roletype,
//and is intrinsically related to the traitor role
//it does not need to be a separate Mind Role Entity
_roleSystem.MindHasRole<TraitorRoleComponent>(mindId, out var traitorRole);
if (traitorRole is not null)
{
Briefing = briefing
}, mind, true);
AddComp<RoleBriefingComponent>(traitorRole.Value.Owner);
Comp<RoleBriefingComponent>(traitorRole.Value.Owner).Briefing = briefing;
}
// Send codewords to only the traitor client
var color = TraitorCodewordColor; // Fall back to a dark red Syndicate color if a prototype is not found

View File

@@ -13,6 +13,7 @@ using Content.Shared.Mind;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Roles;
using Content.Shared.Zombies;
using Robust.Shared.Player;
using Robust.Shared.Timing;
@@ -22,15 +23,16 @@ namespace Content.Server.GameTicking.Rules;
public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
{
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly ZombieSystem _zombie = default!;
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ZombieSystem _zombie = default!;
public override void Initialize()
{
@@ -41,23 +43,20 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
SubscribeLocalEvent<IncurableZombieComponent, ZombifySelfActionEvent>(OnZombifySelf);
}
private void OnGetBriefing(EntityUid uid, InitialInfectedRoleComponent component, ref GetBriefingEvent args)
private void OnGetBriefing(Entity<InitialInfectedRoleComponent> role, ref GetBriefingEvent args)
{
if (!TryComp<MindComponent>(uid, out var mind) || mind.OwnedEntity == null)
return;
if (HasComp<ZombieRoleComponent>(uid)) // don't show both briefings
return;
args.Append(Loc.GetString("zombie-patientzero-role-greeting"));
if (!_roles.MindHasRole<ZombieRoleComponent>(args.Mind.Owner))
args.Append(Loc.GetString("zombie-patientzero-role-greeting"));
}
private void OnGetBriefing(EntityUid uid, ZombieRoleComponent component, ref GetBriefingEvent args)
private void OnGetBriefing(Entity<ZombieRoleComponent> role, ref GetBriefingEvent args)
{
if (!TryComp<MindComponent>(uid, out var mind) || mind.OwnedEntity == null)
return;
args.Append(Loc.GetString("zombie-infection-greeting"));
}
protected override void AppendRoundEndText(EntityUid uid, ZombieRuleComponent component, GameRuleComponent gameRule,
protected override void AppendRoundEndText(EntityUid uid,
ZombieRuleComponent component,
GameRuleComponent gameRule,
ref RoundEndTextAppendEvent args)
{
base.AppendRoundEndText(uid, component, gameRule, ref args);

View File

@@ -1,11 +1,13 @@
namespace Content.Server.Ghost.Roles;
using Content.Shared.Roles;
namespace Content.Server.Ghost.Roles;
/// <summary>
/// This is used for round end display of ghost roles.
/// It may also be used to ensure some ghost roles count as antagonists in future.
/// </summary>
[RegisterComponent]
public sealed partial class GhostRoleMarkerRoleComponent : Component
public sealed partial class GhostRoleMarkerRoleComponent : BaseMindRoleComponent
{
[DataField("name")] public string? Name;
}

View File

@@ -71,18 +71,22 @@ public sealed class GhostRoleSystem : EntitySystem
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
SubscribeLocalEvent<PlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<GhostTakeoverAvailableComponent, MindAddedMessage>(OnMindAdded);
SubscribeLocalEvent<GhostTakeoverAvailableComponent, MindRemovedMessage>(OnMindRemoved);
SubscribeLocalEvent<GhostTakeoverAvailableComponent, MobStateChangedEvent>(OnMobStateChanged);
SubscribeLocalEvent<GhostTakeoverAvailableComponent, TakeGhostRoleEvent>(OnTakeoverTakeRole);
SubscribeLocalEvent<GhostRoleComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<GhostRoleComponent, ComponentStartup>(OnRoleStartup);
SubscribeLocalEvent<GhostRoleComponent, ComponentShutdown>(OnRoleShutdown);
SubscribeLocalEvent<GhostRoleComponent, EntityPausedEvent>(OnPaused);
SubscribeLocalEvent<GhostRoleComponent, EntityUnpausedEvent>(OnUnpaused);
SubscribeLocalEvent<GhostRoleRaffleComponent, ComponentInit>(OnRaffleInit);
SubscribeLocalEvent<GhostRoleRaffleComponent, ComponentShutdown>(OnRaffleShutdown);
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, TakeGhostRoleEvent>(OnSpawnerTakeRole);
SubscribeLocalEvent<GhostTakeoverAvailableComponent, TakeGhostRoleEvent>(OnTakeoverTakeRole);
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, GetVerbsEvent<Verb>>(OnVerb);
SubscribeLocalEvent<GhostRoleMobSpawnerComponent, GhostRoleRadioMessage>(OnGhostRoleRadioMessage);
_playerManager.PlayerStatusChanged += PlayerStatusChanged;
@@ -509,7 +513,11 @@ public sealed class GhostRoleSystem : EntitySystem
var newMind = _mindSystem.CreateMind(player.UserId,
EntityManager.GetComponent<MetaDataComponent>(mob).EntityName);
_roleSystem.MindAddRole(newMind, new GhostRoleMarkerRoleComponent { Name = role.RoleName });
_roleSystem.MindAddRole(newMind, "MindRoleGhostMarker");
if(_roleSystem.MindHasRole<GhostRoleMarkerRoleComponent>(newMind, out _, out var markerRole))
markerRole.Value.Comp.Name = role.RoleName;
_mindSystem.SetUserId(newMind, player.UserId);
_mindSystem.TransferTo(newMind, mob);
@@ -602,10 +610,7 @@ public sealed class GhostRoleSystem : EntitySystem
if (ghostRole.JobProto != null)
{
if (HasComp<JobComponent>(args.Mind))
_roleSystem.MindRemoveRole<JobComponent>(args.Mind);
_roleSystem.MindAddRole(args.Mind, new JobComponent { Prototype = ghostRole.JobProto });
_roleSystem.MindAddJobRole(args.Mind, args.Mind, silent:false,ghostRole.JobProto);
}
ghostRole.Taken = true;

View File

@@ -166,7 +166,7 @@ public sealed class IdentitySystem : SharedIdentitySystem
if (_idCard.TryFindIdCard(target, out var id))
{
presumedName = string.IsNullOrWhiteSpace(id.Comp.FullName) ? null : id.Comp.FullName;
presumedJob = id.Comp.JobTitle?.ToLowerInvariant();
presumedJob = id.Comp.LocalizedJobTitle?.ToLowerInvariant();
}
// If it didn't find a job, that's fine.

View File

@@ -5,6 +5,7 @@ using Content.Server.Stack;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Destructible;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
using Content.Shared.Kitchen;
@@ -122,6 +123,9 @@ namespace Content.Server.Kitchen.EntitySystems
if (solution.Volume > containerSolution.AvailableVolume)
continue;
var dev = new DestructionEventArgs();
RaiseLocalEvent(item, dev);
QueueDel(item);
}

View File

@@ -175,6 +175,10 @@ public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
var materialPerStack = composition.MaterialComposition[materialProto.ID];
var amountToSpawn = amount / materialPerStack;
overflowMaterial = amount - amountToSpawn * materialPerStack;
if (amountToSpawn == 0)
return new List<EntityUid>();
return _stackSystem.SpawnMultiple(materialProto.StackEntity, amountToSpawn, coordinates);
}

View File

@@ -363,8 +363,8 @@ public sealed class SuitSensorSystem : EntitySystem
{
if (card.Comp.FullName != null)
userName = card.Comp.FullName;
if (card.Comp.JobTitle != null)
userJob = card.Comp.JobTitle;
if (card.Comp.LocalizedJobTitle != null)
userJob = card.Comp.LocalizedJobTitle;
userJobIcon = card.Comp.JobIcon;
foreach (var department in card.Comp.JobDepartments)

View File

@@ -43,7 +43,7 @@ namespace Content.Server.Mind.Commands
builder.AppendFormat("player: {0}, mob: {1}\nroles: ", mind.UserId, mind.OwnedEntity);
var roles = _entities.System<SharedRoleSystem>();
foreach (var role in roles.MindGetAllRoles(mindId))
foreach (var role in roles.MindGetAllRoleInfo(mindId))
{
builder.AppendFormat("{0} ", role.Name);
}

View File

@@ -1,5 +0,0 @@
using Content.Shared.Movement.Systems;
namespace Content.Server.Movement.Systems;
public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem;

View File

@@ -14,6 +14,7 @@ public sealed class NameIdentifierSystem : EntitySystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly ILogManager _logManager = default!;
/// <summary>
/// Free IDs available per <see cref="NameIdentifierGroupPrototype"/>.
@@ -118,23 +119,39 @@ public sealed class NameIdentifierSystem : EntitySystem
private void InitialSetupPrototypes()
{
foreach (var proto in _prototypeManager.EnumeratePrototypes<NameIdentifierGroupPrototype>())
{
AddGroup(proto);
}
EnsureIds();
}
private void AddGroup(NameIdentifierGroupPrototype proto)
private void FillGroup(NameIdentifierGroupPrototype proto, List<int> values)
{
var values = new List<int>(proto.MaxValue - proto.MinValue);
values.Clear();
for (var i = proto.MinValue; i < proto.MaxValue; i++)
{
values.Add(i);
}
_robustRandom.Shuffle(values);
CurrentIds.Add(proto.ID, values);
}
private List<int> GetOrCreateIdList(NameIdentifierGroupPrototype proto)
{
if (!CurrentIds.TryGetValue(proto.ID, out var ids))
{
ids = new List<int>(proto.MaxValue - proto.MinValue);
CurrentIds.Add(proto.ID, ids);
}
return ids;
}
private void EnsureIds()
{
foreach (var proto in _prototypeManager.EnumeratePrototypes<NameIdentifierGroupPrototype>())
{
var ids = GetOrCreateIdList(proto);
FillGroup(proto, ids);
}
}
private void OnReloadPrototypes(PrototypesReloadedEventArgs ev)
@@ -159,19 +176,20 @@ public sealed class NameIdentifierSystem : EntitySystem
foreach (var proto in set.Modified.Values)
{
var name_proto = (NameIdentifierGroupPrototype) proto;
// Only bother adding new ones.
if (CurrentIds.ContainsKey(proto.ID))
continue;
AddGroup((NameIdentifierGroupPrototype) proto);
var ids = GetOrCreateIdList(name_proto);
FillGroup(name_proto, ids);
}
}
private void CleanupIds(RoundRestartCleanupEvent ev)
{
foreach (var values in CurrentIds.Values)
{
_robustRandom.Shuffle(values);
}
EnsureIds();
}
}

View File

@@ -33,6 +33,7 @@ using System.Linq;
using Content.Shared.Containers.ItemSlots;
using Robust.Server.GameObjects;
using Content.Shared.Whitelist;
using Content.Shared.Destructible;
namespace Content.Server.Nutrition.EntitySystems;
@@ -335,6 +336,9 @@ public sealed class FoodSystem : EntitySystem
if (ev.Cancelled)
return;
var dev = new DestructionEventArgs();
RaiseLocalEvent(food, dev);
if (component.Trash.Count == 0)
{
QueueDel(food);

View File

@@ -89,7 +89,7 @@ public sealed class KillPersonConditionSystem : EntitySystem
foreach (var mind in allHumans)
{
// RequireAdminNotify used as a cheap way to check for command department
if (_job.MindTryGetJob(mind, out _, out var prototype) && prototype.RequireAdminNotify)
if (_job.MindTryGetJob(mind, out var prototype) && prototype.RequireAdminNotify)
allHeads.Add(mind);
}

View File

@@ -1,9 +1,10 @@
using Content.Server.Objectives.Components;
using Content.Server.Roles;
using Content.Server.Warps;
using Content.Shared.Objectives.Components;
using Content.Shared.Ninja.Components;
using Content.Shared.Roles;
using Robust.Shared.Random;
using Content.Server.Roles;
namespace Content.Server.Objectives.Systems;
@@ -16,6 +17,7 @@ public sealed class NinjaConditionsSystem : EntitySystem
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly NumberObjectiveSystem _number = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
public override void Initialize()
{
@@ -46,10 +48,8 @@ public sealed class NinjaConditionsSystem : EntitySystem
// spider charge
private void OnSpiderChargeRequirementCheck(EntityUid uid, SpiderChargeConditionComponent comp, ref RequirementCheckEvent args)
{
if (args.Cancelled || !HasComp<NinjaRoleComponent>(args.MindId))
{
if (args.Cancelled || !_roles.MindHasRole<NinjaRoleComponent>(args.MindId))
return;
}
// choose spider charge detonation point
var warps = new List<EntityUid>();

View File

@@ -21,7 +21,7 @@ public sealed class NotCommandRequirementSystem : EntitySystem
return;
// cheap equivalent to checking that job department is command, since all command members require admin notification when leaving
if (_job.MindTryGetJob(args.MindId, out _, out var prototype) && prototype.RequireAdminNotify)
if (_job.MindTryGetJob(args.MindId, out var prototype) && prototype.RequireAdminNotify)
args.Cancelled = true;
}
}

View File

@@ -8,6 +8,8 @@ namespace Content.Server.Objectives.Systems;
/// </summary>
public sealed class NotJobRequirementSystem : EntitySystem
{
[Dependency] private readonly SharedJobSystem _jobs = default!;
public override void Initialize()
{
base.Initialize();
@@ -20,11 +22,10 @@ public sealed class NotJobRequirementSystem : EntitySystem
if (args.Cancelled)
return;
// if player has no job then don't care
if (!TryComp<JobComponent>(args.MindId, out var job))
return;
_jobs.MindTryGetJob(args.MindId, out var proto);
if (job.Prototype == comp.Job)
// if player has no job then don't care
if (proto is not null && proto.ID == comp.Job)
args.Cancelled = true;
}
}

View File

@@ -22,8 +22,6 @@ public sealed class RoleRequirementSystem : EntitySystem
if (args.Cancelled)
return;
// this whitelist trick only works because roles are components on the mind and not entities
// if that gets reworked then this will need changing
if (_whitelistSystem.IsWhitelistFail(comp.Roles, args.MindId))
args.Cancelled = true;
}

View File

@@ -192,7 +192,7 @@ namespace Content.Server.PDA
{
ActualOwnerName = pda.OwnerName,
IdOwner = id?.FullName,
JobTitle = id?.JobTitle,
JobTitle = id?.LocalizedJobTitle,
StationAlertLevel = pda.StationAlertLevel,
StationAlertColor = pda.StationAlertColor
},

View File

@@ -30,14 +30,15 @@ namespace Content.Server.Players.PlayTimeTracking;
/// </summary>
public sealed class PlayTimeTrackingSystem : EntitySystem
{
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IAfkManager _afk = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IPrototypeManager _prototypes = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly MindSystem _minds = default!;
[Dependency] private readonly PlayTimeTrackingManager _tracking = default!;
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IServerPreferencesManager _preferencesManager = default!;
[Dependency] private readonly IPrototypeManager _prototypes = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
[Dependency] private readonly PlayTimeTrackingManager _tracking = default!;
public override void Initialize()
{
@@ -101,10 +102,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
public IEnumerable<string> GetTimedRoles(EntityUid mindId)
{
var ev = new MindGetAllRolesEvent(new List<RoleInfo>());
RaiseLocalEvent(mindId, ref ev);
foreach (var role in ev.Roles)
foreach (var role in _roles.MindGetAllRoleInfo(mindId))
{
if (string.IsNullOrWhiteSpace(role.PlayTimeTrackerId))
continue;

View File

@@ -10,3 +10,5 @@ public sealed partial class CommandStaffComponent : Component
{
}
//TODO this should probably be on a mind role, not the mob

View File

@@ -4,9 +4,9 @@ using Content.Shared.Roles;
namespace Content.Server.Roles;
/// <summary>
/// Role used to keep track of space dragons for antag purposes.
/// Added to mind role entities to tag that they are a space dragon.
/// </summary>
[RegisterComponent, Access(typeof(DragonSystem)), ExclusiveAntagonist]
public sealed partial class DragonRoleComponent : AntagonistRoleComponent
[RegisterComponent, Access(typeof(DragonSystem))]
public sealed partial class DragonRoleComponent : BaseMindRoleComponent
{
}

View File

@@ -2,8 +2,11 @@ using Content.Shared.Roles;
namespace Content.Server.Roles;
[RegisterComponent, ExclusiveAntagonist]
public sealed partial class InitialInfectedRoleComponent : AntagonistRoleComponent
/// <summary>
/// Added to mind role entities to tag that they are an initial infected.
/// </summary>
[RegisterComponent]
public sealed partial class InitialInfectedRoleComponent : BaseMindRoleComponent
{
}

View File

@@ -30,7 +30,7 @@ public sealed class JobSystem : SharedJobSystem
if (!_mind.TryGetSession(mindId, out var session))
return;
if (!MindTryGetJob(mindId, out _, out var prototype))
if (!MindTryGetJob(mindId, out var prototype))
return;
_chat.DispatchServerMessage(session, Loc.GetString("job-greet-introduce-job-name",
@@ -47,6 +47,6 @@ public sealed class JobSystem : SharedJobSystem
if (MindHasJobWithId(mindId, jobPrototypeId))
return;
_roles.MindAddRole(mindId, new JobComponent { Prototype = jobPrototypeId });
_roles.MindAddJobRole(mindId, null, false, jobPrototypeId);
}
}

View File

@@ -2,7 +2,10 @@ using Content.Shared.Roles;
namespace Content.Server.Roles;
[RegisterComponent, ExclusiveAntagonist]
public sealed partial class NinjaRoleComponent : AntagonistRoleComponent
/// <summary>
/// Added to mind role entities to tag that they are a space ninja.
/// </summary>
[RegisterComponent]
public sealed partial class NinjaRoleComponent : BaseMindRoleComponent
{
}

View File

@@ -3,9 +3,9 @@ using Content.Shared.Roles;
namespace Content.Server.Roles;
/// <summary>
/// Added to mind entities to tag that they are a nuke operative.
/// Added to mind role entities to tag that they are a nuke operative.
/// </summary>
[RegisterComponent, ExclusiveAntagonist]
public sealed partial class NukeopsRoleComponent : AntagonistRoleComponent
[RegisterComponent]
public sealed partial class NukeopsRoleComponent : BaseMindRoleComponent
{
}

View File

@@ -45,7 +45,7 @@ namespace Content.Server.Roles
var roles = _entityManager.System<SharedRoleSystem>();
var jobs = _entityManager.System<SharedJobSystem>();
if (jobs.MindHasJobWithId(mind, args[1]))
roles.MindRemoveRole<JobComponent>(mind.Value);
roles.MindTryRemoveRole<JobRoleComponent>(mind.Value);
}
}
}

View File

@@ -3,10 +3,10 @@ using Content.Shared.Roles;
namespace Content.Server.Roles;
/// <summary>
/// Added to mind entities to tag that they are a Revolutionary.
/// Added to mind role entities to tag that they are a Revolutionary.
/// </summary>
[RegisterComponent, ExclusiveAntagonist]
public sealed partial class RevolutionaryRoleComponent : AntagonistRoleComponent
[RegisterComponent]
public sealed partial class RevolutionaryRoleComponent : BaseMindRoleComponent
{
/// <summary>
/// For headrevs, how many people you have converted.

View File

@@ -1,32 +1,40 @@
using Content.Shared.Mind;
using Content.Shared.Roles;
namespace Content.Server.Roles;
public sealed class RoleSystem : SharedRoleSystem
{
public override void Initialize()
{
// TODO make roles entities
base.Initialize();
SubscribeAntagEvents<DragonRoleComponent>();
SubscribeAntagEvents<InitialInfectedRoleComponent>();
SubscribeAntagEvents<NinjaRoleComponent>();
SubscribeAntagEvents<NukeopsRoleComponent>();
SubscribeAntagEvents<RevolutionaryRoleComponent>();
SubscribeAntagEvents<SubvertedSiliconRoleComponent>();
SubscribeAntagEvents<TraitorRoleComponent>();
SubscribeAntagEvents<ZombieRoleComponent>();
SubscribeAntagEvents<ThiefRoleComponent>();
}
public string? MindGetBriefing(EntityUid? mindId)
{
if (mindId == null)
{
Log.Error($"MingGetBriefing failed for mind {mindId}");
return null;
}
TryComp<MindComponent>(mindId.Value, out var mindComp);
if (mindComp is null)
{
Log.Error($"MingGetBriefing failed for mind {mindId}");
return null;
}
var ev = new GetBriefingEvent();
RaiseLocalEvent(mindId.Value, ref ev);
// This is on the event because while this Entity<T> is also present on every Mind Role Entity's MindRoleComp
// getting to there from a GetBriefing event subscription can be somewhat boilerplate
// and this needs to be looked up for the event anyway so why calculate it again later
ev.Mind = (mindId.Value, mindComp);
// Briefing is no longer raised on the mind entity itself
// because all the components that briefings subscribe to should be on Mind Role Entities
foreach(var role in mindComp.MindRoles)
{
RaiseLocalEvent(role, ref ev);
}
return ev.Briefing;
}
}
@@ -38,8 +46,16 @@ public sealed class RoleSystem : SharedRoleSystem
[ByRefEvent]
public sealed class GetBriefingEvent
{
/// <summary>
/// The text that will be shown on the Character Screen
/// </summary>
public string? Briefing;
/// <summary>
/// The Mind to whose Mind Role Entities the briefing is sent to
/// </summary>
public Entity<MindComponent> Mind;
public GetBriefingEvent(string? briefing = null)
{
Briefing = briefing;

View File

@@ -2,7 +2,10 @@
namespace Content.Server.Roles;
/// <summary>
/// Added to mind role entities to tag that they are a hacked borg.
/// </summary>
[RegisterComponent]
public sealed partial class SubvertedSiliconRoleComponent : AntagonistRoleComponent
public sealed partial class SubvertedSiliconRoleComponent : BaseMindRoleComponent
{
}

View File

@@ -2,7 +2,10 @@ using Content.Shared.Roles;
namespace Content.Server.Roles;
/// <summary>
/// Added to mind role entities to tag that they are a thief.
/// </summary>
[RegisterComponent]
public sealed partial class ThiefRoleComponent : AntagonistRoleComponent
public sealed partial class ThiefRoleComponent : BaseMindRoleComponent
{
}

View File

@@ -2,7 +2,10 @@ using Content.Shared.Roles;
namespace Content.Server.Roles;
[RegisterComponent, ExclusiveAntagonist]
public sealed partial class TraitorRoleComponent : AntagonistRoleComponent
/// <summary>
/// Added to mind role entities to tag that they are a syndicate traitor.
/// </summary>
[RegisterComponent]
public sealed partial class TraitorRoleComponent : BaseMindRoleComponent
{
}

View File

@@ -2,7 +2,10 @@ using Content.Shared.Roles;
namespace Content.Server.Roles;
[RegisterComponent, ExclusiveAntagonist]
public sealed partial class ZombieRoleComponent : AntagonistRoleComponent
/// <summary>
/// Added to mind role entities to tag that they are a zombie.
/// </summary>
[RegisterComponent]
public sealed partial class ZombieRoleComponent : BaseMindRoleComponent
{
}

View File

@@ -124,6 +124,9 @@ public sealed partial class ShuttleConsoleSystem
if (!TryComp(shuttleUid, out ShuttleComponent? shuttleComp))
return;
if (shuttleComp.Enabled == false)
return;
// Check shuttle can even FTL
if (!_shuttle.CanFTL(shuttleUid.Value, out var reason))
{

View File

@@ -244,18 +244,28 @@ public sealed partial class ShuttleSystem
/// </summary>
public bool CanFTL(EntityUid shuttleUid, [NotNullWhen(false)] out string? reason)
{
// Currently in FTL already
if (HasComp<FTLComponent>(shuttleUid))
{
reason = Loc.GetString("shuttle-console-in-ftl");
return false;
}
if (FTLMassLimit > 0 &&
TryComp(shuttleUid, out PhysicsComponent? shuttlePhysics) &&
shuttlePhysics.Mass > FTLMassLimit)
if (TryComp<PhysicsComponent>(shuttleUid, out var shuttlePhysics))
{
reason = Loc.GetString("shuttle-console-mass");
return false;
// Static physics type is set when station anchor is enabled
if (shuttlePhysics.BodyType == BodyType.Static)
{
reason = Loc.GetString("shuttle-console-static");
return false;
}
// Too large to FTL
if (FTLMassLimit > 0 && shuttlePhysics.Mass > FTLMassLimit)
{
reason = Loc.GetString("shuttle-console-mass");
return false;
}
}
if (HasComp<PreventPilotComponent>(shuttleUid))

View File

@@ -28,12 +28,12 @@ namespace Content.Server.Silicons.Laws;
public sealed class SiliconLawSystem : SharedSiliconLawSystem
{
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly SharedStunSystem _stunSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
/// <inheritdoc/>
public override void Initialize()
@@ -178,10 +178,8 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
if (component.AntagonistRole == null || !_mind.TryGetMind(uid, out var mindId, out _))
return;
if (_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId))
return;
_roles.MindAddRole(mindId, new SubvertedSiliconRoleComponent { PrototypeId = component.AntagonistRole });
if (!_roles.MindHasRole<SubvertedSiliconRoleComponent>(mindId))
_roles.MindAddRole(mindId, "MindRoleSubvertedSilicon");
}
public SiliconLawset GetLaws(EntityUid uid, SiliconLawBoundComponent? component = null)

View File

@@ -12,10 +12,10 @@ namespace Content.Server.Spawners.EntitySystems;
public sealed class ContainerSpawnPointSystem : EntitySystem
{
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly StationSpawningSystem _stationSpawning = default!;
@@ -32,7 +32,7 @@ public sealed class ContainerSpawnPointSystem : EntitySystem
// If it's just a spawn pref check if it's for cryo (silly).
if (args.HumanoidCharacterProfile?.SpawnPriority != SpawnPriorityPreference.Cryosleep &&
(!_proto.TryIndex(args.Job?.Prototype, out var jobProto) || jobProto.JobEntity == null))
(!_proto.TryIndex(args.Job, out var jobProto) || jobProto.JobEntity == null))
{
return;
}
@@ -49,7 +49,7 @@ public sealed class ContainerSpawnPointSystem : EntitySystem
if (spawnPoint.SpawnType == SpawnPointType.Unset)
{
// make sure we also check the job here for various reasons.
if (spawnPoint.Job == null || spawnPoint.Job == args.Job?.Prototype)
if (spawnPoint.Job == null || spawnPoint.Job == args.Job)
possibleContainers.Add((uid, spawnPoint, container, xform));
continue;
}
@@ -61,7 +61,7 @@ public sealed class ContainerSpawnPointSystem : EntitySystem
if (_gameTicker.RunLevel != GameRunLevel.InRound &&
spawnPoint.SpawnType == SpawnPointType.Job &&
(args.Job == null || spawnPoint.Job == args.Job.Prototype))
(args.Job == null || spawnPoint.Job == args.Job))
{
possibleContainers.Add((uid, spawnPoint, container, xform));
}

View File

@@ -39,7 +39,7 @@ public sealed class SpawnPointSystem : EntitySystem
if (_gameTicker.RunLevel != GameRunLevel.InRound &&
spawnPoint.SpawnType == SpawnPointType.Job &&
(args.Job == null || spawnPoint.Job == args.Job.Prototype))
(args.Job == null || spawnPoint.Job == args.Job))
{
possiblePositions.Add(xform.Coordinates);
}

View File

@@ -18,13 +18,10 @@ using Content.Shared.Humanoid.Prototypes;
using Content.Shared.PDA;
using Content.Shared.Preferences;
using Content.Shared.Preferences.Loadouts;
using Content.Shared.Preferences.Loadouts.Effects;
using Content.Shared.Random;
using Content.Shared.Random.Helpers;
using Content.Shared.Roles;
using Content.Shared.Roles.Jobs;
using Content.Shared.Station;
using Content.Shared.StatusIcon;
using JetBrains.Annotations;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
@@ -42,19 +39,19 @@ namespace Content.Server.Station.Systems;
[PublicAPI]
public sealed class StationSpawningSystem : SharedStationSpawningSystem
{
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedAccessSystem _accessSystem = default!;
[Dependency] private readonly ActorSystem _actors = default!;
[Dependency] private readonly ArrivalsSystem _arrivalsSystem = default!;
[Dependency] private readonly CP14ExpeditionSystem _CP14expedition = default!;
[Dependency] private readonly IdCardSystem _cardSystem = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly ContainerSpawnPointSystem _containerSpawnPointSystem = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
[Dependency] private readonly IdCardSystem _cardSystem = default!;
[Dependency] private readonly IdentitySystem _identity = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
[Dependency] private readonly PdaSystem _pdaSystem = default!;
[Dependency] private readonly SharedAccessSystem _accessSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
private bool _randomizeCharacters;
@@ -77,7 +74,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
/// <remarks>
/// This only spawns the character, and does none of the mind-related setup you'd need for it to be playable.
/// </remarks>
public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, JobComponent? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null)
public EntityUid? SpawnPlayerCharacterOnStation(EntityUid? station, ProtoId<JobPrototype>? job, HumanoidCharacterProfile? profile, StationSpawningComponent? stationSpawning = null)
{
if (station != null && !Resolve(station.Value, ref stationSpawning))
throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station));
@@ -105,12 +102,12 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
/// <returns>The spawned entity</returns>
public EntityUid SpawnPlayerMob(
EntityCoordinates coordinates,
JobComponent? job,
ProtoId<JobPrototype>? job,
HumanoidCharacterProfile? profile,
EntityUid? station,
EntityUid? entity = null)
{
_prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out var prototype);
_prototypeManager.TryIndex(job ?? string.Empty, out var prototype);
RoleLoadout? loadout = null;
// Need to get the loadout up-front to handle names if we use an entity spawn override.
@@ -204,9 +201,9 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
return entity.Value;
}
private void DoJobSpecials(JobComponent? job, EntityUid entity)
private void DoJobSpecials(ProtoId<JobPrototype>? job, EntityUid entity)
{
if (!_prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out JobPrototype? prototype))
if (!_prototypeManager.TryIndex(job ?? string.Empty, out JobPrototype? prototype))
return;
foreach (var jobSpecial in prototype.Special)
@@ -273,7 +270,7 @@ public sealed class PlayerSpawningEvent : EntityEventArgs
/// <summary>
/// The job to use, if any.
/// </summary>
public readonly JobComponent? Job;
public readonly ProtoId<JobPrototype>? Job;
/// <summary>
/// The profile to use, if any.
/// </summary>
@@ -283,7 +280,7 @@ public sealed class PlayerSpawningEvent : EntityEventArgs
/// </summary>
public readonly EntityUid? Station;
public PlayerSpawningEvent(JobComponent? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station)
public PlayerSpawningEvent(ProtoId<JobPrototype>? job, HumanoidCharacterProfile? humanoidCharacterProfile, EntityUid? station)
{
Job = job;
HumanoidCharacterProfile = humanoidCharacterProfile;

View File

@@ -33,16 +33,16 @@ public sealed partial class BuyerAntagCondition : ListingCondition
return true;
var roleSystem = ent.System<SharedRoleSystem>();
var roles = roleSystem.MindGetAllRoles(mindId);
var roles = roleSystem.MindGetAllRoleInfo(mindId);
if (Blacklist != null)
{
foreach (var role in roles)
{
if (role.Component is not AntagonistRoleComponent blacklistantag)
if (!role.Antagonist || string.IsNullOrEmpty(role.Prototype))
continue;
if (blacklistantag.PrototypeId != null && Blacklist.Contains(blacklistantag.PrototypeId))
if (Blacklist.Contains(role.Prototype))
return false;
}
}
@@ -52,10 +52,11 @@ public sealed partial class BuyerAntagCondition : ListingCondition
var found = false;
foreach (var role in roles)
{
if (role.Component is not AntagonistRoleComponent antag)
if (!role.Antagonist || string.IsNullOrEmpty(role.Prototype))
continue;
if (antag.PrototypeId != null && Whitelist.Contains(antag.PrototypeId))
if (Whitelist.Contains(role.Prototype))
found = true;
}
if (!found)

View File

@@ -37,13 +37,13 @@ public sealed partial class BuyerDepartmentCondition : ListingCondition
return true;
var jobs = ent.System<SharedJobSystem>();
jobs.MindTryGetJob(mindId, out var job, out _);
jobs.MindTryGetJob(mindId, out var job);
if (Blacklist != null && job?.Prototype != null)
if (Blacklist != null && job != null)
{
foreach (var department in prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
{
if (department.Roles.Contains(job.Prototype.Value) && Blacklist.Contains(department.ID))
if (department.Roles.Contains(job.ID) && Blacklist.Contains(department.ID))
return false;
}
}
@@ -52,11 +52,11 @@ public sealed partial class BuyerDepartmentCondition : ListingCondition
{
var found = false;
if (job?.Prototype != null)
if (job != null)
{
foreach (var department in prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
{
if (department.Roles.Contains(job.Prototype.Value) && Whitelist.Contains(department.ID))
if (department.Roles.Contains(job.ID) && Whitelist.Contains(department.ID))
{
found = true;
break;

View File

@@ -34,17 +34,17 @@ public sealed partial class BuyerJobCondition : ListingCondition
return true;
var jobs = ent.System<SharedJobSystem>();
jobs.MindTryGetJob(mindId, out var job, out _);
jobs.MindTryGetJob(mindId, out var job);
if (Blacklist != null)
{
if (job?.Prototype != null && Blacklist.Contains(job.Prototype))
if (job is not null && Blacklist.Contains(job.ID))
return false;
}
if (Whitelist != null)
{
if (job?.Prototype == null || !Whitelist.Contains(job.Prototype))
if (job == null || !Whitelist.Contains(job.ID))
return false;
}

View File

@@ -6,6 +6,7 @@ using Content.Shared.Examine;
using Content.Shared.Foldable;
using Content.Shared.Popups;
using Content.Shared.Verbs;
using Content.Shared.Roles;
using Robust.Shared.Audio.Systems;
namespace Content.Server.Thief.Systems;
@@ -18,7 +19,7 @@ public sealed class ThiefBeaconSystem : EntitySystem
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
public override void Initialize()
{
base.Initialize();
@@ -37,7 +38,7 @@ public sealed class ThiefBeaconSystem : EntitySystem
return;
var mind = _mind.GetMind(args.User);
if (!HasComp<ThiefRoleComponent>(mind))
if (mind == null || !_roles.MindHasRole<ThiefRoleComponent>(mind.Value))
return;
var user = args.User;

View File

@@ -11,7 +11,6 @@ using Content.Server.Mind.Commands;
using Content.Server.NPC;
using Content.Server.NPC.HTN;
using Content.Server.NPC.Systems;
using Content.Server.Roles;
using Content.Server.Speech.Components;
using Content.Server.Temperature.Components;
using Content.Shared.CombatMode;
@@ -47,18 +46,18 @@ namespace Content.Server.Zombies;
/// </remarks>
public sealed partial class ZombieSystem
{
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly ServerInventorySystem _inventory = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly IChatManager _chatMan = default!;
[Dependency] private readonly SharedCombatModeSystem _combat = default!;
[Dependency] private readonly NpcFactionSystem _faction = default!;
[Dependency] private readonly NPCSystem _npc = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoidAppearance = default!;
[Dependency] private readonly IdentitySystem _identity = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
[Dependency] private readonly SharedCombatModeSystem _combat = default!;
[Dependency] private readonly IChatManager _chatMan = default!;
[Dependency] private readonly ServerInventorySystem _inventory = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
[Dependency] private readonly NPCSystem _npc = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
/// <summary>
/// Handles an entity turning into a zombie when they die or go into crit
@@ -234,7 +233,7 @@ public sealed partial class ZombieSystem
if (hasMind && _mind.TryGetSession(mindId, out var session))
{
//Zombie role for player manifest
_roles.MindAddRole(mindId, new ZombieRoleComponent { PrototypeId = zombiecomp.ZombieRoleId });
_roles.MindAddRole(mindId, "MindRoleZombie", mind: null, silent: true);
//Greeting message for new bebe zombers
_chatMan.DispatchServerMessage(session, Loc.GetString("zombie-infection-greeting"));