Merge pull request #490 from crystallpunk-14/ed-15-10-2024-upstream
Ed 15 10 2024 upstream
This commit is contained in:
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
@@ -5,8 +5,8 @@ concurrency:
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 10 * * *'
|
||||
# schedule:
|
||||
# - cron: '0 10 * * *'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Buckle;
|
||||
using Content.Client.Gravity;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
|
||||
namespace Content.Client.Movement.Systems;
|
||||
|
||||
public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animation = default!;
|
||||
[Dependency] private readonly GravitySystem _gravity = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
[Dependency] private readonly BuckleSystem _buckle = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeAllEvent<StartedWaddlingEvent>(OnStartWaddling);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, AnimationCompletedEvent>(OnAnimationCompleted);
|
||||
SubscribeAllEvent<StoppedWaddlingEvent>(OnStopWaddling);
|
||||
}
|
||||
|
||||
private void OnStartWaddling(StartedWaddlingEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (TryComp<WaddleAnimationComponent>(GetEntity(msg.Entity), out var comp))
|
||||
StartWaddling((GetEntity(msg.Entity), comp));
|
||||
}
|
||||
|
||||
private void OnStopWaddling(StoppedWaddlingEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (TryComp<WaddleAnimationComponent>(GetEntity(msg.Entity), out var comp))
|
||||
StopWaddling((GetEntity(msg.Entity), comp));
|
||||
}
|
||||
|
||||
private void StartWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
if (_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName))
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
return;
|
||||
|
||||
if (_gravity.IsWeightless(entity.Owner))
|
||||
return;
|
||||
|
||||
if (!_actionBlocker.CanMove(entity.Owner, mover))
|
||||
return;
|
||||
|
||||
// Do nothing if buckled in
|
||||
if (_buckle.IsBuckled(entity.Owner))
|
||||
return;
|
||||
|
||||
// Do nothing if crit or dead (for obvious reasons)
|
||||
if (_mobState.IsIncapacitated(entity.Owner))
|
||||
return;
|
||||
|
||||
PlayWaddleAnimationUsing(
|
||||
(entity.Owner, entity.Comp),
|
||||
CalculateAnimationLength(entity.Comp, mover),
|
||||
CalculateTumbleIntensity(entity.Comp)
|
||||
);
|
||||
}
|
||||
|
||||
private static float CalculateTumbleIntensity(WaddleAnimationComponent component)
|
||||
{
|
||||
return component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity;
|
||||
}
|
||||
|
||||
private static float CalculateAnimationLength(WaddleAnimationComponent component, InputMoverComponent mover)
|
||||
{
|
||||
return mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength;
|
||||
}
|
||||
|
||||
private void OnAnimationCompleted(Entity<WaddleAnimationComponent> entity, ref AnimationCompletedEvent args)
|
||||
{
|
||||
if (args.Key != entity.Comp.KeyName)
|
||||
return;
|
||||
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
return;
|
||||
|
||||
PlayWaddleAnimationUsing(
|
||||
(entity.Owner, entity.Comp),
|
||||
CalculateAnimationLength(entity.Comp, mover),
|
||||
CalculateTumbleIntensity(entity.Comp)
|
||||
);
|
||||
}
|
||||
|
||||
private void StopWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
if (!_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName))
|
||||
return;
|
||||
|
||||
_animation.Stop(entity.Owner, entity.Comp.KeyName);
|
||||
|
||||
if (!TryComp<SpriteComponent>(entity.Owner, out var sprite))
|
||||
return;
|
||||
|
||||
sprite.Offset = new Vector2();
|
||||
sprite.Rotation = Angle.FromDegrees(0);
|
||||
}
|
||||
|
||||
private void PlayWaddleAnimationUsing(Entity<WaddleAnimationComponent> entity, float len, float tumbleIntensity)
|
||||
{
|
||||
entity.Comp.LastStep = !entity.Comp.LastStep;
|
||||
|
||||
var anim = new Animation()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(len),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty()
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Rotation),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(0), 0),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(tumbleIntensity), len/2),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(0), len/2),
|
||||
}
|
||||
},
|
||||
new AnimationTrackComponentProperty()
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Offset),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), 0),
|
||||
new AnimationTrackProperty.KeyFrame(entity.Comp.HopIntensity, len/2),
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(), len/2),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_animation.Play(entity.Owner, anim, entity.Comp.KeyName);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.GameTicking;
|
||||
@@ -120,8 +121,8 @@ public sealed class NukeOpsTest
|
||||
Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mind));
|
||||
Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True);
|
||||
Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False);
|
||||
var roles = roleSys.MindGetAllRoles(mind);
|
||||
var cmdRoles = roles.Where(x => x.Prototype == "NukeopsCommander" && x.Component is NukeopsRoleComponent);
|
||||
var roles = roleSys.MindGetAllRoleInfo(mind);
|
||||
var cmdRoles = roles.Where(x => x.Prototype == "NukeopsCommander");
|
||||
Assert.That(cmdRoles.Count(), Is.EqualTo(1));
|
||||
|
||||
// The second dummy player should be a medic
|
||||
@@ -131,8 +132,8 @@ public sealed class NukeOpsTest
|
||||
Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(dummyMind));
|
||||
Assert.That(factionSys.IsMember(dummyEnts[1], "Syndicate"), Is.True);
|
||||
Assert.That(factionSys.IsMember(dummyEnts[1], "NanoTrasen"), Is.False);
|
||||
roles = roleSys.MindGetAllRoles(dummyMind);
|
||||
cmdRoles = roles.Where(x => x.Prototype == "NukeopsMedic" && x.Component is NukeopsRoleComponent);
|
||||
roles = roleSys.MindGetAllRoleInfo(dummyMind);
|
||||
cmdRoles = roles.Where(x => x.Prototype == "NukeopsMedic");
|
||||
Assert.That(cmdRoles.Count(), Is.EqualTo(1));
|
||||
|
||||
// The other two players should have just spawned in as normal.
|
||||
@@ -141,13 +142,14 @@ public sealed class NukeOpsTest
|
||||
void CheckDummy(int i)
|
||||
{
|
||||
var ent = dummyEnts[i];
|
||||
var mind = mindSys.GetMind(ent)!.Value;
|
||||
var mindCrew = mindSys.GetMind(ent)!.Value;
|
||||
Assert.That(entMan.HasComponent<NukeOperativeComponent>(ent), Is.False);
|
||||
Assert.That(roleSys.MindIsAntagonist(mind), Is.False);
|
||||
Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mind), Is.False);
|
||||
Assert.That(roleSys.MindIsAntagonist(mindCrew), Is.False);
|
||||
Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mindCrew), Is.False);
|
||||
Assert.That(factionSys.IsMember(ent, "Syndicate"), Is.False);
|
||||
Assert.That(factionSys.IsMember(ent, "NanoTrasen"), Is.True);
|
||||
Assert.That(roleSys.MindGetAllRoles(mind).Any(x => x.Component is NukeopsRoleComponent), Is.False);
|
||||
var nukeroles = new List<string>() { "Nukeops", "NukeopsMedic", "NukeopsCommander" };
|
||||
Assert.That(roleSys.MindGetAllRoleInfo(mindCrew).Any(x => nukeroles.Contains(x.Prototype)), Is.False);
|
||||
}
|
||||
|
||||
// The game rule exists, and all the stations/shuttles/maps are properly initialized
|
||||
@@ -238,7 +240,8 @@ public sealed class NukeOpsTest
|
||||
for (var i = 0; i < nukies.Length - 1; i++)
|
||||
{
|
||||
entMan.DeleteEntity(nukies[i]);
|
||||
Assert.That(roundEndSys.IsRoundEndRequested, Is.False,
|
||||
Assert.That(roundEndSys.IsRoundEndRequested,
|
||||
Is.False,
|
||||
$"The round ended, but {nukies.Length - i - 1} nukies are still alive!");
|
||||
}
|
||||
// Delete the last nukie and make sure the round ends.
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Internals;
|
||||
|
||||
@@ -25,10 +24,7 @@ public sealed class AutoInternalsTests
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var profile = new HumanoidCharacterProfile();
|
||||
var dummy = stationSpawning.SpawnPlayerMob(testMap.GridCoords, new JobComponent()
|
||||
{
|
||||
Prototype = "TestInternalsDummy"
|
||||
}, profile, station: null);
|
||||
var dummy = stationSpawning.SpawnPlayerMob(testMap.GridCoords, "TestInternalsDummy", profile, station: null);
|
||||
|
||||
Assert.That(atmos.HasAtmosphere(testMap.Grid), Is.False, "Test map has atmosphere - test needs adjustment!");
|
||||
Assert.That(internals.AreInternalsWorking(dummy), "Internals did not automatically connect!");
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Server.Ghost.Roles;
|
||||
using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Mind.Commands;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Roles;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
@@ -18,7 +16,6 @@ using Robust.Server.Console;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -287,27 +284,27 @@ public sealed partial class MindTests
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobRoleComponent>(mindId), Is.False);
|
||||
});
|
||||
|
||||
var traitorRole = new TraitorRoleComponent();
|
||||
var traitorRole = "MindRoleTraitor";
|
||||
|
||||
roleSystem.MindAddRole(mindId, traitorRole);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId));
|
||||
Assert.That(roleSystem.MindHasRole<JobComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobRoleComponent>(mindId), Is.False);
|
||||
});
|
||||
|
||||
var jobRole = new JobComponent();
|
||||
var jobRole = "";
|
||||
|
||||
roleSystem.MindAddRole(mindId, jobRole);
|
||||
roleSystem.MindAddJobRole(mindId, jobPrototype:jobRole);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId));
|
||||
Assert.That(roleSystem.MindHasRole<JobComponent>(mindId));
|
||||
Assert.That(roleSystem.MindHasRole<JobRoleComponent>(mindId));
|
||||
});
|
||||
|
||||
roleSystem.MindRemoveRole<TraitorRoleComponent>(mindId);
|
||||
@@ -315,15 +312,15 @@ public sealed partial class MindTests
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobComponent>(mindId));
|
||||
Assert.That(roleSystem.MindHasRole<JobRoleComponent>(mindId));
|
||||
});
|
||||
|
||||
roleSystem.MindRemoveRole<JobComponent>(mindId);
|
||||
roleSystem.MindRemoveRole<JobRoleComponent>(mindId);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobComponent>(mindId), Is.False);
|
||||
Assert.That(roleSystem.MindHasRole<JobRoleComponent>(mindId), Is.False);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using Content.Server.Station.Systems;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
@@ -68,10 +67,7 @@ public sealed class LoadoutTests
|
||||
|
||||
profile.SetLoadout(new RoleLoadout("LoadoutTester"));
|
||||
|
||||
var tester = stationSystem.SpawnPlayerMob(testMap.GridCoords, job: new JobComponent()
|
||||
{
|
||||
Prototype = "LoadoutTester"
|
||||
}, profile, station: null);
|
||||
var tester = stationSystem.SpawnPlayerMob(testMap.GridCoords, job: "LoadoutTester", profile, station: null);
|
||||
|
||||
var slotQuery = inventorySystem.GetSlotEnumerator(tester);
|
||||
var checkedCount = 0;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
|
||||
PrivilegedIdIsAuthorized(uid, component),
|
||||
true,
|
||||
targetIdComponent.FullName,
|
||||
targetIdComponent.JobTitle,
|
||||
targetIdComponent.LocalizedJobTitle,
|
||||
targetAccessComponent.Tags.ToList(),
|
||||
possibleAccess,
|
||||
jobProto,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
using Content.Shared.Movement.Systems;
|
||||
|
||||
namespace Content.Server.Movement.Systems;
|
||||
|
||||
public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -10,3 +10,5 @@ public sealed partial class CommandStaffComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//TODO this should probably be on a mind role, not the mob
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -130,7 +130,7 @@ public sealed class CP14ExpeditionSystem : EntitySystem
|
||||
var possiblePositions = new List<EntityCoordinates>();
|
||||
while (points.MoveNext(out var uid, out var spawnPoint, out var xform))
|
||||
{
|
||||
if (ev.Job != null && spawnPoint.Job != ev.Job.Prototype)
|
||||
if (ev.Job != null && spawnPoint.Job != ev.Job.Value)
|
||||
continue;
|
||||
|
||||
if (xform.GridUid != gridUid)
|
||||
|
||||
@@ -20,7 +20,12 @@ public sealed partial class IdCardComponent : Component
|
||||
[DataField]
|
||||
[AutoNetworkedField]
|
||||
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWrite)]
|
||||
public string? JobTitle;
|
||||
public LocId? JobTitle;
|
||||
|
||||
private string? _jobTitle;
|
||||
|
||||
[Access(typeof(SharedIdCardSystem), typeof(SharedPdaSystem), typeof(SharedAgentIdCardSystem), Other = AccessPermissions.ReadWriteExecute)]
|
||||
public string? LocalizedJobTitle { set => _jobTitle = value; get => _jobTitle ?? Loc.GetString(JobTitle ?? string.Empty); }
|
||||
|
||||
/// <summary>
|
||||
/// The state of the job icon rsi.
|
||||
|
||||
@@ -67,7 +67,7 @@ public sealed class IdExaminableSystem : EntitySystem
|
||||
|
||||
private string GetNameAndJob(IdCardComponent id)
|
||||
{
|
||||
var jobSuffix = string.IsNullOrWhiteSpace(id.JobTitle) ? string.Empty : $" ({id.JobTitle})";
|
||||
var jobSuffix = string.IsNullOrWhiteSpace(id.LocalizedJobTitle) ? string.Empty : $" ({id.LocalizedJobTitle})";
|
||||
|
||||
var val = string.IsNullOrWhiteSpace(id.FullName)
|
||||
? Loc.GetString(id.NameLocId,
|
||||
|
||||
@@ -116,6 +116,7 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If provided with a player's EntityUid to the player parameter, adds the change to the admin logs.
|
||||
/// Actually works with the LocalizedJobTitle DataField and not with JobTitle.
|
||||
/// </remarks>
|
||||
public bool TryChangeJobTitle(EntityUid uid, string? jobTitle, IdCardComponent? id = null, EntityUid? player = null)
|
||||
{
|
||||
@@ -134,9 +135,9 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
jobTitle = null;
|
||||
}
|
||||
|
||||
if (id.JobTitle == jobTitle)
|
||||
if (id.LocalizedJobTitle == jobTitle)
|
||||
return true;
|
||||
id.JobTitle = jobTitle;
|
||||
id.LocalizedJobTitle = jobTitle;
|
||||
Dirty(uid, id);
|
||||
UpdateEntityName(uid, id);
|
||||
|
||||
@@ -238,7 +239,7 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
if (!Resolve(uid, ref id))
|
||||
return;
|
||||
|
||||
var jobSuffix = string.IsNullOrWhiteSpace(id.JobTitle) ? string.Empty : $" ({id.JobTitle})";
|
||||
var jobSuffix = string.IsNullOrWhiteSpace(id.LocalizedJobTitle) ? string.Empty : $" ({id.LocalizedJobTitle})";
|
||||
|
||||
var val = string.IsNullOrWhiteSpace(id.FullName)
|
||||
? Loc.GetString(id.NameLocId,
|
||||
@@ -251,7 +252,7 @@ public abstract class SharedIdCardSystem : EntitySystem
|
||||
|
||||
private static string ExtractFullTitle(IdCardComponent idCardComponent)
|
||||
{
|
||||
return $"{idCardComponent.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(idCardComponent.JobTitle ?? string.Empty)})"
|
||||
return $"{idCardComponent.FullName} ({CultureInfo.CurrentCulture.TextInfo.ToTitleCase(idCardComponent.LocalizedJobTitle ?? string.Empty)})"
|
||||
.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.ChangeNameInContainer;
|
||||
|
||||
/// <summary>
|
||||
/// An entity with this component will get its name and verb chaned to the container it's inside of. E.g, if your a
|
||||
/// pAI that has this component and are inside a lizard plushie, your name when talking will be "lizard plushie".
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, Access(typeof(ChangeNameInContainerSystem))]
|
||||
public sealed partial class ChangeVoiceInContainerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// A whitelist of containers that will change the name.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Whitelist;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Chat;
|
||||
using Robust.Shared.Containers;
|
||||
using Content.Shared.Whitelist;
|
||||
using Content.Shared.Speech;
|
||||
|
||||
namespace Content.Shared.ChangeNameInContainer;
|
||||
|
||||
public sealed partial class ChangeNameInContainerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ChangeVoiceInContainerComponent, TransformSpeakerNameEvent>(OnTransformSpeakerName);
|
||||
}
|
||||
|
||||
private void OnTransformSpeakerName(Entity<ChangeVoiceInContainerComponent> ent, ref TransformSpeakerNameEvent args)
|
||||
{
|
||||
if (!_container.TryGetContainingContainer((ent, null, null), out var container)
|
||||
|| _whitelist.IsWhitelistFail(ent.Comp.Whitelist, container.Owner))
|
||||
return;
|
||||
|
||||
args.VoiceName = Name(container.Owner);
|
||||
if (TryComp<SpeechComponent>(container.Owner, out var speechComp))
|
||||
args.SpeechVerb = speechComp.SpeechVerb;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Clothing.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Defines something as causing waddling when worn.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class WaddleWhenWornComponent : Component
|
||||
{
|
||||
///<summary>
|
||||
/// How high should they hop during the waddle? Higher hop = more energy.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public Vector2 HopIntensity = new(0, 0.25f);
|
||||
|
||||
/// <summary>
|
||||
/// How far should they rock backward and forward during the waddle?
|
||||
/// Each step will alternate between this being a positive and negative rotation. More rock = more scary.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float TumbleIntensity = 20.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How long should a complete step take? Less time = more chaos.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float AnimationLength = 0.66f;
|
||||
|
||||
/// <summary>
|
||||
/// How much shorter should the animation be when running?
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float RunAnimationLengthMultiplier = 0.568f;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Inventory.Events;
|
||||
|
||||
namespace Content.Shared.Clothing.EntitySystems;
|
||||
|
||||
public sealed class WaddleClothingSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<WaddleWhenWornComponent, ClothingGotEquippedEvent>(OnGotEquipped);
|
||||
SubscribeLocalEvent<WaddleWhenWornComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
|
||||
}
|
||||
|
||||
private void OnGotEquipped(EntityUid entity, WaddleWhenWornComponent comp, ClothingGotEquippedEvent args)
|
||||
{
|
||||
var waddleAnimComp = EnsureComp<WaddleAnimationComponent>(args.Wearer);
|
||||
|
||||
waddleAnimComp.AnimationLength = comp.AnimationLength;
|
||||
waddleAnimComp.HopIntensity = comp.HopIntensity;
|
||||
waddleAnimComp.RunAnimationLengthMultiplier = comp.RunAnimationLengthMultiplier;
|
||||
waddleAnimComp.TumbleIntensity = comp.TumbleIntensity;
|
||||
}
|
||||
|
||||
private void OnGotUnequipped(EntityUid entity, WaddleWhenWornComponent comp, ClothingGotUnequippedEvent args)
|
||||
{
|
||||
RemComp<WaddleAnimationComponent>(args.Wearer);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ public sealed partial class MindContainerComponent : Component
|
||||
/// The mind controlling this mob. Can be null.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
[Access(typeof(SharedMindSystem), Other = AccessPermissions.ReadWriteExecute)] // FIXME Friends
|
||||
public EntityUid? Mind { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -35,7 +34,6 @@ public sealed partial class MindContainerComponent : Component
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("ghostOnShutdown")]
|
||||
[Access(typeof(SharedMindSystem), Other = AccessPermissions.ReadWriteExecute)] // FIXME Friends
|
||||
public bool GhostOnShutdown { get; set; } = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Robust.Shared.GameStates;
|
||||
@@ -87,17 +86,21 @@ public sealed partial class MindComponent : Component
|
||||
/// <summary>
|
||||
/// Prevents user from ghosting out
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("preventGhosting")]
|
||||
[DataField]
|
||||
public bool PreventGhosting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Prevents user from suiciding
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("preventSuicide")]
|
||||
[DataField]
|
||||
public bool PreventSuicide { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Mind Role Entities belonging to this Mind
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<EntityUid> MindRoles = new List<EntityUid>();
|
||||
|
||||
/// <summary>
|
||||
/// The session of the player owning this mind.
|
||||
/// Can be null, in which case the player is currently not logged in.
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Movement.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Declares that an entity has started to waddle like a duck/clown.
|
||||
/// </summary>
|
||||
/// <param name="entity">The newly be-waddled.</param>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class StartedWaddlingEvent(NetEntity entity) : EntityEventArgs
|
||||
{
|
||||
public NetEntity Entity = entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declares that an entity has stopped waddling like a duck/clown.
|
||||
/// </summary>
|
||||
/// <param name="entity">The former waddle-er.</param>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class StoppedWaddlingEvent(NetEntity entity) : EntityEventArgs
|
||||
{
|
||||
public NetEntity Entity = entity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines something as having a waddle animation when it moves.
|
||||
/// </summary>
|
||||
[RegisterComponent, AutoGenerateComponentState]
|
||||
public sealed partial class WaddleAnimationComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// What's the name of this animation? Make sure it's unique so it can play along side other animations.
|
||||
/// This prevents someone accidentally causing two identical waddling effects to play on someone at the same time.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string KeyName = "Waddle";
|
||||
|
||||
///<summary>
|
||||
/// How high should they hop during the waddle? Higher hop = more energy.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public Vector2 HopIntensity = new(0, 0.25f);
|
||||
|
||||
/// <summary>
|
||||
/// How far should they rock backward and forward during the waddle?
|
||||
/// Each step will alternate between this being a positive and negative rotation. More rock = more scary.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float TumbleIntensity = 20.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How long should a complete step take? Less time = more chaos.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float AnimationLength = 0.66f;
|
||||
|
||||
/// <summary>
|
||||
/// How much shorter should the animation be when running?
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float RunAnimationLengthMultiplier = 0.568f;
|
||||
|
||||
/// <summary>
|
||||
/// Stores which step we made last, so if someone cancels out of the animation mid-step then restarts it looks more natural.
|
||||
/// </summary>
|
||||
public bool LastStep;
|
||||
|
||||
/// <summary>
|
||||
/// Stores if we're currently waddling so we can start/stop as appropriate and can tell other systems our state.
|
||||
/// </summary>
|
||||
[AutoNetworkedField]
|
||||
public bool IsCurrentlyWaddling;
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Standing;
|
||||
using Content.Shared.Stunnable;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Shared.Movement.Systems;
|
||||
|
||||
public abstract class SharedWaddleAnimationSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
// Startup
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, ComponentStartup>(OnComponentStartup);
|
||||
|
||||
// Start moving possibilities
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, MoveInputEvent>(OnMovementInput);
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, StoodEvent>(OnStood);
|
||||
|
||||
// Stop moving possibilities
|
||||
SubscribeLocalEvent((Entity<WaddleAnimationComponent> ent, ref StunnedEvent _) => StopWaddling(ent));
|
||||
SubscribeLocalEvent((Entity<WaddleAnimationComponent> ent, ref DownedEvent _) => StopWaddling(ent));
|
||||
SubscribeLocalEvent((Entity<WaddleAnimationComponent> ent, ref BuckledEvent _) => StopWaddling(ent));
|
||||
SubscribeLocalEvent<WaddleAnimationComponent, GravityChangedEvent>(OnGravityChanged);
|
||||
}
|
||||
|
||||
private void OnGravityChanged(Entity<WaddleAnimationComponent> ent, ref GravityChangedEvent args)
|
||||
{
|
||||
if (!args.HasGravity && ent.Comp.IsCurrentlyWaddling)
|
||||
StopWaddling(ent);
|
||||
}
|
||||
|
||||
private void OnComponentStartup(Entity<WaddleAnimationComponent> entity, ref ComponentStartup args)
|
||||
{
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var moverComponent))
|
||||
return;
|
||||
|
||||
// If the waddler is currently moving, make them start waddling
|
||||
if ((moverComponent.HeldMoveButtons & MoveButtons.AnyDirection) == MoveButtons.AnyDirection)
|
||||
{
|
||||
RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner)));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMovementInput(Entity<WaddleAnimationComponent> entity, ref MoveInputEvent args)
|
||||
{
|
||||
// Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume
|
||||
// they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR.
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.HasDirectionalMovement && entity.Comp.IsCurrentlyWaddling)
|
||||
{
|
||||
StopWaddling(entity);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Only start waddling if we're not currently AND we're actually moving.
|
||||
if (entity.Comp.IsCurrentlyWaddling || !args.HasDirectionalMovement)
|
||||
return;
|
||||
|
||||
entity.Comp.IsCurrentlyWaddling = true;
|
||||
|
||||
RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner)));
|
||||
}
|
||||
|
||||
private void OnStood(Entity<WaddleAnimationComponent> entity, ref StoodEvent args)
|
||||
{
|
||||
// Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume
|
||||
// they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR.
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp<InputMoverComponent>(entity.Owner, out var mover))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((mover.HeldMoveButtons & MoveButtons.AnyDirection) == MoveButtons.None)
|
||||
return;
|
||||
|
||||
if (entity.Comp.IsCurrentlyWaddling)
|
||||
return;
|
||||
|
||||
entity.Comp.IsCurrentlyWaddling = true;
|
||||
|
||||
RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner)));
|
||||
}
|
||||
|
||||
private void StopWaddling(Entity<WaddleAnimationComponent> entity)
|
||||
{
|
||||
entity.Comp.IsCurrentlyWaddling = false;
|
||||
|
||||
RaiseNetworkEvent(new StoppedWaddlingEvent(GetNetEntity(entity.Owner)));
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Shared.Roles;
|
||||
|
||||
public abstract partial class AntagonistRoleComponent : Component
|
||||
{
|
||||
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
|
||||
public string? PrototypeId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark the antagonist role component as being exclusive
|
||||
/// IE by default other antagonists should refuse to select the same entity for a different antag role
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||
[BaseTypeRequired(typeof(AntagonistRoleComponent))]
|
||||
public sealed partial class ExclusiveAntagonistAttribute : Attribute
|
||||
{
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Roles.Jobs;
|
||||
|
||||
/// <summary>
|
||||
/// Added to mind entities to hold the data for the player's current job.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class JobComponent : Component
|
||||
{
|
||||
[DataField(required: true), AutoNetworkedField]
|
||||
public ProtoId<JobPrototype>? Prototype;
|
||||
}
|
||||
12
Content.Shared/Roles/Jobs/JobRoleComponent.cs
Normal file
12
Content.Shared/Roles/Jobs/JobRoleComponent.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Roles.Jobs;
|
||||
|
||||
/// <summary>
|
||||
/// Added to mind role entities to mark them as a job role entity.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class JobRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
|
||||
}
|
||||
@@ -13,8 +13,10 @@ namespace Content.Shared.Roles.Jobs;
|
||||
/// </summary>
|
||||
public abstract class SharedJobSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly SharedPlayerSystem _playerSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
|
||||
private readonly Dictionary<string, string> _inverseTrackerLookup = new();
|
||||
|
||||
public override void Initialize()
|
||||
@@ -100,32 +102,44 @@ public abstract class SharedJobSystem : EntitySystem
|
||||
|
||||
public bool MindHasJobWithId(EntityUid? mindId, string prototypeId)
|
||||
{
|
||||
return CompOrNull<JobComponent>(mindId)?.Prototype == prototypeId;
|
||||
|
||||
MindRoleComponent? comp = null;
|
||||
if (mindId is null)
|
||||
return false;
|
||||
|
||||
_roles.MindHasRole<JobRoleComponent>(mindId.Value, out var role);
|
||||
|
||||
if (role is null)
|
||||
return false;
|
||||
|
||||
comp = role.Value.Comp;
|
||||
|
||||
return (comp.JobPrototype == prototypeId);
|
||||
}
|
||||
|
||||
public bool MindTryGetJob(
|
||||
[NotNullWhen(true)] EntityUid? mindId,
|
||||
[NotNullWhen(true)] out JobComponent? comp,
|
||||
[NotNullWhen(true)] out JobPrototype? prototype)
|
||||
{
|
||||
comp = null;
|
||||
prototype = null;
|
||||
MindTryGetJobId(mindId, out var protoId);
|
||||
|
||||
return TryComp(mindId, out comp) &&
|
||||
comp.Prototype != null &&
|
||||
_prototypes.TryIndex(comp.Prototype, out prototype);
|
||||
return (_prototypes.TryIndex<JobPrototype>(protoId, out prototype) || prototype is not null);
|
||||
}
|
||||
|
||||
public bool MindTryGetJobId([NotNullWhen(true)] EntityUid? mindId, out ProtoId<JobPrototype>? job)
|
||||
public bool MindTryGetJobId(
|
||||
[NotNullWhen(true)] EntityUid? mindId,
|
||||
out ProtoId<JobPrototype>? job)
|
||||
{
|
||||
if (!TryComp(mindId, out JobComponent? comp))
|
||||
{
|
||||
job = null;
|
||||
return false;
|
||||
}
|
||||
job = null;
|
||||
|
||||
job = comp.Prototype;
|
||||
return true;
|
||||
if (mindId is null)
|
||||
return false;
|
||||
|
||||
if (_roles.MindHasRole<JobRoleComponent>(mindId.Value, out var role))
|
||||
job = role.Value.Comp.JobPrototype;
|
||||
|
||||
return (job is not null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -134,7 +148,7 @@ public abstract class SharedJobSystem : EntitySystem
|
||||
/// </summary>
|
||||
public bool MindTryGetJobName([NotNullWhen(true)] EntityUid? mindId, out string name)
|
||||
{
|
||||
if (MindTryGetJob(mindId, out _, out var prototype))
|
||||
if (MindTryGetJob(mindId, out var prototype))
|
||||
{
|
||||
name = prototype.LocalizedName;
|
||||
return true;
|
||||
@@ -161,7 +175,7 @@ public abstract class SharedJobSystem : EntitySystem
|
||||
if (_playerSystem.ContentData(player) is not { Mind: { } mindId })
|
||||
return true;
|
||||
|
||||
if (!MindTryGetJob(mindId, out _, out var prototype))
|
||||
if (!MindTryGetJob(mindId, out var prototype))
|
||||
return true;
|
||||
|
||||
return prototype.CanBeAntag;
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Content.Shared.Roles;
|
||||
/// </summary>
|
||||
/// <param name="Roles">The list of roles on the player.</param>
|
||||
[ByRefEvent]
|
||||
public readonly record struct MindGetAllRolesEvent(List<RoleInfo> Roles);
|
||||
public readonly record struct MindGetAllRoleInfoEvent(List<RoleInfo> Roles);
|
||||
|
||||
/// <summary>
|
||||
/// Returned by <see cref="MindGetAllRolesEvent"/> to give some information about a player's role.
|
||||
@@ -17,4 +17,4 @@ public readonly record struct MindGetAllRolesEvent(List<RoleInfo> Roles);
|
||||
/// <param name="Antagonist">Whether or not this role makes this player an antagonist.</param>
|
||||
/// <param name="PlayTimeTrackerId">The <see cref="PlayTimeTrackerPrototype"/> id associated with the role.</param>
|
||||
/// <param name="Prototype">The prototype ID of the role</param>
|
||||
public readonly record struct RoleInfo(Component Component, string Name, bool Antagonist, string? PlayTimeTrackerId, string Prototype);
|
||||
public readonly record struct RoleInfo(string Name, bool Antagonist, string? PlayTimeTrackerId, string Prototype);
|
||||
48
Content.Shared/Roles/MindRoleComponent.cs
Normal file
48
Content.Shared/Roles/MindRoleComponent.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Content.Shared.Mind;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// This holds data for, and indicates, a Mind Role entity
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class MindRoleComponent : BaseMindRoleComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks this Mind Role as Antagonist
|
||||
/// A single antag Mind Role is enough to make the owner mind count as Antagonist.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Antag { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// True if this mindrole is an exclusive antagonist. Antag setting is not checked if this is True.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool ExclusiveAntag { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// The Mind that this role belongs to
|
||||
/// </summary>
|
||||
public Entity<MindComponent> Mind { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Antagonist prototype of this role
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<AntagPrototype>? AntagPrototype { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Job prototype of this role
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<JobPrototype>? JobPrototype { get; set; }
|
||||
}
|
||||
|
||||
public abstract partial class BaseMindRoleComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,34 +1,31 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Ghost.Roles;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Roles;
|
||||
|
||||
public abstract class SharedRoleSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedMindSystem _minds = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
|
||||
// TODO please lord make role entities
|
||||
private readonly HashSet<Type> _antagTypes = new();
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly SharedGameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
|
||||
private JobRequirementOverridePrototype? _requirementOverride;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
// TODO make roles entities
|
||||
SubscribeLocalEvent<JobComponent, MindGetAllRolesEvent>(OnJobGetAllRoles);
|
||||
Subs.CVar(_cfg, CCVars.GameRoleTimerOverride, SetRequirementOverride, true);
|
||||
}
|
||||
|
||||
@@ -44,124 +41,117 @@ public abstract class SharedRoleSystem : EntitySystem
|
||||
Log.Error($"Unknown JobRequirementOverridePrototype: {value}");
|
||||
}
|
||||
|
||||
private void OnJobGetAllRoles(EntityUid uid, JobComponent component, ref MindGetAllRolesEvent args)
|
||||
{
|
||||
var name = "game-ticker-unknown-role";
|
||||
var prototype = "";
|
||||
string? playTimeTracker = null;
|
||||
if (component.Prototype != null && _prototypes.TryIndex(component.Prototype, out JobPrototype? job))
|
||||
{
|
||||
name = job.Name;
|
||||
prototype = job.ID;
|
||||
playTimeTracker = job.PlayTimeTracker;
|
||||
}
|
||||
|
||||
name = Loc.GetString(name);
|
||||
|
||||
args.Roles.Add(new RoleInfo(component, name, false, playTimeTracker, prototype));
|
||||
}
|
||||
|
||||
protected void SubscribeAntagEvents<T>() where T : AntagonistRoleComponent
|
||||
{
|
||||
SubscribeLocalEvent((EntityUid _, T component, ref MindGetAllRolesEvent args) =>
|
||||
{
|
||||
var name = "game-ticker-unknown-role";
|
||||
var prototype = "";
|
||||
if (component.PrototypeId != null && _prototypes.TryIndex(component.PrototypeId, out AntagPrototype? antag))
|
||||
{
|
||||
name = antag.Name;
|
||||
prototype = antag.ID;
|
||||
}
|
||||
name = Loc.GetString(name);
|
||||
|
||||
args.Roles.Add(new RoleInfo(component, name, true, null, prototype));
|
||||
});
|
||||
|
||||
SubscribeLocalEvent((EntityUid _, T _, ref MindIsAntagonistEvent args) => { args.IsAntagonist = true; args.IsExclusiveAntagonist |= typeof(T).TryGetCustomAttribute<ExclusiveAntagonistAttribute>(out _); });
|
||||
_antagTypes.Add(typeof(T));
|
||||
}
|
||||
|
||||
public void MindAddRoles(EntityUid mindId, ComponentRegistry components, MindComponent? mind = null, bool silent = false)
|
||||
{
|
||||
if (!Resolve(mindId, ref mind))
|
||||
return;
|
||||
|
||||
EntityManager.AddComponents(mindId, components);
|
||||
var antagonist = false;
|
||||
foreach (var compReg in components.Values)
|
||||
{
|
||||
var compType = compReg.Component.GetType();
|
||||
|
||||
var comp = EntityManager.ComponentFactory.GetComponent(compType);
|
||||
if (IsAntagonistRole(comp.GetType()))
|
||||
{
|
||||
antagonist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var mindEv = new MindRoleAddedEvent(silent);
|
||||
RaiseLocalEvent(mindId, ref mindEv);
|
||||
|
||||
var message = new RoleAddedEvent(mindId, mind, antagonist, silent);
|
||||
if (mind.OwnedEntity != null)
|
||||
{
|
||||
RaiseLocalEvent(mind.OwnedEntity.Value, message, true);
|
||||
}
|
||||
|
||||
_adminLogger.Add(LogType.Mind, LogImpact.Low,
|
||||
$"Role components {string.Join(components.Keys.ToString(), ", ")} added to mind of {_minds.MindOwnerLoggingString(mind)}");
|
||||
}
|
||||
|
||||
public void MindAddRole(EntityUid mindId, Component component, MindComponent? mind = null, bool silent = false)
|
||||
{
|
||||
if (!Resolve(mindId, ref mind))
|
||||
return;
|
||||
|
||||
if (HasComp(mindId, component.GetType()))
|
||||
{
|
||||
throw new ArgumentException($"We already have this role: {component}");
|
||||
}
|
||||
|
||||
EntityManager.AddComponent(mindId, component);
|
||||
var antagonist = IsAntagonistRole(component.GetType());
|
||||
|
||||
var mindEv = new MindRoleAddedEvent(silent);
|
||||
RaiseLocalEvent(mindId, ref mindEv);
|
||||
|
||||
var message = new RoleAddedEvent(mindId, mind, antagonist, silent);
|
||||
if (mind.OwnedEntity != null)
|
||||
{
|
||||
RaiseLocalEvent(mind.OwnedEntity.Value, message, true);
|
||||
}
|
||||
|
||||
_adminLogger.Add(LogType.Mind, LogImpact.Low,
|
||||
$"'Role {component}' added to mind of {_minds.MindOwnerLoggingString(mind)}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gives this mind a new role.
|
||||
/// Adds multiple mind roles to a mind
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind to add the role to.</param>
|
||||
/// <param name="component">The role instance to add.</param>
|
||||
/// <typeparam name="T">The role type to add.</typeparam>
|
||||
/// <param name="silent">Whether or not the role should be added silently</param>
|
||||
/// <returns>The instance of the role.</returns>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown if we already have a role with this type.
|
||||
/// </exception>
|
||||
public void MindAddRole<T>(EntityUid mindId, T component, MindComponent? mind = null, bool silent = false) where T : IComponent, new()
|
||||
/// <param name="mindId">The mind entity to add the role to</param>
|
||||
/// <param name="roles">The list of mind roles to add</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>
|
||||
public void MindAddRoles(EntityUid mindId,
|
||||
List<ProtoId<EntityPrototype>>? roles,
|
||||
MindComponent? mind = null,
|
||||
bool silent = false)
|
||||
{
|
||||
if (!Resolve(mindId, ref mind))
|
||||
if (roles is null || roles.Count == 0)
|
||||
return;
|
||||
|
||||
if (HasComp<T>(mindId))
|
||||
foreach (var proto in roles)
|
||||
{
|
||||
throw new ArgumentException($"We already have this role: {typeof(T)}");
|
||||
MindAddRole(mindId, proto, mind, silent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a mind role to a mind
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity to add the role to</param>
|
||||
/// <param name="protoId">The mind role to add</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>
|
||||
public void MindAddRole(EntityUid mindId,
|
||||
ProtoId<EntityPrototype> protoId,
|
||||
MindComponent? mind = null,
|
||||
bool silent = false)
|
||||
{
|
||||
if (protoId == "MindRoleJob")
|
||||
MindAddJobRole(mindId, mind, silent, "");
|
||||
else
|
||||
MindAddRoleDo(mindId, protoId, mind, silent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Job mind role with the specified job prototype
|
||||
/// </summary>
|
||||
/// /// <param name="mindId">The mind entity to add the job role to</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="jobPrototype">The Job prototype for the new role</param>
|
||||
public void MindAddJobRole(EntityUid mindId,
|
||||
MindComponent? mind = null,
|
||||
bool silent = false,
|
||||
string? jobPrototype = null)
|
||||
{
|
||||
// Can't have someone get paid for two jobs now, can we
|
||||
if (MindHasRole<JobRoleComponent>(mindId, out var jobRole)
|
||||
&& jobRole.Value.Comp.JobPrototype != jobPrototype)
|
||||
{
|
||||
Resolve(mindId, ref mind);
|
||||
if (mind is not null)
|
||||
{
|
||||
_adminLogger.Add(LogType.Mind,
|
||||
LogImpact.Low,
|
||||
$"Job Role of {ToPrettyString(mind.OwnedEntity)} changed from '{jobRole.Value.Comp.JobPrototype}' to '{jobPrototype}'");
|
||||
}
|
||||
|
||||
jobRole.Value.Comp.JobPrototype = jobPrototype;
|
||||
}
|
||||
else
|
||||
MindAddRoleDo(mindId, "MindRoleJob", mind, silent, jobPrototype);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a Mind Role
|
||||
/// </summary>
|
||||
private void MindAddRoleDo(EntityUid mindId,
|
||||
ProtoId<EntityPrototype> protoId,
|
||||
MindComponent? mind = null,
|
||||
bool silent = false,
|
||||
string? jobPrototype = null)
|
||||
{
|
||||
if (!Resolve(mindId, ref mind))
|
||||
{
|
||||
Log.Error($"Failed to add role {protoId} to mind {mindId} : Mind does not match provided mind component");
|
||||
return;
|
||||
}
|
||||
|
||||
AddComp(mindId, component);
|
||||
var antagonist = IsAntagonistRole<T>();
|
||||
var antagonist = false;
|
||||
|
||||
if (!_prototypes.TryIndex(protoId, out var protoEnt))
|
||||
{
|
||||
Log.Error($"Failed to add role {protoId} to mind {mindId} : Role prototype does not exist");
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO don't let a prototype being added a second time
|
||||
//If that was somehow to occur, a second mindrole for that comp would be created
|
||||
//Meaning any mind role checks could return wrong results, since they just return the first match they find
|
||||
|
||||
var mindRoleId = Spawn(protoId, MapCoordinates.Nullspace);
|
||||
EnsureComp<MindRoleComponent>(mindRoleId);
|
||||
var mindRoleComp = Comp<MindRoleComponent>(mindRoleId);
|
||||
|
||||
mindRoleComp.Mind = (mindId,mind);
|
||||
if (jobPrototype is not null)
|
||||
{
|
||||
mindRoleComp.JobPrototype = jobPrototype;
|
||||
EnsureComp<JobRoleComponent>(mindRoleId);
|
||||
}
|
||||
|
||||
if (mindRoleComp.Antag || mindRoleComp.ExclusiveAntag)
|
||||
antagonist = true;
|
||||
|
||||
mind.MindRoles.Add(mindRoleId);
|
||||
|
||||
var mindEv = new MindRoleAddedEvent(silent);
|
||||
RaiseLocalEvent(mindId, ref mindEv);
|
||||
@@ -172,94 +162,336 @@ public abstract class SharedRoleSystem : EntitySystem
|
||||
RaiseLocalEvent(mind.OwnedEntity.Value, message, true);
|
||||
}
|
||||
|
||||
_adminLogger.Add(LogType.Mind, LogImpact.Low,
|
||||
$"'Role {typeof(T).Name}' added to mind of {_minds.MindOwnerLoggingString(mind)}");
|
||||
var name = Loc.GetString(protoEnt.Name);
|
||||
if (mind.OwnedEntity is not null)
|
||||
{
|
||||
_adminLogger.Add(LogType.Mind,
|
||||
LogImpact.Low,
|
||||
$"{name} added to mind of {ToPrettyString(mind.OwnedEntity)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
//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
|
||||
_adminLogger.Add(LogType.Mind,
|
||||
LogImpact.Low,
|
||||
$"{name} added to {ToPrettyString(mindId)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a role from this mind.
|
||||
/// Removes all instances of a specific role from this mind.
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind to remove the role from.</param>
|
||||
/// <typeparam name="T">The type of the role to remove.</typeparam>
|
||||
/// <exception cref="ArgumentException">
|
||||
/// Thrown if we do not have this role.
|
||||
/// </exception>
|
||||
public void MindRemoveRole<T>(EntityUid mindId) where T : IComponent
|
||||
/// <exception cref="ArgumentException">Thrown if the mind does not exist or does not have this role.</exception>
|
||||
/// <returns>Returns False if there was something wrong with the mind or the removal. True if successful</returns>>
|
||||
public bool MindRemoveRole<T>(EntityUid mindId) where T : IComponent
|
||||
{
|
||||
if (!RemComp<T>(mindId))
|
||||
if (!TryComp<MindComponent>(mindId, out var mind) )
|
||||
throw new ArgumentException($"{mindId} does not exist or does not have mind component");
|
||||
|
||||
var found = false;
|
||||
var antagonist = false;
|
||||
var delete = new List<EntityUid>();
|
||||
foreach (var role in mind.MindRoles)
|
||||
{
|
||||
throw new ArgumentException($"We do not have this role: {typeof(T)}");
|
||||
if (!HasComp<T>(role))
|
||||
continue;
|
||||
|
||||
var roleComp = Comp<MindRoleComponent>(role);
|
||||
antagonist = roleComp.Antag;
|
||||
_entityManager.DeleteEntity(role);
|
||||
|
||||
delete.Add(role);
|
||||
found = true;
|
||||
|
||||
}
|
||||
|
||||
foreach (var role in delete)
|
||||
{
|
||||
mind.MindRoles.Remove(role);
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
throw new ArgumentException($"{mindId} does not have this role: {typeof(T)}");
|
||||
}
|
||||
|
||||
var mind = Comp<MindComponent>(mindId);
|
||||
var antagonist = IsAntagonistRole<T>();
|
||||
var message = new RoleRemovedEvent(mindId, mind, antagonist);
|
||||
|
||||
if (mind.OwnedEntity != null)
|
||||
{
|
||||
RaiseLocalEvent(mind.OwnedEntity.Value, message, true);
|
||||
}
|
||||
_adminLogger.Add(LogType.Mind, LogImpact.Low,
|
||||
$"'Role {typeof(T).Name}' removed from mind of {_minds.MindOwnerLoggingString(mind)}");
|
||||
}
|
||||
|
||||
public bool MindTryRemoveRole<T>(EntityUid mindId) where T : IComponent
|
||||
{
|
||||
if (!MindHasRole<T>(mindId))
|
||||
return false;
|
||||
|
||||
MindRemoveRole<T>(mindId);
|
||||
_adminLogger.Add(LogType.Mind,
|
||||
LogImpact.Low,
|
||||
$"'Role {typeof(T).Name}' removed from mind of {ToPrettyString(mind.OwnedEntity)}");
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool MindHasRole<T>(EntityUid mindId) where T : IComponent
|
||||
/// <summary>
|
||||
/// Finds and removes all mind roles of a specific type
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity</param>
|
||||
/// <typeparam name="T">The type of the role to remove.</typeparam>
|
||||
/// <returns>True if the role existed and was removed</returns>
|
||||
public bool MindTryRemoveRole<T>(EntityUid mindId) where T : IComponent
|
||||
{
|
||||
DebugTools.Assert(HasComp<MindComponent>(mindId));
|
||||
return HasComp<T>(mindId);
|
||||
}
|
||||
if (!MindHasRole<T>(mindId))
|
||||
{
|
||||
Log.Warning($"Failed to remove role {typeof(T)} from {mindId} : mind does not have role ");
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<RoleInfo> MindGetAllRoles(EntityUid mindId)
|
||||
{
|
||||
DebugTools.Assert(HasComp<MindComponent>(mindId));
|
||||
var ev = new MindGetAllRolesEvent(new List<RoleInfo>());
|
||||
RaiseLocalEvent(mindId, ref ev);
|
||||
return ev.Roles;
|
||||
}
|
||||
|
||||
public bool MindIsAntagonist(EntityUid? mindId)
|
||||
{
|
||||
if (mindId == null)
|
||||
if (typeof(T) == typeof(MindRoleComponent))
|
||||
return false;
|
||||
|
||||
DebugTools.Assert(HasComp<MindComponent>(mindId));
|
||||
var ev = new MindIsAntagonistEvent();
|
||||
RaiseLocalEvent(mindId.Value, ref ev);
|
||||
return ev.IsAntagonist;
|
||||
return MindRemoveRole<T>(mindId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity</param>
|
||||
/// <typeparam name="T">The type of the role to find.</typeparam>
|
||||
/// <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>
|
||||
public bool MindHasRole<T>(EntityUid mindId,
|
||||
[NotNullWhen(true)] out Entity<MindRoleComponent>? role,
|
||||
[NotNullWhen(true)] out Entity<T>? roleT) where T : IComponent
|
||||
{
|
||||
role = null;
|
||||
roleT = null;
|
||||
|
||||
if (!TryComp<MindComponent>(mindId, out var mind))
|
||||
return false;
|
||||
|
||||
var found = false;
|
||||
|
||||
foreach (var roleEnt in mind.MindRoles)
|
||||
{
|
||||
if (!HasComp<T>(roleEnt))
|
||||
continue;
|
||||
|
||||
role = (roleEnt,Comp<MindRoleComponent>(roleEnt));
|
||||
roleT = (roleEnt,Comp<T>(roleEnt));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first mind role of a specific type on a mind entity.
|
||||
/// Outputs an entity component for the mind role's MindRoleComponent
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity</param>
|
||||
/// <param name="type">The Type to look for</param>
|
||||
/// <param name="role">The output role</param>
|
||||
/// <returns>True if the role is found</returns>
|
||||
public bool MindHasRole(EntityUid mindId,
|
||||
Type type,
|
||||
[NotNullWhen(true)] out Entity<MindRoleComponent>? role)
|
||||
{
|
||||
role = null;
|
||||
// All MindRoles have this component, it would just return the first one.
|
||||
// Order might not be what is expected.
|
||||
// Better to report null
|
||||
if (type == Type.GetType("MindRoleComponent"))
|
||||
{
|
||||
Log.Error($"Something attempted to query mind role 'MindRoleComponent' on mind {mindId}. This component is present on every single mind role.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryComp<MindComponent>(mindId, out var mind))
|
||||
return false;
|
||||
|
||||
var found = false;
|
||||
|
||||
foreach (var roleEnt in mind.MindRoles)
|
||||
{
|
||||
if (!HasComp(roleEnt, type))
|
||||
continue;
|
||||
|
||||
role = (roleEnt,Comp<MindRoleComponent>(roleEnt));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first mind role of a specific type on a mind entity.
|
||||
/// Outputs an entity component for the mind role's MindRoleComponent
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity</param>
|
||||
/// <param name="role">The Mind Role entity component</param>
|
||||
/// <typeparam name="T">The type of the role to find.</typeparam>
|
||||
/// <returns>True if the role is found</returns>
|
||||
public bool MindHasRole<T>(EntityUid mindId,
|
||||
[NotNullWhen(true)] out Entity<MindRoleComponent>? role) where T : IComponent
|
||||
{
|
||||
return MindHasRole<T>(mindId, out role, out _);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds the first mind role of a specific type on a mind entity.
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity</param>
|
||||
/// <typeparam name="T">The type of the role to find.</typeparam>
|
||||
/// <returns>True if the role is found</returns>
|
||||
public bool MindHasRole<T>(EntityUid mindId) where T : IComponent
|
||||
{
|
||||
return MindHasRole<T>(mindId, out _, out _);
|
||||
}
|
||||
|
||||
//TODO: Delete this later
|
||||
/// <summary>
|
||||
/// Returns the first mind role of a specific type
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity</param>
|
||||
/// <returns>Entity Component of the mind role</returns>
|
||||
[Obsolete("Use MindHasRole's output value")]
|
||||
public Entity<MindRoleComponent>? MindGetRole<T>(EntityUid mindId) where T : IComponent
|
||||
{
|
||||
Entity<MindRoleComponent>? result = null;
|
||||
|
||||
var mind = Comp<MindComponent>(mindId);
|
||||
|
||||
foreach (var uid in mind.MindRoles)
|
||||
{
|
||||
if (HasComp<T>(uid) && TryComp<MindRoleComponent>(uid, out var comp))
|
||||
result = (uid,comp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads all Roles of a mind Entity and returns their data as RoleInfo
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity</param>
|
||||
/// <returns>RoleInfo list</returns>
|
||||
public List<RoleInfo> MindGetAllRoleInfo(EntityUid mindId)
|
||||
{
|
||||
var roleInfo = new List<RoleInfo>();
|
||||
|
||||
if (!TryComp<MindComponent>(mindId, out var mind))
|
||||
return roleInfo;
|
||||
|
||||
foreach (var role in mind.MindRoles)
|
||||
{
|
||||
var valid = false;
|
||||
var name = "game-ticker-unknown-role";
|
||||
var prototype = "";
|
||||
string? playTimeTracker = null;
|
||||
|
||||
var comp = Comp<MindRoleComponent>(role);
|
||||
if (comp.AntagPrototype is not null)
|
||||
{
|
||||
prototype = comp.AntagPrototype;
|
||||
}
|
||||
|
||||
if (comp.JobPrototype is not null && comp.AntagPrototype is null)
|
||||
{
|
||||
prototype = comp.JobPrototype;
|
||||
if (_prototypes.TryIndex(comp.JobPrototype, out var job))
|
||||
{
|
||||
playTimeTracker = job.PlayTimeTracker;
|
||||
name = job.Name;
|
||||
valid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($" Mind Role Prototype '{role.Id}' contains invalid Job prototype: '{comp.JobPrototype}'");
|
||||
}
|
||||
}
|
||||
else if (comp.AntagPrototype is not null && comp.JobPrototype is null)
|
||||
{
|
||||
prototype = comp.AntagPrototype;
|
||||
if (_prototypes.TryIndex(comp.AntagPrototype, out var antag))
|
||||
{
|
||||
name = antag.Name;
|
||||
valid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($" Mind Role Prototype '{role.Id}' contains invalid Antagonist prototype: '{comp.AntagPrototype}'");
|
||||
}
|
||||
}
|
||||
else if (comp.JobPrototype is not null && comp.AntagPrototype is not null)
|
||||
{
|
||||
Log.Error($" Mind Role Prototype '{role.Id}' contains both Job and Antagonist prototypes");
|
||||
}
|
||||
|
||||
if (valid)
|
||||
roleInfo.Add(new RoleInfo(name, comp.Antag || comp.ExclusiveAntag , playTimeTracker, prototype));
|
||||
}
|
||||
return roleInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does this mind possess an antagonist role
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity</param>
|
||||
/// <returns>True if the mind possesses any antag roles</returns>
|
||||
public bool MindIsAntagonist(EntityUid? mindId)
|
||||
{
|
||||
if (mindId is null)
|
||||
{
|
||||
Log.Warning($"Antagonist status of mind entity {mindId} could not be determined - mind entity not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
return CheckAntagonistStatus(mindId.Value).Item1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does this mind possess an exclusive antagonist role
|
||||
/// </summary>
|
||||
/// <param name="mindId">The mind entity</param>
|
||||
/// <returns>True if the mind possesses an exclusive antag role</returns>
|
||||
/// <returns>True if the mind possesses any exclusive antag roles</returns>
|
||||
public bool MindIsExclusiveAntagonist(EntityUid? mindId)
|
||||
{
|
||||
if (mindId == null)
|
||||
if (mindId is null)
|
||||
{
|
||||
Log.Warning($"Antagonist status of mind entity {mindId} could not be determined - mind entity not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
var ev = new MindIsAntagonistEvent();
|
||||
RaiseLocalEvent(mindId.Value, ref ev);
|
||||
return ev.IsExclusiveAntagonist;
|
||||
return CheckAntagonistStatus(mindId.Value).Item2;
|
||||
}
|
||||
|
||||
public bool IsAntagonistRole<T>()
|
||||
{
|
||||
return _antagTypes.Contains(typeof(T));
|
||||
}
|
||||
private (bool, bool) CheckAntagonistStatus(EntityUid mindId)
|
||||
{
|
||||
if (!TryComp<MindComponent>(mindId, out var mind))
|
||||
{
|
||||
Log.Warning($"Antagonist status of mind entity {mindId} could not be determined - mind component not found");
|
||||
return (false, false);
|
||||
}
|
||||
|
||||
public bool IsAntagonistRole(Type component)
|
||||
{
|
||||
return _antagTypes.Contains(component);
|
||||
var antagonist = false;
|
||||
var exclusiveAntag = false;
|
||||
foreach (var role in mind.MindRoles)
|
||||
{
|
||||
if (!TryComp<MindRoleComponent>(role, out var roleComp))
|
||||
{
|
||||
//If this ever shows up outside of an integration test, then we need to look into this further.
|
||||
Log.Warning($"Mind Role Entity {role} does not have MindRoleComponent!");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (roleComp.Antag || exclusiveAntag)
|
||||
antagonist = true;
|
||||
if (roleComp.ExclusiveAntag)
|
||||
exclusiveAntag = true;
|
||||
}
|
||||
|
||||
return (antagonist, exclusiveAntag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Shared.GameStates;
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Audio;
|
||||
using Content.Shared.Whitelist;
|
||||
|
||||
namespace Content.Shared.Storage.Components
|
||||
{
|
||||
@@ -26,6 +27,12 @@ namespace Content.Shared.Storage.Components
|
||||
[DataField("maxItemSize")]
|
||||
public ProtoId<ItemSizePrototype> MaxItemSize = "Small";
|
||||
|
||||
/// <summary>
|
||||
/// Entity blacklist for secret stashes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityWhitelist? Blacklist;
|
||||
|
||||
/// <summary>
|
||||
/// This sound will be played when you try to insert an item in the stash.
|
||||
/// The sound will be played whether or not the item is actually inserted.
|
||||
|
||||
@@ -13,6 +13,7 @@ using Robust.Shared.Audio.Systems;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Tools.EntitySystems;
|
||||
using Content.Shared.Whitelist;
|
||||
|
||||
namespace Content.Shared.Storage.EntitySystems;
|
||||
|
||||
@@ -27,7 +28,7 @@ public sealed class SecretStashSystem : EntitySystem
|
||||
[Dependency] private readonly SharedItemSystem _item = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly ToolOpenableSystem _toolOpenableSystem = default!;
|
||||
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -90,8 +91,9 @@ public sealed class SecretStashSystem : EntitySystem
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if item is too big to fit into secret stash
|
||||
if (_item.GetSizePrototype(itemComp.Size) > _item.GetSizePrototype(entity.Comp.MaxItemSize))
|
||||
// check if item is too big to fit into secret stash or is in the blacklist
|
||||
if (_item.GetSizePrototype(itemComp.Size) > _item.GetSizePrototype(entity.Comp.MaxItemSize) ||
|
||||
_whitelistSystem.IsBlacklistPass(entity.Comp.Blacklist, itemToHideUid))
|
||||
{
|
||||
var msg = Loc.GetString("comp-secret-stash-action-hide-item-too-big",
|
||||
("item", itemToHideUid), ("stashname", GetStashName(entity)));
|
||||
|
||||
@@ -32,6 +32,12 @@ public sealed partial class EntityWhitelist
|
||||
[DataField] public string[]? Components;
|
||||
// TODO yaml validation
|
||||
|
||||
/// <summary>
|
||||
/// Mind Role Prototype names that are allowed in the whitelist.
|
||||
/// </summary>
|
||||
[DataField] public string[]? MindRoles;
|
||||
// TODO yaml validation
|
||||
|
||||
/// <summary>
|
||||
/// Item sizes that are allowed in the whitelist.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Tag;
|
||||
|
||||
namespace Content.Shared.Whitelist;
|
||||
@@ -7,6 +8,7 @@ namespace Content.Shared.Whitelist;
|
||||
public sealed class EntityWhitelistSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||
[Dependency] private readonly SharedRoleSystem _roles = default!;
|
||||
[Dependency] private readonly TagSystem _tag = default!;
|
||||
|
||||
private EntityQuery<ItemComponent> _itemQuery;
|
||||
@@ -46,9 +48,30 @@ public sealed class EntityWhitelistSystem : EntitySystem
|
||||
public bool IsValid(EntityWhitelist list, EntityUid uid)
|
||||
{
|
||||
if (list.Components != null)
|
||||
EnsureRegistrations(list);
|
||||
{
|
||||
var regs = StringsToRegs(list.Components);
|
||||
|
||||
if (list.Registrations != null)
|
||||
list.Registrations ??= new List<ComponentRegistration>();
|
||||
list.Registrations.AddRange(regs);
|
||||
}
|
||||
|
||||
if (list.MindRoles != null)
|
||||
{
|
||||
var regs = StringsToRegs(list.MindRoles);
|
||||
|
||||
foreach (var role in regs)
|
||||
{
|
||||
if ( _roles.MindHasRole(uid, role.Type, out _))
|
||||
{
|
||||
if (!list.RequireAll)
|
||||
return true;
|
||||
}
|
||||
else if (list.RequireAll)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (list.Registrations != null && list.Registrations.Count > 0)
|
||||
{
|
||||
foreach (var reg in list.Registrations)
|
||||
{
|
||||
@@ -153,7 +176,7 @@ public sealed class EntityWhitelistSystem : EntitySystem
|
||||
return IsWhitelistPassOrNull(blacklist, uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Helper function to determine if Blacklist is either null or the entity is not on the list
|
||||
/// Duplicate of equivalent Whitelist function
|
||||
/// </summary>
|
||||
@@ -162,24 +185,27 @@ public sealed class EntityWhitelistSystem : EntitySystem
|
||||
return IsWhitelistFailOrNull(blacklist, uid);
|
||||
}
|
||||
|
||||
private void EnsureRegistrations(EntityWhitelist list)
|
||||
private List<ComponentRegistration> StringsToRegs(string[]? input)
|
||||
{
|
||||
if (list.Components == null)
|
||||
return;
|
||||
var list = new List<ComponentRegistration>();
|
||||
|
||||
list.Registrations = new List<ComponentRegistration>();
|
||||
foreach (var name in list.Components)
|
||||
if (input == null || input.Length == 0)
|
||||
return list;
|
||||
|
||||
foreach (var name in input)
|
||||
{
|
||||
var availability = _factory.GetComponentAvailability(name);
|
||||
if (_factory.TryGetRegistration(name, out var registration)
|
||||
&& availability == ComponentAvailability.Available)
|
||||
{
|
||||
list.Registrations.Add(registration);
|
||||
list.Add(registration);
|
||||
}
|
||||
else if (availability == ComponentAvailability.Unknown)
|
||||
{
|
||||
Log.Warning($"Unknown component name {name} passed to EntityWhitelist!");
|
||||
Log.Error($"StringsToRegs failed: Unknown component name {name} passed to EntityWhitelist!");
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,5 +552,12 @@ Entries:
|
||||
id: 68
|
||||
time: '2024-09-23T07:28:42.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32395
|
||||
- author: SlamBamActionman
|
||||
changes:
|
||||
- message: Added admin logging for generated codewords.
|
||||
type: Add
|
||||
id: 69
|
||||
time: '2024-10-09T11:55:49.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32531
|
||||
Name: Admin
|
||||
Order: 1
|
||||
|
||||
@@ -1,126 +1,4 @@
|
||||
Entries:
|
||||
- author: Moomoobeef
|
||||
changes:
|
||||
- message: Some radio channel colors have been tweaked in order to be more easily
|
||||
distinguishable.
|
||||
type: Tweak
|
||||
id: 6984
|
||||
time: '2024-07-26T06:47:20.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30133
|
||||
- author: Errant
|
||||
changes:
|
||||
- message: Replay ghosts now actually spawn on the proper station, take two.
|
||||
type: Fix
|
||||
id: 6985
|
||||
time: '2024-07-26T12:59:43.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30273
|
||||
- author: themias
|
||||
changes:
|
||||
- message: Arcade machines are functional again
|
||||
type: Fix
|
||||
id: 6986
|
||||
time: '2024-07-26T17:30:50.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30376
|
||||
- author: themias
|
||||
changes:
|
||||
- message: Zombies now get uncuffed upon transformation
|
||||
type: Fix
|
||||
id: 6987
|
||||
time: '2024-07-26T18:48:03.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30321
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- message: Fix grid labels getting spammed from VGRoid.
|
||||
type: Fix
|
||||
id: 6988
|
||||
time: '2024-07-27T01:54:38.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/29946
|
||||
- author: GoldenCan
|
||||
changes:
|
||||
- message: Added a Security Clown Mask which is obtainable by hacking a SecDrobe.
|
||||
type: Add
|
||||
id: 6989
|
||||
time: '2024-07-27T04:09:24.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30249
|
||||
- author: Plykiya
|
||||
changes:
|
||||
- message: Thief game rule now properly selects more than one thief.
|
||||
type: Fix
|
||||
id: 6990
|
||||
time: '2024-07-27T07:27:21.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30393
|
||||
- author: BombasterDS
|
||||
changes:
|
||||
- message: Added new plant mutations for apple, sugarcane and galaxythistle
|
||||
type: Add
|
||||
id: 6991
|
||||
time: '2024-07-27T15:08:49.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/28993
|
||||
- author: Spessmann
|
||||
changes:
|
||||
- message: Thief objectives for figurines and stamps now require less items
|
||||
type: Tweak
|
||||
id: 6992
|
||||
time: '2024-07-27T23:11:27.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30390
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- message: Moved VGRoid from 1,000m away to ~500m.
|
||||
type: Tweak
|
||||
id: 6993
|
||||
time: '2024-07-28T03:14:18.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/29943
|
||||
- author: lzk228
|
||||
changes:
|
||||
- message: Fixed pancakes stacks. Before it, splitting not default pancakes stacks
|
||||
would give you default pancakes.
|
||||
type: Fix
|
||||
id: 6994
|
||||
time: '2024-07-28T03:49:06.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30270
|
||||
- author: Plykiya
|
||||
changes:
|
||||
- message: Fixed the client mispredicting people slipping with their magboots turned
|
||||
on
|
||||
type: Fix
|
||||
id: 6995
|
||||
time: '2024-07-28T06:17:06.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30425
|
||||
- author: Katzenminer
|
||||
changes:
|
||||
- message: Pun and similar pets are no longer firemune
|
||||
type: Fix
|
||||
id: 6996
|
||||
time: '2024-07-28T08:32:27.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30424
|
||||
- author: lzk228
|
||||
changes:
|
||||
- message: Fixed permanent absence of the approver string in cargo invoice.
|
||||
type: Fix
|
||||
id: 6997
|
||||
time: '2024-07-29T06:19:43.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/29690
|
||||
- author: JIPDawg
|
||||
changes:
|
||||
- message: F9 is correctly bound to the Round End Summary window by default now.
|
||||
type: Fix
|
||||
id: 6998
|
||||
time: '2024-07-29T06:49:28.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30438
|
||||
- author: githubuser508
|
||||
changes:
|
||||
- message: Candles crate and the ability for Cargo to order it.
|
||||
type: Add
|
||||
id: 6999
|
||||
time: '2024-07-29T08:29:27.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/29736
|
||||
- author: Blackern5000
|
||||
changes:
|
||||
- message: Emergency oxygen and fire lockers now generally contain more supplies
|
||||
type: Tweak
|
||||
id: 7000
|
||||
time: '2024-07-29T09:57:04.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/29230
|
||||
- author: Moomoobeef
|
||||
changes:
|
||||
- message: Added the ability to wear lizard plushies on your head!
|
||||
@@ -3937,3 +3815,140 @@
|
||||
id: 7483
|
||||
time: '2024-10-03T14:01:01.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32552
|
||||
- author: Southbridge
|
||||
changes:
|
||||
- message: Box station's recycler has been properly connected.
|
||||
type: Fix
|
||||
- message: Box station's singlo substation has been rewired to the station-wide
|
||||
HV network.
|
||||
type: Tweak
|
||||
- message: Box station now has AI law boards in the upload room.
|
||||
type: Add
|
||||
id: 7484
|
||||
time: '2024-10-04T01:12:59.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32608
|
||||
- author: Plykiya
|
||||
changes:
|
||||
- message: You can no longer FTL the station.
|
||||
type: Fix
|
||||
id: 7485
|
||||
time: '2024-10-04T02:55:36.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32558
|
||||
- author: SoulFN
|
||||
changes:
|
||||
- message: Dragon can now pull things using tail.
|
||||
type: Add
|
||||
id: 7486
|
||||
time: '2024-10-04T05:39:50.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32568
|
||||
- author: SaphireLattice
|
||||
changes:
|
||||
- message: Explosives throw a container they are in
|
||||
type: Tweak
|
||||
id: 7487
|
||||
time: '2024-10-04T08:43:45.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32428
|
||||
- author: SaphireLattice
|
||||
changes:
|
||||
- message: Syndicate C4 now starts a countdown on signal, rather than exploding
|
||||
instantly.
|
||||
type: Tweak
|
||||
id: 7488
|
||||
time: '2024-10-04T09:34:49.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32423
|
||||
- author: slarticodefast
|
||||
changes:
|
||||
- message: Light bulbs now fit into the trash bag again.
|
||||
type: Fix
|
||||
id: 7489
|
||||
time: '2024-10-05T18:44:47.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32452
|
||||
- author: Just_Art
|
||||
changes:
|
||||
- message: Added a flower wreath and hairflower to loadout!
|
||||
type: Add
|
||||
id: 7490
|
||||
time: '2024-10-06T12:48:49.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32097
|
||||
- author: Golinth
|
||||
changes:
|
||||
- message: Removed clown waddling until implemented properly
|
||||
type: Remove
|
||||
id: 7491
|
||||
time: '2024-10-06T13:33:02.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32652
|
||||
- author: SaphireLattice
|
||||
changes:
|
||||
- message: Minibomb is now explosion resistant and will start counting down if damaged
|
||||
by a C4
|
||||
type: Tweak
|
||||
id: 7492
|
||||
time: '2024-10-07T22:42:43.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32429
|
||||
- author: shampunj
|
||||
changes:
|
||||
- message: When curtains are destroyed, only 1 cloth drops out
|
||||
type: Tweak
|
||||
id: 7493
|
||||
time: '2024-10-08T09:51:23.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32685
|
||||
- author: Plykiya
|
||||
changes:
|
||||
- message: Librarians now start with a D20.
|
||||
type: Tweak
|
||||
id: 7494
|
||||
time: '2024-10-08T09:53:50.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32648
|
||||
- author: K-Dynamic
|
||||
changes:
|
||||
- message: CHIMP and APE particles should be almost as fast as before
|
||||
type: Tweak
|
||||
id: 7495
|
||||
time: '2024-10-08T18:29:37.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32690
|
||||
- author: ss14nekow
|
||||
changes:
|
||||
- message: Added pumpkin pie! Can be made in the microwave with 1 pie tin, 1 pie
|
||||
dough, and 1 pumpkin.
|
||||
type: Add
|
||||
id: 7496
|
||||
time: '2024-10-08T23:29:41.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32623
|
||||
- author: Beck Thompson
|
||||
changes:
|
||||
- message: The radio jammers price has been decreased from 4 -> 3 TC.
|
||||
type: Tweak
|
||||
id: 7497
|
||||
time: '2024-10-09T12:44:38.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32472
|
||||
- author: beck-thompson, Moomoobeef
|
||||
changes:
|
||||
- message: Plushies now can have small items stuffed inside of them! To open, click
|
||||
on the plush with a sharp item. You can then insert a small item into the plush.
|
||||
To close, just click on it again with any sharp item!
|
||||
type: Add
|
||||
- message: When pAIs are inserted into plushies, their names will be updated to
|
||||
the plushies name. E.g when inside a lizard plushie the pAI's name will appear
|
||||
as "lizard plushie" when speaking.
|
||||
type: Add
|
||||
id: 7498
|
||||
time: '2024-10-09T18:01:32.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/30805
|
||||
- author: Errant
|
||||
changes:
|
||||
- message: Nukies now have role briefing on their character screen.
|
||||
type: Tweak
|
||||
- message: Animals who are made Thief no longer get told in their briefing about
|
||||
the thief gear that they don't actually have and couldn't possibly use.
|
||||
type: Fix
|
||||
id: 7499
|
||||
time: '2024-10-10T08:48:57.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/31318
|
||||
- author: nikthechampiongr
|
||||
changes:
|
||||
- message: It is no longer impossible to do bounties other than the first one if
|
||||
the server has been up for a long period of time.
|
||||
type: Fix
|
||||
id: 7500
|
||||
time: '2024-10-10T14:35:36.0000000+00:00'
|
||||
url: https://github.com/space-wizards/space-station-14/pull/32700
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
### Announcement
|
||||
|
||||
earlyleave-cryo-job-unknown = Unknown
|
||||
earlyleave-cryo-announcement = {$character} ({$job}) { CONJUGATE-HAVE($entity) } entered cryogenic storage!
|
||||
# {$entity} available for GENDER function purposes
|
||||
earlyleave-cryo-announcement = {$character} ({$job}) has entered cryogenic storage!
|
||||
earlyleave-cryo-sender = Station
|
||||
|
||||
@@ -4,6 +4,7 @@ nukeops-description = Nuclear operatives have targeted the station. Try to keep
|
||||
nukeops-welcome =
|
||||
You are a nuclear operative. Your goal is to blow up {$station}, and ensure that it is nothing but a pile of rubble. Your bosses, the Syndicate, have provided you with the tools you'll need for the task.
|
||||
Operation {$name} is a go ! Death to Nanotrasen!
|
||||
nukeops-briefing = Your objectives are simple. Deliver the payload and make sure it detonates. Begin mission.
|
||||
|
||||
nukeops-opsmajor = [color=crimson]Syndicate major victory![/color]
|
||||
nukeops-opsminor = [color=crimson]Syndicate minor victory![/color]
|
||||
|
||||
@@ -62,6 +62,11 @@ job-name-unknown = Unknown
|
||||
job-name-virologist = Virologist
|
||||
job-name-zombie = Zombie
|
||||
|
||||
# Job titles
|
||||
job-title-visitor = Visitor
|
||||
job-title-cluwne = Cluwne
|
||||
job-title-universal = Universal
|
||||
|
||||
# Role timers - Make these alphabetical or I cut you
|
||||
JobAtmosphericTechnician = Atmospheric Technician
|
||||
JobBartender = Bartender
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
job-supervisors-centcom = Central Command
|
||||
job-supervisors-captain = the captain
|
||||
job-supervisors-hop = the head of personnel
|
||||
job-supervisors-hos = the head of security
|
||||
job-supervisors-ce = the chief engineer
|
||||
job-supervisors-cmo = the chief medical officer
|
||||
job-supervisors-rd = the research director
|
||||
job-supervisors-qm = the quartermaster
|
||||
job-supervisors-service = chefs, botanists, the bartender, and the head of personnel
|
||||
job-supervisors-engineering = station engineers, atmospheric technicians, and the chief engineer
|
||||
job-supervisors-medicine = medical doctors, chemists, and the chief medical officer
|
||||
job-supervisors-security = security officers, the warden, and the head of security
|
||||
job-supervisors-science = scientists, and the research director
|
||||
job-supervisors-captain = the Captain
|
||||
job-supervisors-hop = the Head of Personnel
|
||||
job-supervisors-hos = the Head of Security
|
||||
job-supervisors-ce = the Chief Engineer
|
||||
job-supervisors-cmo = the Chief Medical Officer
|
||||
job-supervisors-rd = the Research Director
|
||||
job-supervisors-qm = the Quartermaster
|
||||
job-supervisors-service = Chefs, Botanists, the Bartender, and the Head of Personnel
|
||||
job-supervisors-engineering = Station Engineers, Atmospheric Technicians, and the Chief Engineer
|
||||
job-supervisors-medicine = Medical Doctors, Paramedics, Chemists, and the Chief Medical Officer
|
||||
job-supervisors-security = Security Officers, the Warden, and the Head of Security
|
||||
job-supervisors-science = Scientists and the Research Director
|
||||
job-supervisors-hire = whoever hires you
|
||||
job-supervisors-everyone = absolutely everyone
|
||||
|
||||
@@ -28,6 +28,9 @@ signal-port-description-doorbolt = Bolts door when HIGH.
|
||||
signal-port-name-trigger = Trigger
|
||||
signal-port-description-trigger = Triggers some mechanism on the device.
|
||||
|
||||
signal-port-name-timer = Timer
|
||||
signal-port-description-timer = Starts the timer countdown of the device.
|
||||
|
||||
signal-port-name-order-sender = Order sender
|
||||
signal-port-description-order-sender = Cargo console order sender
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ shuttle-pilot-end = Stopped piloting
|
||||
shuttle-console-in-ftl = Currently in FTL
|
||||
shuttle-console-mass = Too large to FTL
|
||||
shuttle-console-prevent = You are unable to pilot this ship
|
||||
shuttle-console-static = Grid is static
|
||||
|
||||
# NAV
|
||||
|
||||
|
||||
@@ -22,3 +22,4 @@ comp-secret-stash-verb-open = Open
|
||||
### Stash names
|
||||
secret-stash-plant = plant
|
||||
secret-stash-toilet = toilet cistern
|
||||
secret-stash-plushie = plushie
|
||||
|
||||
@@ -11579,7 +11579,7 @@ entities:
|
||||
pos: 24.5,16.5
|
||||
parent: 8364
|
||||
- type: Door
|
||||
secondsUntilStateChange: -15146.963
|
||||
secondsUntilStateChange: -16708.217
|
||||
state: Opening
|
||||
- type: DeviceLinkSource
|
||||
lastSignals:
|
||||
@@ -14682,6 +14682,13 @@ entities:
|
||||
rot: 1.5707963267948966 rad
|
||||
pos: 8.5,-46.5
|
||||
parent: 8364
|
||||
- proto: ArtistCircuitBoard
|
||||
entities:
|
||||
- uid: 27915
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -4.5121183,-14.413322
|
||||
parent: 8364
|
||||
- proto: Ash
|
||||
entities:
|
||||
- uid: 13307
|
||||
@@ -14689,6 +14696,13 @@ entities:
|
||||
- type: Transform
|
||||
pos: -36.5,20.5
|
||||
parent: 8364
|
||||
- proto: AsimovCircuitBoard
|
||||
entities:
|
||||
- uid: 27912
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 3.2198467,-12.066172
|
||||
parent: 8364
|
||||
- proto: AsteroidRock
|
||||
entities:
|
||||
- uid: 2042
|
||||
@@ -39720,12 +39734,12 @@ entities:
|
||||
- uid: 3869
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 13.5,-74.5
|
||||
pos: 15.5,-77.5
|
||||
parent: 8364
|
||||
- uid: 4050
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 8.5,-74.5
|
||||
pos: 10.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 4519
|
||||
components:
|
||||
@@ -39735,7 +39749,7 @@ entities:
|
||||
- uid: 4553
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 15.5,-74.5
|
||||
pos: 11.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 4585
|
||||
components:
|
||||
@@ -39785,7 +39799,7 @@ entities:
|
||||
- uid: 4973
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 7.5,-74.5
|
||||
pos: 9.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 4988
|
||||
components:
|
||||
@@ -41467,11 +41481,6 @@ entities:
|
||||
- type: Transform
|
||||
pos: 23.5,23.5
|
||||
parent: 8364
|
||||
- uid: 8185
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 5.5,-74.5
|
||||
parent: 8364
|
||||
- uid: 8434
|
||||
components:
|
||||
- type: Transform
|
||||
@@ -42860,7 +42869,7 @@ entities:
|
||||
- uid: 10762
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 6.5,-74.5
|
||||
pos: 8.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 10783
|
||||
components:
|
||||
@@ -44167,6 +44176,16 @@ entities:
|
||||
- type: Transform
|
||||
pos: -6.5,-50.5
|
||||
parent: 8364
|
||||
- uid: 15804
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 10.5,-81.5
|
||||
parent: 8364
|
||||
- uid: 15810
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -0.5,-79.5
|
||||
parent: 8364
|
||||
- uid: 15815
|
||||
components:
|
||||
- type: Transform
|
||||
@@ -44965,12 +44984,7 @@ entities:
|
||||
- uid: 17602
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 14.5,-74.5
|
||||
parent: 8364
|
||||
- uid: 17697
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 12.5,-74.5
|
||||
pos: 15.5,-76.5
|
||||
parent: 8364
|
||||
- uid: 17698
|
||||
components:
|
||||
@@ -46147,15 +46161,10 @@ entities:
|
||||
- type: Transform
|
||||
pos: 25.5,-109.5
|
||||
parent: 8364
|
||||
- uid: 23221
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 11.5,-74.5
|
||||
parent: 8364
|
||||
- uid: 23223
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 10.5,-74.5
|
||||
pos: 12.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 23238
|
||||
components:
|
||||
@@ -46172,6 +46181,11 @@ entities:
|
||||
- type: Transform
|
||||
pos: 23.5,-109.5
|
||||
parent: 8364
|
||||
- uid: 24687
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 13.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 25012
|
||||
components:
|
||||
- type: Transform
|
||||
@@ -46805,17 +46819,17 @@ entities:
|
||||
- uid: 26589
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 9.5,-81.5
|
||||
pos: 14.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 26591
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 9.5,-80.5
|
||||
pos: 15.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 26608
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 9.5,-79.5
|
||||
pos: 15.5,-79.5
|
||||
parent: 8364
|
||||
- uid: 26627
|
||||
components:
|
||||
@@ -47000,7 +47014,7 @@ entities:
|
||||
- uid: 27230
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 7.5,-81.5
|
||||
pos: 15.5,-78.5
|
||||
parent: 8364
|
||||
- uid: 27461
|
||||
components:
|
||||
@@ -47192,6 +47206,31 @@ entities:
|
||||
- type: Transform
|
||||
pos: 9.5,-68.5
|
||||
parent: 8364
|
||||
- uid: 27905
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 9.5,-71.5
|
||||
parent: 8364
|
||||
- uid: 27906
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 9.5,-72.5
|
||||
parent: 8364
|
||||
- uid: 27907
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 9.5,-70.5
|
||||
parent: 8364
|
||||
- uid: 27908
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 9.5,-69.5
|
||||
parent: 8364
|
||||
- uid: 27909
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 9.5,-73.5
|
||||
parent: 8364
|
||||
- proto: CableHVStack
|
||||
entities:
|
||||
- uid: 1683
|
||||
@@ -64174,6 +64213,11 @@ entities:
|
||||
- type: Transform
|
||||
pos: 1.5,28.5
|
||||
parent: 8364
|
||||
- uid: 27919
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 2.7203026,-11.488351
|
||||
parent: 8364
|
||||
- proto: ChairOfficeLight
|
||||
entities:
|
||||
- uid: 5220
|
||||
@@ -69939,6 +69983,13 @@ entities:
|
||||
rot: 3.141592653589793 rad
|
||||
pos: 22.5,-3.5
|
||||
parent: 8364
|
||||
- proto: CommandmentCircuitBoard
|
||||
entities:
|
||||
- uid: 27910
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -4.600172,-11.893632
|
||||
parent: 8364
|
||||
- proto: CommsComputerCircuitboard
|
||||
entities:
|
||||
- uid: 16499
|
||||
@@ -70504,65 +70555,87 @@ entities:
|
||||
rot: -1.5707963267948966 rad
|
||||
pos: -60.5,-15.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 6227
|
||||
components:
|
||||
- type: Transform
|
||||
rot: -1.5707963267948966 rad
|
||||
pos: -59.5,-15.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 6228
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -57.5,-15.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 6229
|
||||
components:
|
||||
- type: Transform
|
||||
rot: -1.5707963267948966 rad
|
||||
pos: -57.5,-16.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 6230
|
||||
components:
|
||||
- type: Transform
|
||||
rot: -1.5707963267948966 rad
|
||||
pos: -58.5,-16.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 6231
|
||||
components:
|
||||
- type: Transform
|
||||
rot: -1.5707963267948966 rad
|
||||
pos: -59.5,-16.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 6232
|
||||
components:
|
||||
- type: Transform
|
||||
rot: -1.5707963267948966 rad
|
||||
pos: -60.5,-16.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 6233
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 3.141592653589793 rad
|
||||
pos: -61.5,-16.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 6234
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 3.141592653589793 rad
|
||||
pos: -61.5,-17.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 6235
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 3.141592653589793 rad
|
||||
pos: -61.5,-18.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 6236
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 3.141592653589793 rad
|
||||
pos: -61.5,-19.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 7162
|
||||
components:
|
||||
- type: Transform
|
||||
@@ -70647,6 +70720,8 @@ entities:
|
||||
rot: 3.141592653589793 rad
|
||||
pos: -61.5,-20.5
|
||||
parent: 8364
|
||||
- type: DeviceLinkSink
|
||||
invokeCounter: 4
|
||||
- uid: 17202
|
||||
components:
|
||||
- type: Transform
|
||||
@@ -70724,6 +70799,13 @@ entities:
|
||||
- type: Transform
|
||||
pos: 90.5,-27.5
|
||||
parent: 8364
|
||||
- proto: CorporateCircuitBoard
|
||||
entities:
|
||||
- uid: 27914
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -4.441743,-12.321369
|
||||
parent: 8364
|
||||
- proto: CowToolboxFilled
|
||||
entities:
|
||||
- uid: 13853
|
||||
@@ -81392,6 +81474,13 @@ entities:
|
||||
- type: Transform
|
||||
pos: -33.51487,5.3832693
|
||||
parent: 8364
|
||||
- proto: DrinkHotCoffee
|
||||
entities:
|
||||
- uid: 27918
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 3.3560345,-11.363264
|
||||
parent: 8364
|
||||
- proto: DrinkMugBlack
|
||||
entities:
|
||||
- uid: 18382
|
||||
@@ -81446,6 +81535,13 @@ entities:
|
||||
- type: Transform
|
||||
pos: 74.3515,-18.443996
|
||||
parent: 8364
|
||||
- proto: DungeonMasterCircuitBoard
|
||||
entities:
|
||||
- uid: 27917
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 3.5477152,-12.389968
|
||||
parent: 8364
|
||||
- proto: EmergencyLight
|
||||
entities:
|
||||
- uid: 21308
|
||||
@@ -84322,7 +84418,7 @@ entities:
|
||||
pos: -34.5,-14.5
|
||||
parent: 8364
|
||||
- type: Door
|
||||
secondsUntilStateChange: -9335.5
|
||||
secondsUntilStateChange: -10896.755
|
||||
state: Closing
|
||||
- uid: 15010
|
||||
components:
|
||||
@@ -84814,6 +84910,9 @@ entities:
|
||||
- type: Transform
|
||||
pos: -4.5,-71.5
|
||||
parent: 8364
|
||||
- type: Door
|
||||
secondsUntilStateChange: -2644.4858
|
||||
state: Closing
|
||||
- proto: Fireplace
|
||||
entities:
|
||||
- uid: 11559
|
||||
@@ -119851,6 +119950,12 @@ entities:
|
||||
- type: Transform
|
||||
pos: 15.5,-41.5
|
||||
parent: 8364
|
||||
- uid: 21746
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 3.141592653589793 rad
|
||||
pos: 10.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 21751
|
||||
components:
|
||||
- type: Transform
|
||||
@@ -120086,6 +120191,12 @@ entities:
|
||||
- type: Transform
|
||||
pos: 17.5,-52.5
|
||||
parent: 8364
|
||||
- uid: 23221
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 3.141592653589793 rad
|
||||
pos: 10.5,-81.5
|
||||
parent: 8364
|
||||
- uid: 23222
|
||||
components:
|
||||
- type: Transform
|
||||
@@ -124581,6 +124692,13 @@ entities:
|
||||
- type: Transform
|
||||
pos: 32.5,-6.5
|
||||
parent: 8364
|
||||
- proto: NTDefaultCircuitBoard
|
||||
entities:
|
||||
- uid: 27913
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -4.5442195,-11.268198
|
||||
parent: 8364
|
||||
- proto: NuclearBomb
|
||||
entities:
|
||||
- uid: 12086
|
||||
@@ -124595,6 +124713,13 @@ entities:
|
||||
- type: Transform
|
||||
pos: 8.5,-27.5
|
||||
parent: 8364
|
||||
- proto: NutimovCircuitBoard
|
||||
entities:
|
||||
- uid: 27916
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 3.6621494,-11.814552
|
||||
parent: 8364
|
||||
- proto: OperatingTable
|
||||
entities:
|
||||
- uid: 2385
|
||||
@@ -124868,6 +124993,13 @@ entities:
|
||||
- type: Transform
|
||||
pos: 58.5,-11.5
|
||||
parent: 8364
|
||||
- proto: PaladinCircuitBoard
|
||||
entities:
|
||||
- uid: 27911
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -4.443845,-11.67473
|
||||
parent: 8364
|
||||
- proto: Paper
|
||||
entities:
|
||||
- uid: 1779
|
||||
@@ -137083,6 +137215,12 @@ entities:
|
||||
- type: Transform
|
||||
pos: -3.5,-20.5
|
||||
parent: 8364
|
||||
- uid: 8185
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 3.141592653589793 rad
|
||||
pos: 10.5,-81.5
|
||||
parent: 8364
|
||||
- uid: 8207
|
||||
components:
|
||||
- type: Transform
|
||||
@@ -137907,6 +138045,12 @@ entities:
|
||||
- type: Transform
|
||||
pos: 25.5,-75.5
|
||||
parent: 8364
|
||||
- uid: 17697
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 3.141592653589793 rad
|
||||
pos: 10.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 17787
|
||||
components:
|
||||
- type: Transform
|
||||
@@ -138440,6 +138584,13 @@ entities:
|
||||
- type: Transform
|
||||
pos: 0.2510581,39.68831
|
||||
parent: 8364
|
||||
- proto: RobocopCircuitBoard
|
||||
entities:
|
||||
- uid: 22443
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 3.4364183,-14.443886
|
||||
parent: 8364
|
||||
- proto: RobustHarvestChemistryBottle
|
||||
entities:
|
||||
- uid: 13505
|
||||
@@ -143986,6 +144137,13 @@ entities:
|
||||
- type: Transform
|
||||
pos: -20.5,-74.5
|
||||
parent: 8364
|
||||
- proto: StationEfficiencyCircuitBoard
|
||||
entities:
|
||||
- uid: 27536
|
||||
components:
|
||||
- type: Transform
|
||||
pos: -4.631007,-12.460042
|
||||
parent: 8364
|
||||
- proto: StationMap
|
||||
entities:
|
||||
- uid: 27690
|
||||
@@ -151539,8 +151697,12 @@ entities:
|
||||
- Right: Reverse
|
||||
- Middle: Off
|
||||
6120:
|
||||
- Left: Forward
|
||||
- Right: Reverse
|
||||
- Left: Forward
|
||||
- Middle: Off
|
||||
16588:
|
||||
- Right: Reverse
|
||||
- Left: Forward
|
||||
- Middle: Off
|
||||
- uid: 16780
|
||||
components:
|
||||
@@ -161682,16 +161844,6 @@ entities:
|
||||
- type: Transform
|
||||
pos: 10.5,-79.5
|
||||
parent: 8364
|
||||
- uid: 15804
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 10.5,-81.5
|
||||
parent: 8364
|
||||
- uid: 15810
|
||||
components:
|
||||
- type: Transform
|
||||
pos: 10.5,-80.5
|
||||
parent: 8364
|
||||
- uid: 15813
|
||||
components:
|
||||
- type: Transform
|
||||
@@ -172478,7 +172630,7 @@ entities:
|
||||
rot: 1.5707963267948966 rad
|
||||
pos: 8.5,-18.5
|
||||
parent: 8364
|
||||
- uid: 21746
|
||||
- uid: 31746
|
||||
components:
|
||||
- type: Transform
|
||||
rot: 3.141592653589793 rad
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user