Merge remote-tracking branch 'upstream/stable' into ed-15-10-2024-upstream
# Conflicts: # Content.Server/Station/Systems/StationSpawningSystem.cs
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"));
|
||||
|
||||
@@ -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
|
||||
|
||||
18700
Resources/Maps/cog.yml
18700
Resources/Maps/cog.yml
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user