diff --git a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs index 8512107b69..de51b2fb19 100644 --- a/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs +++ b/Content.Benchmarks/SpawnEquipDeleteBenchmark.cs @@ -58,7 +58,7 @@ await _pair.Server.WaitPost(() => for (var i = 0; i < N; i++) { _entity = server.EntMan.SpawnAttachedTo(Mob, _coords); - _spawnSys.EquipStartingGear(_entity, _gear); + _spawnSys.EquipStartingGear(_entity, _gear, null); server.EntMan.DeleteEntity(_entity); } }); diff --git a/Content.Client/Actions/ActionsSystem.cs b/Content.Client/Actions/ActionsSystem.cs index b9d554607c..b992e77256 100644 --- a/Content.Client/Actions/ActionsSystem.cs +++ b/Content.Client/Actions/ActionsSystem.cs @@ -248,10 +248,7 @@ public void TriggerAction(EntityUid actionId, BaseActionComponent action) if (action.ClientExclusive) { if (instantAction.Event != null) - { instantAction.Event.Performer = user; - instantAction.Event.Action = actionId; - } PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime); } diff --git a/Content.Client/Goobstation/Changeling/ChangelingSystem.cs b/Content.Client/Goobstation/Changeling/ChangelingSystem.cs deleted file mode 100644 index 58d949a634..0000000000 --- a/Content.Client/Goobstation/Changeling/ChangelingSystem.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Content.Client.Alerts; -using Content.Client.UserInterface.Systems.Alerts.Controls; -using Content.Shared.Changeling; -using Content.Shared.StatusIcon.Components; -using Robust.Shared.Prototypes; - -namespace Content.Client.Changeling; - -public sealed partial class ChangelingSystem : EntitySystem -{ - - [Dependency] private readonly IPrototypeManager _prototype = default!; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnUpdateAlert); - SubscribeLocalEvent(GetChanglingIcon); - } - - private void OnUpdateAlert(EntityUid uid, ChangelingComponent comp, ref UpdateAlertSpriteEvent args) - { - var stateNormalized = 0f; - - // hardcoded because uhh umm i don't know. send help. - switch (args.Alert.AlertViewEntity) - { - case "ChangelingChemicals": - stateNormalized = (int) (comp.Chemicals / comp.MaxChemicals * 18); - break; - - case "ChangelingBiomass": - stateNormalized = (int) (comp.Biomass / comp.MaxBiomass * 16); - break; - default: - return; - } - var sprite = args.SpriteViewEnt.Comp; - sprite.LayerSetState(AlertVisualLayers.Base, $"{stateNormalized}"); - } - - private void GetChanglingIcon(Entity ent, ref GetStatusIconsEvent args) - { - if (HasComp(ent) && _prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype)) - args.StatusIcons.Add(iconPrototype); - } -} diff --git a/Content.Client/Humanoid/HumanoidAppearanceSystem.cs b/Content.Client/Humanoid/HumanoidAppearanceSystem.cs index 867dcbc269..8087d1833e 100644 --- a/Content.Client/Humanoid/HumanoidAppearanceSystem.cs +++ b/Content.Client/Humanoid/HumanoidAppearanceSystem.cs @@ -118,11 +118,8 @@ private void SetLayerData( /// This should not be used if the entity is owned by the server. The server will otherwise /// override this with the appearance data it sends over. /// - public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile? profile, HumanoidAppearanceComponent? humanoid = null) + public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidAppearanceComponent? humanoid = null) { - if (profile == null) - return; - if (!Resolve(uid, ref humanoid)) { return; diff --git a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs index 7b67d23cec..09663ba82c 100644 --- a/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs +++ b/Content.Client/UserInterface/Systems/Actions/ActionUIController.cs @@ -215,7 +215,6 @@ private bool TryTargetWorld(in PointerInputCmdArgs args, EntityUid actionId, Wor { action.Event.Target = coords; action.Event.Performer = user; - action.Event.Action = actionId; } _actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime); @@ -250,7 +249,6 @@ private bool TryTargetEntity(in PointerInputCmdArgs args, EntityUid actionId, En { action.Event.Target = entity; action.Event.Performer = user; - action.Event.Action = actionId; } _actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime); diff --git a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs index 19699696c6..2372d98f8d 100644 --- a/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs +++ b/Content.Client/UserInterface/Systems/Actions/Controls/ActionButton.cs @@ -281,19 +281,10 @@ public void UpdateIcons() _controller ??= UserInterfaceManager.GetUIController(); _spriteSys ??= _entities.System(); - if ((_controller.SelectingTargetFor == ActionId || _action.Toggled)) - { - if (_action.IconOn != null) - SetActionIcon(_spriteSys.Frame0(_action.IconOn)); - - if (_action.BackgroundOn != null) - _buttonBackgroundTexture = _spriteSys.Frame0(_action.BackgroundOn); - } + if ((_controller.SelectingTargetFor == ActionId || _action.Toggled) && _action.IconOn != null) + SetActionIcon(_spriteSys.Frame0(_action.IconOn)); else - { SetActionIcon(_action.Icon != null ? _spriteSys.Frame0(_action.Icon) : null); - _buttonBackgroundTexture = Theme.ResolveTexture("SlotBackground"); - } } public void UpdateBackground() diff --git a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs b/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs deleted file mode 100644 index f539daee36..0000000000 --- a/Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs +++ /dev/null @@ -1,208 +0,0 @@ -/* WD edit - -#nullable enable -using System.Linq; -using Content.Server.Body.Components; -using Content.Server.GameTicking; -using Content.Server.GameTicking.Presets; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Mind; -using Content.Server.NPC.Systems; -using Content.Server.Pinpointer; -using Content.Server.Roles; -using Content.Server.Shuttles.Components; -using Content.Server.Station.Components; -using Content.Shared.CCVar; -using Content.Shared.Damage; -using Content.Shared.FixedPoint; -using Content.Shared.GameTicking; -using Content.Shared.Hands.Components; -using Content.Shared.Inventory; -using Content.Shared.NukeOps; -using Robust.Server.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.Map.Components; - -namespace Content.IntegrationTests.Tests.GameRules; - -[TestFixture] -public sealed class NukeOpsTest -{ - /// - /// Check that a nuke ops game mode can start without issue. I.e., that the nuke station and such all get loaded. - /// - [Test] - public async Task TryStopNukeOpsFromConstantlyFailing() - { - await using var pair = await PoolManager.GetServerClient(new PoolSettings - { - Dirty = true, - DummyTicker = false, - Connected = true, - InLobby = true - }); - - var server = pair.Server; - var client = pair.Client; - var entMan = server.EntMan; - var mapSys = server.System(); - var ticker = server.System(); - var mindSys = server.System(); - var roleSys = server.System(); - var invSys = server.System(); - var factionSys = server.System(); - - Assert.That(server.CfgMan.GetCVar(CCVars.GridFill), Is.False); - server.CfgMan.SetCVar(CCVars.GridFill, true); - - // Initially in the lobby - Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); - Assert.That(client.AttachedEntity, Is.Null); - Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay)); - - // There are no grids or maps - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - - // And no nukie related components - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - Assert.That(entMan.Count(), Is.Zero); - - // Ready up and start nukeops - await pair.WaitClientCommand("toggleready True"); - Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.ReadyToPlay)); - await pair.WaitCommand("forcepreset Nukeops"); - await pair.RunTicksSync(10); - - // Game should have started - Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound)); - Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.JoinedGame)); - Assert.That(client.EntMan.EntityExists(client.AttachedEntity)); - var player = pair.Player!.AttachedEntity!.Value; - Assert.That(entMan.EntityExists(player)); - - // Maps now exist - Assert.That(entMan.Count(), Is.GreaterThan(0)); - Assert.That(entMan.Count(), Is.GreaterThan(0)); - Assert.That(entMan.Count(), Is.EqualTo(2)); // The main station & nukie station - Assert.That(entMan.Count(), Is.GreaterThan(3)); // Each station has at least 1 grid, plus some shuttles - Assert.That(entMan.Count(), Is.EqualTo(1)); - - // And we now have nukie related components - Assert.That(entMan.Count(), Is.EqualTo(1)); - Assert.That(entMan.Count(), Is.EqualTo(1)); - Assert.That(entMan.Count(), Is.EqualTo(1)); - Assert.That(entMan.Count(), Is.EqualTo(1)); - - // The player entity should be the nukie commander - var mind = mindSys.GetMind(player)!.Value; - Assert.That(entMan.HasComponent(player)); - Assert.That(roleSys.MindIsAntagonist(mind)); - Assert.That(roleSys.MindHasRole(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); - Assert.That(cmdRoles.Count(), Is.EqualTo(1)); - - // The game rule exists, and all the stations/shuttles/maps are properly initialized - var rule = entMan.AllComponents().Single().Component; - var mapRule = entMan.AllComponents().Single().Component; - foreach (var grid in mapRule.MapGrids) - { - Assert.That(entMan.EntityExists(grid)); - Assert.That(entMan.HasComponent(grid)); - Assert.That(entMan.HasComponent(grid)); - } - Assert.That(entMan.EntityExists(rule.TargetStation)); - - Assert.That(entMan.HasComponent(rule.TargetStation)); - - var nukieShuttlEnt = entMan.AllComponents().FirstOrDefault().Uid; - Assert.That(entMan.EntityExists(nukieShuttlEnt)); - - EntityUid? nukieStationEnt = null; - foreach (var grid in mapRule.MapGrids) - { - if (entMan.HasComponent(grid)) - { - nukieStationEnt = grid; - break; - } - } - - Assert.That(entMan.EntityExists(nukieStationEnt)); - var nukieStation = entMan.GetComponent(nukieStationEnt!.Value); - - Assert.That(entMan.EntityExists(nukieStation.Station)); - Assert.That(nukieStation.Station, Is.Not.EqualTo(rule.TargetStation)); - - Assert.That(server.MapMan.MapExists(mapRule.Map)); - var nukieMap = mapSys.GetMap(mapRule.Map!.Value); - - var targetStation = entMan.GetComponent(rule.TargetStation!.Value); - var targetGrid = targetStation.Grids.First(); - var targetMap = entMan.GetComponent(targetGrid).MapUid!.Value; - Assert.That(targetMap, Is.Not.EqualTo(nukieMap)); - - Assert.That(entMan.GetComponent(player).MapUid, Is.EqualTo(nukieMap)); - Assert.That(entMan.GetComponent(nukieStationEnt.Value).MapUid, Is.EqualTo(nukieMap)); - Assert.That(entMan.GetComponent(nukieShuttlEnt).MapUid, Is.EqualTo(nukieMap)); - - // The maps are all map-initialized, including the player - // Yes, this is necessary as this has repeatedly been broken somehow. - Assert.That(mapSys.IsInitialized(nukieMap)); - Assert.That(mapSys.IsInitialized(targetMap)); - Assert.That(mapSys.IsPaused(nukieMap), Is.False); - Assert.That(mapSys.IsPaused(targetMap), Is.False); - - EntityLifeStage LifeStage(EntityUid? uid) => entMan.GetComponent(uid!.Value).EntityLifeStage; - Assert.That(LifeStage(player), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(nukieMap), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(targetMap), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(nukieStationEnt.Value), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(nukieShuttlEnt), Is.GreaterThan(EntityLifeStage.Initialized)); - Assert.That(LifeStage(rule.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized)); - - // Make sure the player has hands. We've had fucking disarmed nukies before. - Assert.That(entMan.HasComponent(player)); - Assert.That(entMan.GetComponent(player).Hands.Count, Is.GreaterThan(0)); - - // While we're at it, lets make sure they aren't naked. I don't know how many inventory slots all mobs will be - // likely to have in the future. But nukies should probably have at least 3 slots with something in them. - var enumerator = invSys.GetSlotEnumerator(player); - int total = 0; - while (enumerator.NextItem(out _)) - { - total++; - } - Assert.That(total, Is.GreaterThan(3)); - - // Finally lets check the nukie commander passed basic training and figured out how to breathe. - var totalSeconds = 30; - var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds); - int increment = 5; - var resp = entMan.GetComponent(player); - var damage = entMan.GetComponent(player); - for (var tick = 0; tick < totalTicks; tick += increment) - { - await pair.RunTicksSync(increment); - Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold)); - Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero)); - } - - //ticker.SetGamePreset((GamePresetPrototype?)null); WD edit - server.CfgMan.SetCVar(CCVars.GridFill, false); - await pair.CleanReturnAsync(); - } -} - -*/ diff --git a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs index ffaff3b8de..1e3f9c9854 100644 --- a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs @@ -1,6 +1,5 @@ using Content.Server.GameTicking; using Content.Server.GameTicking.Commands; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Shared.CCVar; diff --git a/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs b/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs index 5d7ae8efbf..0f665a63de 100644 --- a/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs @@ -17,7 +17,6 @@ public async Task TestSecretStarts() var server = pair.Server; await server.WaitIdleAsync(); - var entMan = server.ResolveDependency(); var gameTicker = server.ResolveDependency().GetEntitySystem(); await server.WaitAssertion(() => @@ -33,7 +32,10 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - Assert.That(gameTicker.GetAddedGameRules().Count(), Is.GreaterThan(1), $"No additional rules started by secret rule."); + foreach (var rule in gameTicker.GetAddedGameRules()) + { + Assert.That(gameTicker.GetActiveGameRules(), Does.Contain(rule)); + } // End all rules gameTicker.ClearGameRules(); diff --git a/Content.Server/Actions/ActionOnInteractSystem.cs b/Content.Server/Actions/ActionOnInteractSystem.cs index e226b7803b..c9a5f4b5d0 100644 --- a/Content.Server/Actions/ActionOnInteractSystem.cs +++ b/Content.Server/Actions/ActionOnInteractSystem.cs @@ -47,10 +47,7 @@ private void OnActivate(EntityUid uid, ActionOnInteractComponent component, Acti var (actId, act) = _random.Pick(options); if (act.Event != null) - { act.Event.Performer = args.User; - act.Event.Action = actId; - } _actions.PerformAction(args.User, null, actId, act, act.Event, _timing.CurTime, false); args.Handled = true; @@ -78,7 +75,6 @@ private void OnAfterInteract(EntityUid uid, ActionOnInteractComponent component, if (entAct.Event != null) { entAct.Event.Performer = args.User; - entAct.Event.Action = entActId; entAct.Event.Target = args.Target.Value; } @@ -104,7 +100,6 @@ private void OnAfterInteract(EntityUid uid, ActionOnInteractComponent component, if (act.Event != null) { act.Event.Performer = args.User; - act.Event.Action = actId; act.Event.Target = args.ClickLocation; } diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index 04fd38598f..6f10ef9b47 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Content.Server.Administration.Systems; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; using Content.Server.Maps; diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs index 108b4db0fb..9849d2df79 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs @@ -1,37 +1,23 @@ -using Content.Server.Administration.Commands; -using Content.Server.Antag; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.GameTicking.Rules; using Content.Server.Zombies; using Content.Shared.Administration; using Content.Shared.Database; +using Content.Shared.Humanoid; using Content.Shared.Mind.Components; -using Content.Shared.Roles; using Content.Shared.Verbs; using Robust.Shared.Player; -using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Administration.Systems; public sealed partial class AdminVerbSystem { - [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly ZombieSystem _zombie = default!; - - [ValidatePrototypeId] - private const string DefaultTraitorRule = "Traitor"; - - [ValidatePrototypeId] - private const string DefaultNukeOpRule = "LoneOpsSpawn"; - - [ValidatePrototypeId] - private const string DefaultRevsRule = "Revolutionary"; - - [ValidatePrototypeId] - private const string DefaultThiefRule = "Thief"; - - [ValidatePrototypeId] - private const string PirateGearId = "PirateGear"; + [Dependency] private readonly ThiefRuleSystem _thief = default!; + [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; + [Dependency] private readonly NukeopsRuleSystem _nukeopsRule = default!; + [Dependency] private readonly PiratesRuleSystem _piratesRule = default!; + [Dependency] private readonly RevolutionaryRuleSystem _revolutionaryRule = default!; // All antag verbs have names so invokeverb works. private void AddAntagVerbs(GetVerbsEvent args) @@ -54,7 +40,9 @@ private void AddAntagVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Structures/Wallmounts/posters.rsi"), "poster5_contraband"), Act = () => { - _antag.ForceMakeAntag(player, DefaultTraitorRule); + // if its a monkey or mouse or something dont give uplink or objectives + var isHuman = HasComp(args.Target); + _traitorRule.MakeTraitorAdmin(args.Target, giveUplink: isHuman, giveObjectives: isHuman); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-traitor"), @@ -83,7 +71,7 @@ private void AddAntagVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Wallmounts/signs.rsi"), "radiation"), Act = () => { - _antag.ForceMakeAntag(player, DefaultNukeOpRule); + _nukeopsRule.MakeLoneNukie(args.Target); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-nuclear-operative"), @@ -97,14 +85,14 @@ private void AddAntagVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Rsi(new("/Textures/Clothing/Head/Hats/pirate.rsi"), "icon"), Act = () => { - // pirates just get an outfit because they don't really have logic associated with them - SetOutfitCommand.SetOutfit(args.Target, PirateGearId, EntityManager); + _piratesRule.MakePirate(args.Target); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-pirate"), }; args.Verbs.Add(pirate); + //todo come here at some point dear lort. Verb headRev = new() { Text = Loc.GetString("admin-verb-text-make-head-rev"), @@ -112,7 +100,7 @@ private void AddAntagVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/job_icons.rsi"), "HeadRevolutionary"), Act = () => { - _antag.ForceMakeAntag(player, DefaultRevsRule); + _revolutionaryRule.OnHeadRevAdmin(args.Target); }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-head-rev"), @@ -126,26 +114,11 @@ private void AddAntagVerbs(GetVerbsEvent args) Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Clothing/Hands/Gloves/ihscombat.rsi"), "icon"), Act = () => { - _antag.ForceMakeAntag(player, DefaultThiefRule); + _thief.AdminMakeThief(args.Target, false); //Midround add pacified is bad }, Impact = LogImpact.High, Message = Loc.GetString("admin-verb-make-thief"), }; args.Verbs.Add(thief); - - // Goobstation - changelings - Verb ling = new() - { - Text = Loc.GetString("admin-verb-text-make-changeling"), - Category = VerbCategory.Antag, - Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Goobstation/Changeling/changeling_abilities.rsi"), "transform"), - Act = () => - { - _antag.ForceMakeAntag(player, "Changeling"); - }, - Impact = LogImpact.High, - Message = Loc.GetString("admin-verb-make-changeling"), - }; - args.Verbs.Add(ling); } } diff --git a/Content.Server/Antag/AntagSelectionPlayerPool.cs b/Content.Server/Antag/AntagSelectionPlayerPool.cs deleted file mode 100644 index 87873e96d1..0000000000 --- a/Content.Server/Antag/AntagSelectionPlayerPool.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Robust.Shared.Player; -using Robust.Shared.Random; - -namespace Content.Server.Antag; - -public sealed class AntagSelectionPlayerPool (List> orderedPools) -{ - public bool TryPickAndTake(IRobustRandom random, [NotNullWhen(true)] out ICommonSession? session) - { - session = null; - - foreach (var pool in orderedPools) - { - if (pool.Count == 0) - continue; - - session = random.PickAndTake(pool); - break; - } - - return session != null; - } - - public int Count => orderedPools.Sum(p => p.Count); -} diff --git a/Content.Server/Antag/AntagSelectionSystem.API.cs b/Content.Server/Antag/AntagSelectionSystem.API.cs deleted file mode 100644 index 470f98fca1..0000000000 --- a/Content.Server/Antag/AntagSelectionSystem.API.cs +++ /dev/null @@ -1,303 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Content.Server.Antag.Components; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Objectives; -using Content.Shared.Chat; -using Content.Shared.Mind; -using JetBrains.Annotations; -using Robust.Shared.Audio; -using Robust.Shared.Player; - -namespace Content.Server.Antag; - -public sealed partial class AntagSelectionSystem -{ - /// - /// Tries to get the next non-filled definition based on the current amount of selected minds and other factors. - /// - public bool TryGetNextAvailableDefinition(Entity ent, - [NotNullWhen(true)] out AntagSelectionDefinition? definition) - { - definition = null; - - var totalTargetCount = GetTargetAntagCount(ent); - var mindCount = ent.Comp.SelectedMinds.Count; - if (mindCount >= totalTargetCount) - return false; - - foreach (var def in ent.Comp.Definitions) - { - var target = GetTargetAntagCount(ent, null, def); - - if (mindCount < target) - { - definition = def; - return true; - } - - mindCount -= target; - } - - return false; - } - - /// - /// Gets the number of antagonists that should be present for a given rule based on the provided pool. - /// A null pool will simply use the player count. - /// - public int GetTargetAntagCount(Entity ent, AntagSelectionPlayerPool? pool = null) - { - var count = 0; - foreach (var def in ent.Comp.Definitions) - { - count += GetTargetAntagCount(ent, pool, def); - } - - return count; - } - - /// - /// Gets the number of antagonists that should be present for a given antag definition based on the provided pool. - /// A null pool will simply use the player count. - /// - public int GetTargetAntagCount(Entity ent, AntagSelectionPlayerPool? pool, AntagSelectionDefinition def) - { - var poolSize = pool?.Count ?? _playerManager.Sessions.Length; - // factor in other definitions' affect on the count. - var countOffset = 0; - foreach (var otherDef in ent.Comp.Definitions) - { - countOffset += Math.Clamp(poolSize / otherDef.PlayerRatio, otherDef.Min, otherDef.Max) * otherDef.PlayerRatio; - } - // make sure we don't double-count the current selection - countOffset -= Math.Clamp((poolSize + countOffset) / def.PlayerRatio, def.Min, def.Max) * def.PlayerRatio; - - return Math.Clamp((poolSize - countOffset) / def.PlayerRatio, def.Min, def.Max); - } - - /// - /// Returns identifiable information for all antagonists to be used in a round end summary. - /// - /// - /// A list containing, in order, the antag's mind, the session data, and the original name stored as a string. - /// - public List<(EntityUid, SessionData, string)> GetAntagIdentifiers(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return new List<(EntityUid, SessionData, string)>(); - - var output = new List<(EntityUid, SessionData, string)>(); - foreach (var (mind, name) in ent.Comp.SelectedMinds) - { - if (!TryComp(mind, out var mindComp) || mindComp.OriginalOwnerUserId == null) - continue; - - if (!_playerManager.TryGetPlayerData(mindComp.OriginalOwnerUserId.Value, out var data)) - continue; - - output.Add((mind, data, name)); - } - return output; - } - - /// - /// Returns all the minds of antagonists. - /// - public List> GetAntagMinds(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return new(); - - var output = new List>(); - foreach (var (mind, _) in ent.Comp.SelectedMinds) - { - if (!TryComp(mind, out var mindComp) || mindComp.OriginalOwnerUserId == null) - continue; - - output.Add((mind, mindComp)); - } - return output; - } - - /// - /// Helper specifically for - /// - public List GetAntagMindEntityUids(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return new(); - - return ent.Comp.SelectedMinds.Select(p => p.Item1).ToList(); - } - - /// - /// Returns all the antagonists for this rule who are currently alive - /// - public IEnumerable GetAliveAntags(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - yield break; - - var minds = GetAntagMinds(ent); - foreach (var mind in minds) - { - if (_mind.IsCharacterDeadIc(mind)) - continue; - - if (mind.Comp.OriginalOwnedEntity != null) - yield return GetEntity(mind.Comp.OriginalOwnedEntity.Value); - } - } - - /// - /// Returns the number of alive antagonists for this rule. - /// - public int GetAliveAntagCount(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return 0; - - var numbah = 0; - var minds = GetAntagMinds(ent); - foreach (var mind in minds) - { - if (_mind.IsCharacterDeadIc(mind)) - continue; - - numbah++; - } - - return numbah; - } - - /// - /// Returns if there are any remaining antagonists alive for this rule. - /// - public bool AnyAliveAntags(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return false; - - return GetAliveAntags(ent).Any(); - } - - /// - /// Checks if all the antagonists for this rule are alive. - /// - public bool AllAntagsAlive(Entity ent) - { - if (!Resolve(ent, ref ent.Comp, false)) - return false; - - return GetAliveAntagCount(ent) == ent.Comp.SelectedMinds.Count; - } - - /// - /// Helper method to send the briefing text and sound to a player entity - /// - /// The entity chosen to be antag - /// The briefing text to send - /// The color the briefing should be, null for default - /// The sound to briefing/greeting sound to play - public void SendBriefing(EntityUid entity, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) - { - if (!_mind.TryGetMind(entity, out _, out var mindComponent)) - return; - - if (mindComponent.Session == null) - return; - - SendBriefing(mindComponent.Session, briefing, briefingColor, briefingSound); - } - - /// - /// Helper method to send the briefing text and sound to a list of sessions - /// - /// The sessions that will be sent the briefing - /// The briefing text to send - /// The color the briefing should be, null for default - /// The sound to briefing/greeting sound to play - [PublicAPI] - public void SendBriefing(List sessions, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) - { - foreach (var session in sessions) - { - SendBriefing(session, briefing, briefingColor, briefingSound); - } - } - - /// - /// Helper method to send the briefing text and sound to a session - /// - /// The player chosen to be an antag - /// The briefing data - public void SendBriefing( - ICommonSession? session, - BriefingData? data) - { - if (session == null || data == null) - return; - - var text = data.Value.Text == null ? string.Empty : Loc.GetString(data.Value.Text); - SendBriefing(session, text, data.Value.Color, data.Value.Sound); - } - - /// - /// Helper method to send the briefing text and sound to a session - /// - /// The player chosen to be an antag - /// The briefing text to send - /// The color the briefing should be, null for default - /// The sound to briefing/greeting sound to play - public void SendBriefing( - ICommonSession? session, - string briefing, - Color? briefingColor, - SoundSpecifier? briefingSound) - { - if (session == null) - return; - - _audio.PlayGlobal(briefingSound, session); - if (!string.IsNullOrEmpty(briefing)) - { - var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", briefing)); - _chat.ChatMessageToOne(ChatChannel.Server, briefing, wrappedMessage, default, false, session.Channel, - briefingColor); - } - } - - /// - /// This technically is a gamerule-ent-less way to make an entity an antag. - /// You should almost never be using this. - /// - public void ForceMakeAntag(ICommonSession? player, string defaultRule) where T : Component - { - var rule = ForceGetGameRuleEnt(defaultRule); - - if (!TryGetNextAvailableDefinition(rule, out var def)) - def = rule.Comp.Definitions.Last(); - - MakeAntag(rule, player, def.Value); - } - - /// - /// Tries to grab one of the weird specific antag gamerule ents or starts a new one. - /// This is gross code but also most of this is pretty gross to begin with. - /// - public Entity ForceGetGameRuleEnt(string id) where T : Component - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var comp)) - { - return (uid, comp); - } - var ruleEnt = GameTicker.AddGameRule(id); - RemComp(ruleEnt); - var antag = Comp(ruleEnt); - antag.SelectionsComplete = true; // don't do normal selection. - GameTicker.StartGameRule(ruleEnt); - return (ruleEnt, antag); - } -} diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index 7f61585add..b11c562df5 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -1,469 +1,347 @@ -using System.Linq; -using Content.Server.Antag.Components; -using Content.Server.Chat.Managers; -using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; -using Content.Server.Ghost.Roles; -using Content.Server.Ghost.Roles.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Server.Mind; 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.Ghost; using Content.Shared.Humanoid; using Content.Shared.Players; using Content.Shared.Preferences; +using Content.Shared.Roles; using Robust.Server.Audio; -using Robust.Server.GameObjects; -using Robust.Server.Player; -using Robust.Shared.Enums; -using Robust.Shared.Map; +using Robust.Shared.Audio; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Random; +using System.Linq; +using Content.Shared.Chat; +using Robust.Shared.Enums; namespace Content.Server.Antag; -public sealed partial class AntagSelectionSystem : GameRuleSystem +public sealed class AntagSelectionSystem : GameRuleSystem { - [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 GhostRoleSystem _ghostRole = default!; + [Dependency] private readonly IServerPreferencesManager _prefs = default!; + [Dependency] private readonly AudioSystem _audioSystem = default!; [Dependency] private readonly JobSystem _jobs = default!; - [Dependency] private readonly MindSystem _mind = default!; - [Dependency] private readonly RoleSystem _role = default!; - [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; - [Dependency] private readonly TransformSystem _transform = default!; - - // arbitrary random number to give late joining some mild interest. - public const float LateJoinRandomChance = 0.5f; - - /// - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnTakeGhostRole); - - SubscribeLocalEvent(OnPlayerSpawning); - SubscribeLocalEvent(OnJobsAssigned); - SubscribeLocalEvent(OnSpawnComplete); - } + [Dependency] private readonly MindSystem _mindSystem = default!; + [Dependency] private readonly SharedRoleSystem _roleSystem = default!; - private void OnTakeGhostRole(Entity ent, ref TakeGhostRoleEvent args) + #region Eligible Player Selection + /// + /// Get all players that are eligible for an antag role + /// + /// All sessions from which to select eligible players + /// The prototype to get eligible players for + /// Should jobs that prohibit antag roles (ie Heads, Sec, Interns) be included + /// Should players already selected as antags be eligible + /// Should we ignore if the player has enabled this specific role + /// A custom condition that each player is tested against, if it returns true the player is excluded from eligibility + /// List of all player entities that match the requirements + public List GetEligiblePlayers(IEnumerable playerSessions, + ProtoId antagPrototype, + bool includeAllJobs = false, + AntagAcceptability acceptableAntags = AntagAcceptability.NotExclusive, + bool ignorePreferences = false, + bool allowNonHumanoids = false, + Func? customExcludeCondition = null) { - if (args.TookRole) - return; - - if (ent.Comp.Rule is not { } rule || ent.Comp.Definition is not { } def) - return; + var eligiblePlayers = new List(); - if (!Exists(rule) || !TryComp(rule, out var select)) - return; + foreach (var player in playerSessions) + { + if (IsPlayerEligible(player, antagPrototype, includeAllJobs, acceptableAntags, ignorePreferences, allowNonHumanoids, customExcludeCondition)) + eligiblePlayers.Add(player.AttachedEntity!.Value); + } - MakeAntag((rule, select), args.Player, def, ignoreSpawner: true); - args.TookRole = true; - _ghostRole.UnregisterGhostRole((ent, Comp(ent))); + return eligiblePlayers; } - private void OnPlayerSpawning(RulePlayerSpawningEvent args) + /// + /// Get all sessions that are eligible for an antag role, can be run prior to sessions being attached to an entity + /// This does not exclude sessions that have already been chosen as antags - that must be handled manually + /// + /// All sessions from which to select eligible players + /// The prototype to get eligible players for + /// Should we ignore if the player has enabled this specific role + /// List of all player sessions that match the requirements + public List GetEligibleSessions(IEnumerable playerSessions, ProtoId antagPrototype, bool ignorePreferences = false) { - var pool = args.PlayerPool; + var eligibleSessions = new List(); - var query = QueryActiveRules(); - while (query.MoveNext(out var uid, out _, out var comp, out _)) + foreach (var session in playerSessions) { - if (comp.SelectionTime != AntagSelectionTime.PrePlayerSpawn) - continue; - - if (comp.SelectionsComplete) - return; - - ChooseAntags((uid, comp), pool); - comp.SelectionsComplete = true; - - foreach (var session in comp.SelectedSessions) - { - args.PlayerPool.Remove(session); - GameTicker.PlayerJoinGame(session); - } + if (IsSessionEligible(session, antagPrototype, ignorePreferences)) + eligibleSessions.Add(session); } - } - - private void OnJobsAssigned(RulePlayerJobsAssignedEvent args) - { - var query = QueryActiveRules(); - while (query.MoveNext(out var uid, out _, out var comp, out _)) - { - if (comp.SelectionTime != AntagSelectionTime.PostPlayerSpawn) - continue; - - if (comp.SelectionsComplete) - continue; - ChooseAntags((uid, comp)); - comp.SelectionsComplete = true; - } + return eligibleSessions; } - private void OnSpawnComplete(PlayerSpawnCompleteEvent args) + /// + /// Test eligibility of the player for a specific antag role + /// + /// The player session to test + /// The prototype to get eligible players for + /// Should jobs that prohibit antag roles (ie Heads, Sec, Interns) be included + /// Should players already selected as antags be eligible + /// Should we ignore if the player has enabled this specific role + /// A function, accepting an EntityUid and returning bool. Each player is tested against this, returning truw will exclude the player from eligibility + /// True if the player session matches the requirements, false otherwise + public bool IsPlayerEligible(ICommonSession session, + ProtoId antagPrototype, + bool includeAllJobs = false, + AntagAcceptability acceptableAntags = AntagAcceptability.NotExclusive, + bool ignorePreferences = false, + bool allowNonHumanoids = false, + Func? customExcludeCondition = null) { - if (!args.LateJoin) - return; - - // TODO: this really doesn't handle multiple latejoin definitions well - // eventually this should probably store the players per definition with some kind of unique identifier. - // something to figure out later. - - var query = QueryActiveRules(); - while (query.MoveNext(out var uid, out _, out var antag, out _)) - { - if (!RobustRandom.Prob(LateJoinRandomChance)) - continue; + if (!IsSessionEligible(session, antagPrototype, ignorePreferences)) + return false; - if (!antag.Definitions.Any(p => p.LateJoinAdditional)) - continue; + //Ensure the player has a mind + if (session.GetMind() is not { } playerMind) + return false; - if (!TryGetNextAvailableDefinition((uid, antag), out var def)) - continue; + //Ensure the player has an attached entity + if (session.AttachedEntity is not { } playerEntity) + return false; - if (TryMakeAntag((uid, antag), args.Player, def.Value)) - break; - } - } + //Ignore latejoined players, ie those on the arrivals station + if (HasComp(playerEntity)) + return false; - protected override void Added(EntityUid uid, AntagSelectionComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) - { - base.Added(uid, component, gameRule, args); + //Exclude jobs that cannot be antag, unless explicitly allowed + if (!includeAllJobs && !_jobs.CanBeAntag(session)) + return false; - for (var i = 0; i < component.Definitions.Count; i++) + //Check if the entity is already an antag + switch (acceptableAntags) { - var def = component.Definitions[i]; - - if (def.MinRange != null) - { - def.Min = def.MinRange.Value.Next(RobustRandom); - } - - if (def.MaxRange != null) - { - def.Max = def.MaxRange.Value.Next(RobustRandom); - } + //If we dont want to select any antag roles + case AntagAcceptability.None: + { + if (_roleSystem.MindIsAntagonist(playerMind)) + return false; + break; + } + //If we dont want to select exclusive antag roles + case AntagAcceptability.NotExclusive: + { + if (_roleSystem.MindIsExclusiveAntagonist(playerMind)) + return false; + break; + } } - } - - protected override void Started(EntityUid uid, AntagSelectionComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) - { - base.Started(uid, component, gameRule, args); - if (component.SelectionsComplete) - return; + //Unless explictly allowed, ignore non humanoids (eg pets) + if (!allowNonHumanoids && !HasComp(playerEntity)) + return false; - if (GameTicker.RunLevel != GameRunLevel.InRound) - return; + //If a custom condition was provided, test it and exclude the player if it returns true + if (customExcludeCondition != null && customExcludeCondition(playerEntity)) + return false; - if (GameTicker.RunLevel == GameRunLevel.InRound && component.SelectionTime == AntagSelectionTime.PrePlayerSpawn) - return; - ChooseAntags((uid, component)); - component.SelectionsComplete = true; + return true; } /// - /// Chooses antagonists from the current selection of players + /// Check if the session is eligible for a role, can be run prior to the session being attached to an entity /// - public void ChooseAntags(Entity ent) + /// Player session to check + /// Which antag prototype to check for + /// Ignore if the player has enabled this antag + /// True if the session matches the requirements, false otherwise + public bool IsSessionEligible(ICommonSession session, ProtoId antagPrototype, bool ignorePreferences = false) { - var sessions = _playerManager.Sessions.ToList(); - ChooseAntags(ent, sessions); + //Exclude disconnected or zombie sessions + //No point giving antag roles to them + if (session.Status == SessionStatus.Disconnected || + session.Status == SessionStatus.Zombie) + return false; + + //Check the player has this antag preference selected + //Unless we are ignoring preferences, in which case add them anyway + var pref = (HumanoidCharacterProfile) _prefs.GetPreferences(session.UserId).SelectedCharacter; + if (!pref.AntagPreferences.Contains(antagPrototype.Id) && !ignorePreferences) + return false; + + return true; } + #endregion /// - /// Chooses antagonists from the given selection of players + /// Helper method to calculate the number of antags to select based upon the number of players /// - public void ChooseAntags(Entity ent, List pool) + /// How many players there are on the server + /// How many players should there be for an additional antag + /// Maximum number of antags allowed + /// The number of antags that should be chosen + public int CalculateAntagCount(int playerCount, int playersPerAntag, int maxAntags) { - foreach (var def in ent.Comp.Definitions) - { - ChooseAntags(ent, pool, def); - } + return Math.Clamp(playerCount / playersPerAntag, 1, maxAntags); } + #region Antag Selection /// - /// Chooses antagonists from the given selection of players for the given antag definition. + /// Selects a set number of entities from several lists, prioritising the first list till its empty, then second list etc /// - public void ChooseAntags(Entity ent, List pool, AntagSelectionDefinition def) + /// Array of lists, which are chosen from in order until the correct number of items are selected + /// How many items to select + /// Up to the specified count of elements from all provided lists + public List ChooseAntags(int count, params List[] eligiblePlayerLists) { - var playerPool = GetPlayerPool(ent, pool, def); - var count = GetTargetAntagCount(ent, playerPool, def); - - // if there is both a spawner and players getting picked, let it fall back to a spawner. - var noSpawner = def.SpawnerPrototype == null; - for (var i = 0; i < count; i++) + var chosenPlayers = new List(); + foreach (var playerList in eligiblePlayerLists) { - var session = (ICommonSession?) null; - if (def.PickPlayer) + //Remove all chosen players from this list, to prevent duplicates + foreach (var chosenPlayer in chosenPlayers) { - if (!playerPool.TryPickAndTake(RobustRandom, out session) && noSpawner) - { - Log.Warning($"Couldn't pick a player for {ToPrettyString(ent):rule}, no longer choosing antags for this definition"); - break; - } - - if (session != null && ent.Comp.SelectedSessions.Contains(session)) - { - Log.Warning($"Somehow picked {session} for an antag when this rule already selected them previously"); - continue; - } + playerList.Remove(chosenPlayer); } - MakeAntag(ent, session, def); + //If we have reached the desired number of players, skip + if (chosenPlayers.Count >= count) + continue; + + //Pick and choose a random number of players from this list + chosenPlayers.AddRange(ChooseAntags(count - chosenPlayers.Count, playerList)); } + return chosenPlayers; } - /// - /// Tries to makes a given player into the specified antagonist. + /// Helper method to choose antags from a list /// - public bool TryMakeAntag(Entity ent, ICommonSession? session, AntagSelectionDefinition def, bool ignoreSpawner = false) + /// List of eligible players + /// How many to choose + /// Up to the specified count of elements from the provided list + public List ChooseAntags(int count, List eligiblePlayers) { - if (!IsSessionValid(ent, session, def) || - !IsEntityValid(session?.AttachedEntity, def)) + var chosenPlayers = new List(); + + for (var i = 0; i < count; i++) { - return false; + if (eligiblePlayers.Count == 0) + break; + + chosenPlayers.Add(RobustRandom.PickAndTake(eligiblePlayers)); } - MakeAntag(ent, session, def, ignoreSpawner); - return true; + return chosenPlayers; } /// - /// Makes a given player into the specified antagonist. + /// Selects a set number of sessions from several lists, prioritising the first list till its empty, then second list etc /// - public void MakeAntag(Entity ent, ICommonSession? session, AntagSelectionDefinition def, bool ignoreSpawner = false) + /// Array of lists, which are chosen from in order until the correct number of items are selected + /// How many items to select + /// Up to the specified count of elements from all provided lists + public List ChooseAntags(int count, params List[] eligiblePlayerLists) { - var antagEnt = (EntityUid?) null; - var isSpawner = false; - - if (session != null) - { - ent.Comp.SelectedSessions.Add(session); - - // we shouldn't be blocking the entity if they're just a ghost or smth. - if (!HasComp(session.AttachedEntity)) - antagEnt = session.AttachedEntity; - } - else if (!ignoreSpawner && def.SpawnerPrototype != null) // don't add spawners if we have a player, dummy. - { - antagEnt = Spawn(def.SpawnerPrototype); - isSpawner = true; - } - - if (!antagEnt.HasValue) - { - var getEntEv = new AntagSelectEntityEvent(session, ent); - RaiseLocalEvent(ent, ref getEntEv, true); - - if (!getEntEv.Handled) - { - throw new InvalidOperationException($"Attempted to make {session} antagonist in gamerule {ToPrettyString(ent)} but there was no valid entity for player."); - } - - antagEnt = getEntEv.Entity; - } - - if (antagEnt is not { } player) - return; - - var getPosEv = new AntagSelectLocationEvent(session, ent); - RaiseLocalEvent(ent, ref getPosEv, true); - if (getPosEv.Handled) - { - var playerXform = Transform(player); - var pos = RobustRandom.Pick(getPosEv.Coordinates); - _transform.SetMapCoordinates((player, playerXform), pos); - } - - if (isSpawner) - { - if (!TryComp(player, out var spawnerComp)) - { - Log.Error("Antag spawner with GhostRoleAntagSpawnerComponent."); - return; - } - - spawnerComp.Rule = ent; - spawnerComp.Definition = def; - return; - } - - EntityManager.AddComponents(player, def.Components); - _stationSpawning.EquipStartingGear(player, def.StartingGear); - - if (session != null) + var chosenPlayers = new List(); + foreach (var playerList in eligiblePlayerLists) { - var curMind = session.GetMind(); - if (curMind == null) + //Remove all chosen players from this list, to prevent duplicates + foreach (var chosenPlayer in chosenPlayers) { - curMind = _mind.CreateMind(session.UserId, Name(antagEnt.Value)); - _mind.SetUserId(curMind.Value, session.UserId); + playerList.Remove(chosenPlayer); } - _mind.TransferTo(curMind.Value, antagEnt, ghostCheckOverride: true); - _role.MindAddRoles(curMind.Value, def.MindComponents); - ent.Comp.SelectedMinds.Add((curMind.Value, Name(player))); - } + //If we have reached the desired number of players, skip + if (chosenPlayers.Count >= count) + continue; - if (def.Briefing is { } briefing) - { - SendBriefing(session, briefing); + //Pick and choose a random number of players from this list + chosenPlayers.AddRange(ChooseAntags(count - chosenPlayers.Count, playerList)); } - - var afterEv = new AfterAntagEntitySelectedEvent(session, player, ent, def); - RaiseLocalEvent(ent, ref afterEv, true); + return chosenPlayers; } - /// - /// Gets an ordered player pool based on player preferences and the antagonist definition. + /// Helper method to choose sessions from a list /// - public AntagSelectionPlayerPool GetPlayerPool(Entity ent, List sessions, AntagSelectionDefinition def) + /// List of eligible sessions + /// How many to choose + /// Up to the specified count of elements from the provided list + public List ChooseAntags(int count, List eligiblePlayers) { - var preferredList = new List(); - var secondBestList = new List(); - var unwantedList = new List(); - var invalidList = new List(); - foreach (var session in sessions) + var chosenPlayers = new List(); + + for (int i = 0; i < count; i++) { - if (!IsSessionValid(ent, session, def) || - !IsEntityValid(session.AttachedEntity, def)) - { - invalidList.Add(session); - continue; - } + if (eligiblePlayers.Count == 0) + break; - var pref = (HumanoidCharacterProfile) _pref.GetPreferences(session.UserId).SelectedCharacter; - if (def.PrefRoles.Count != 0 && pref.AntagPreferences.Any(p => def.PrefRoles.Contains(p))) - { - preferredList.Add(session); - } - else if (def.FallbackRoles.Count != 0 && pref.AntagPreferences.Any(p => def.FallbackRoles.Contains(p))) - { - secondBestList.Add(session); - } - else - { - unwantedList.Add(session); - } + chosenPlayers.Add(RobustRandom.PickAndTake(eligiblePlayers)); } - return new AntagSelectionPlayerPool(new() { preferredList, secondBestList, unwantedList, invalidList }); + return chosenPlayers; } + #endregion + #region Briefings /// - /// Checks if a given session is valid for an antagonist. + /// Helper method to send the briefing text and sound to a list of entities /// - public bool IsSessionValid(Entity ent, ICommonSession? session, AntagSelectionDefinition def, EntityUid? mind = null) + /// The players chosen to be antags + /// The briefing text to send + /// The color the briefing should be, null for default + /// The sound to briefing/greeting sound to play + public void SendBriefing(List entities, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) { - if (session == null) - return true; - - mind ??= session.GetMind(); - - if (session.Status is SessionStatus.Disconnected or SessionStatus.Zombie) - return false; - - if (ent.Comp.SelectedSessions.Contains(session)) - return false; - - //todo: we need some way to check that we're not getting the same role twice. (double picking thieves or zombies through midrounds) - - switch (def.MultiAntagSetting) + foreach (var entity in entities) { - case AntagAcceptability.None: - { - if (_role.MindIsAntagonist(mind)) - return false; - break; - } - case AntagAcceptability.NotExclusive: - { - if (_role.MindIsExclusiveAntagonist(mind)) - return false; - break; - } + SendBriefing(entity, briefing, briefingColor, briefingSound); } - - // todo: expand this to allow for more fine antag-selection logic for game rules. - if (!_jobs.CanBeAntag(session)) - return false; - - return true; } /// - /// Checks if a given entity (mind/session not included) is valid for a given antagonist. + /// Helper method to send the briefing text and sound to a player entity /// - private bool IsEntityValid(EntityUid? entity, AntagSelectionDefinition def) + /// The entity chosen to be antag + /// The briefing text to send + /// The color the briefing should be, null for default + /// The sound to briefing/greeting sound to play + public void SendBriefing(EntityUid entity, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) { - if (entity == null) - return false; + if (!_mindSystem.TryGetMind(entity, out _, out var mindComponent)) + return; - if (HasComp(entity)) - return false; + if (mindComponent.Session == null) + return; - if (!def.AllowNonHumans && !HasComp(entity)) - return false; + SendBriefing(mindComponent.Session, briefing, briefingColor, briefingSound); + } - if (def.Whitelist != null) - { - if (!def.Whitelist.IsValid(entity.Value, EntityManager)) - return false; - } + /// + /// Helper method to send the briefing text and sound to a list of sessions + /// + /// + /// + /// + /// - if (def.Blacklist != null) + public void SendBriefing(List sessions, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) + { + foreach (var session in sessions) { - if (def.Blacklist.IsValid(entity.Value, EntityManager)) - return false; + SendBriefing(session, briefing, briefingColor, briefingSound); } - - return true; } -} - -/// -/// Event raised on a game rule entity in order to determine what the antagonist entity will be. -/// Only raised if the selected player's current entity is invalid. -/// -[ByRefEvent] -public record struct AntagSelectEntityEvent(ICommonSession? Session, Entity GameRule) -{ - public readonly ICommonSession? Session = Session; - - public bool Handled => Entity != null; - - public EntityUid? Entity; -} - -/// -/// Event raised on a game rule entity to determine the location for the antagonist. -/// -[ByRefEvent] -public record struct AntagSelectLocationEvent(ICommonSession? Session, Entity GameRule) -{ - public readonly ICommonSession? Session = Session; - - public bool Handled => Coordinates.Any(); + /// + /// Helper method to send the briefing text and sound to a session + /// + /// The player chosen to be an antag + /// The briefing text to send + /// The color the briefing should be, null for default + /// The sound to briefing/greeting sound to play - public List Coordinates = new(); + public void SendBriefing(ICommonSession session, string briefing, Color? briefingColor, SoundSpecifier? briefingSound) + { + _audioSystem.PlayGlobal(briefingSound, session); + var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", briefing)); + ChatManager.ChatMessageToOne(ChatChannel.Server, briefing, wrappedMessage, default, false, session.Channel, briefingColor); + } + #endregion } - -/// -/// Event raised on a game rule entity after the setup logic for an antag is complete. -/// Used for applying additional more complex setup logic. -/// -[ByRefEvent] -public readonly record struct AfterAntagEntitySelectedEvent(ICommonSession? Session, EntityUid EntityUid, Entity GameRule, AntagSelectionDefinition Def); diff --git a/Content.Server/Antag/Components/AntagSelectionComponent.cs b/Content.Server/Antag/Components/AntagSelectionComponent.cs deleted file mode 100644 index 4c32d9c2ca..0000000000 --- a/Content.Server/Antag/Components/AntagSelectionComponent.cs +++ /dev/null @@ -1,190 +0,0 @@ -using Content.Server.Administration.Systems; -using Content.Server.Destructible.Thresholds; -using Content.Shared.Antag; -using Content.Shared.Roles; -using Content.Shared.Storage; -using Content.Shared.Whitelist; -using Robust.Shared.Audio; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; - -namespace Content.Server.Antag.Components; - -[RegisterComponent, Access(typeof(AntagSelectionSystem), typeof(AdminVerbSystem))] -public sealed partial class AntagSelectionComponent : Component -{ - /// - /// Has the primary selection of antagonists finished yet? - /// - [DataField] - public bool SelectionsComplete; - - /// - /// The definitions for the antagonists - /// - [DataField] - public List Definitions = new(); - - /// - /// The minds and original names of the players selected to be antagonists. - /// - [DataField] - public List<(EntityUid, string)> SelectedMinds = new(); - - /// - /// When the antag selection will occur. - /// - [DataField] - public AntagSelectionTime SelectionTime = AntagSelectionTime.PostPlayerSpawn; - - /// - /// Cached sessions of players who are chosen. Used so we don't have to rebuild the pool multiple times in a tick. - /// Is not serialized. - /// - public HashSet SelectedSessions = new(); -} - -[DataDefinition] -public partial struct AntagSelectionDefinition() -{ - /// - /// A list of antagonist roles that are used for selecting which players will be antagonists. - /// - [DataField] - public List> PrefRoles = new(); - - /// - /// Fallback for . Useful if you need multiple role preferences for a team antagonist. - /// - [DataField] - public List> FallbackRoles = new(); - - /// - /// Should we allow people who already have an antagonist role? - /// - [DataField] - public AntagAcceptability MultiAntagSetting = AntagAcceptability.None; - - /// - /// The minimum number of this antag. - /// - [DataField] - public int Min = 1; - - /// - /// The maximum number of this antag. - /// - [DataField] - public int Max = 1; - - /// - /// A range used to randomly select - /// - [DataField] - public MinMax? MinRange; - - /// - /// A range used to randomly select - /// - [DataField] - public MinMax? MaxRange; - - /// - /// a player to antag ratio: used to determine the amount of antags that will be present. - /// - [DataField] - public int PlayerRatio = 10; - - /// - /// Whether or not players should be picked to inhabit this antag or not. - /// If no players are left and is set, it will make a ghost role. - /// - [DataField] - public bool PickPlayer = true; - - /// - /// If true, players that latejoin into a round have a chance of being converted into antagonists. - /// - [DataField] - public bool LateJoinAdditional = false; - - //todo: find out how to do this with minimal boilerplate: filler department, maybe? - //public HashSet> JobBlacklist = new() - - /// - /// Mostly just here for legacy compatibility and reducing boilerplate - /// - [DataField] - public bool AllowNonHumans = false; - - /// - /// A whitelist for selecting which players can become this antag. - /// - [DataField] - public EntityWhitelist? Whitelist; - - /// - /// A blacklist for selecting which players can become this antag. - /// - [DataField] - public EntityWhitelist? Blacklist; - - /// - /// Components added to the player. - /// - [DataField] - public ComponentRegistry Components = new(); - - /// - /// Components added to the player's mind. - /// - [DataField] - public ComponentRegistry MindComponents = new(); - - /// - /// A set of starting gear that's equipped to the player. - /// - [DataField] - public ProtoId? StartingGear; - - /// - /// A briefing shown to the player. - /// - [DataField] - public BriefingData? Briefing; - - /// - /// A spawner used to defer the selection of this particular definition. - /// - /// - /// Not the cleanest way of doing this code but it's just an odd specific behavior. - /// Sue me. - /// - [DataField] - public EntProtoId? SpawnerPrototype; -} - -/// -/// Contains data used to generate a briefing. -/// -[DataDefinition] -public partial struct BriefingData -{ - /// - /// The text shown - /// - [DataField] - public LocId? Text; - - /// - /// The color of the text. - /// - [DataField] - public Color? Color; - - /// - /// The sound played. - /// - [DataField] - public SoundSpecifier? Sound; -} diff --git a/Content.Server/Antag/Components/GhostRoleAntagSpawnerComponent.cs b/Content.Server/Antag/Components/GhostRoleAntagSpawnerComponent.cs deleted file mode 100644 index fcaa4d4267..0000000000 --- a/Content.Server/Antag/Components/GhostRoleAntagSpawnerComponent.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Content.Server.Antag.Components; - -/// -/// Ghost role spawner that creates an antag for the associated gamerule. -/// -[RegisterComponent, Access(typeof(AntagSelectionSystem))] -public sealed partial class GhostRoleAntagSpawnerComponent : Component -{ - [DataField] - public EntityUid? Rule; - - [DataField] - public AntagSelectionDefinition? Definition; -} diff --git a/Content.Server/Antag/MobReplacementRuleSystem.cs b/Content.Server/Antag/MobReplacementRuleSystem.cs index 18837b5a7c..ba09c84bce 100644 --- a/Content.Server/Antag/MobReplacementRuleSystem.cs +++ b/Content.Server/Antag/MobReplacementRuleSystem.cs @@ -1,16 +1,45 @@ +using System.Numerics; +using Content.Server.Advertise.Components; +using Content.Server.Advertise.EntitySystems; using Content.Server.Antag.Mimic; -using Content.Server.GameTicking.Components; +using Content.Server.Chat.Systems; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; +using Content.Server.NPC.Systems; +using Content.Server.Station.Systems; +using Content.Server.GameTicking; using Content.Shared.VendingMachines; using Robust.Shared.Map; +using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Server.GameObjects; +using Robust.Shared.Physics.Systems; +using System.Linq; +using Robust.Shared.Physics; +using Content.Shared.Movement.Components; +using Content.Shared.Damage; +using Content.Server.NPC.HTN; +using Content.Server.NPC; +using Content.Shared.Weapons.Melee; +using Content.Server.Power.Components; +using Content.Shared.CombatMode; namespace Content.Server.Antag; public sealed class MobReplacementRuleSystem : GameRuleSystem { [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IComponentFactory _componentFactory = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly NpcFactionSystem _npcFaction = default!; + [Dependency] private readonly NPCSystem _npc = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly AdvertiseSystem _advertise = default!; + protected override void Started(EntityUid uid, MobReplacementRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { @@ -18,21 +47,133 @@ protected override void Started(EntityUid uid, MobReplacementRuleComponent compo var query = AllEntityQuery(); var spawns = new List<(EntityUid Entity, EntityCoordinates Coordinates)>(); + var stations = _gameTicker.GetSpawnableStations(); while (query.MoveNext(out var vendingUid, out _, out var xform)) { - if (!_random.Prob(component.Chance)) + var ownerStation = _station.GetOwningStation(vendingUid); + + if (ownerStation == null + || ownerStation != stations[0]) + continue; + + // Make sure that we aren't running this on something that is already a mimic + if (HasComp(vendingUid)) continue; spawns.Add((vendingUid, xform.Coordinates)); } - foreach (var entity in spawns) + if (spawns == null) { - var coordinates = entity.Coordinates; - Del(entity.Entity); + //WTF THE STATION DOESN'T EXIST! WE MUST BE IN A TEST! QUICK, PUT A MIMIC AT 0,0!!! + Spawn(component.Proto, new EntityCoordinates(uid, new Vector2(0, 0))); + } + else + { + // This is intentionally not clamped. If a server host wants to replace every vending machine in the entire station with a mimic, who am I to stop them? + var k = MathF.MaxMagnitude(component.NumberToReplace, 1); + while (k > 0 && spawns != null && spawns.Count > 0) + { + if (k > 1) + { + var spawnLocation = _random.PickAndTake(spawns); + BuildAMimicWorkshop(spawnLocation.Entity, component); + } + else + { + BuildAMimicWorkshop(spawns[0].Entity, component); + } + + if (k == MathF.MaxMagnitude(component.NumberToReplace, 1) + && component.DoAnnouncement) + _chat.DispatchStationAnnouncement(stations[0], Loc.GetString("station-event-rampant-intelligence-announcement"), playDefaultSound: true, + colorOverride: Color.Red, sender: "Central Command"); + + k--; + } + } + } + + /// + /// It's like Build a Bear, but MURDER + /// + /// + public void BuildAMimicWorkshop(EntityUid uid, MobReplacementRuleComponent component) + { + var metaData = MetaData(uid); + var vendorPrototype = metaData.EntityPrototype; + var mimicProto = _prototype.Index(component.Proto); + + var vendorComponents = vendorPrototype?.Components.Keys + .Where(n => n != "Transform" && n != "MetaData") + .Select(name => (name, _componentFactory.GetRegistration(name).Type)) + .ToList() ?? new List<(string name, Type type)>(); + + var mimicComponents = mimicProto?.Components.Keys + .Where(n => n != "Transform" && n != "MetaData") + .Select(name => (name, _componentFactory.GetRegistration(name).Type)) + .ToList() ?? new List<(string name, Type type)>(); - Spawn(component.Proto, coordinates); + foreach (var name in mimicComponents.Except(vendorComponents)) + { + var newComponent = _componentFactory.GetComponent(name.name); + EntityManager.AddComponent(uid, newComponent); } + + var xform = Transform(uid); + if (xform.Anchored) + _transform.Unanchor(uid, xform); + + SetupMimicNPC(uid, component); + + if (TryComp(uid, out var vendor) + && component.VendorModify) + SetupMimicVendor(uid, component, vendor); + } + /// + /// This handles getting the entity ready to be a hostile NPC + /// + /// + /// + private void SetupMimicNPC(EntityUid uid, MobReplacementRuleComponent component) + { + _physics.SetBodyType(uid, BodyType.KinematicController); + _npcFaction.AddFaction(uid, "SimpleHostile"); + + var melee = EnsureComp(uid); + melee.Angle = 0; + DamageSpecifier dspec = new() + { + DamageDict = new() + { + { "Blunt", component.MimicMeleeDamage } + } + }; + melee.Damage = dspec; + + var movementSpeed = EnsureComp(uid); + (movementSpeed.BaseSprintSpeed, movementSpeed.BaseWalkSpeed) = (component.MimicMoveSpeed, component.MimicMoveSpeed); + + var htn = EnsureComp(uid); + htn.RootTask = new HTNCompoundTask() { Task = component.MimicAIType }; + htn.Blackboard.SetValue(NPCBlackboard.NavSmash, component.MimicSmashGlass); + _npc.WakeNPC(uid, htn); + } + + /// + /// Handling specific interactions with vending machines + /// + /// + /// + /// + private void SetupMimicVendor(EntityUid uid, MobReplacementRuleComponent mimicComponent, AdvertiseComponent vendorComponent) + { + vendorComponent.MinimumWait = 5; + vendorComponent.MaximumWait = 15; + _advertise.SayAdvertisement(uid, vendorComponent); + + if (TryComp(uid, out var aPC)) + aPC.NeedsPower = false; } } diff --git a/Content.Server/DeltaV/ParadoxAnomaly/Systems/ParadoxAnomalySystem.cs b/Content.Server/DeltaV/ParadoxAnomaly/Systems/ParadoxAnomalySystem.cs index abdc950020..62d994dac3 100644 --- a/Content.Server/DeltaV/ParadoxAnomaly/Systems/ParadoxAnomalySystem.cs +++ b/Content.Server/DeltaV/ParadoxAnomaly/Systems/ParadoxAnomalySystem.cs @@ -144,7 +144,7 @@ private bool TrySpawnParadoxAnomaly(string rule, [NotNullWhen(true)] out EntityU if (job.StartingGear != null && _proto.TryIndex(job.StartingGear, out var gear)) { - _stationSpawning.EquipStartingGear(spawned, gear); + _stationSpawning.EquipStartingGear(spawned, gear, profile); _stationSpawning.EquipIdCard(spawned, profile.Name, job, diff --git a/Content.Server/DeltaV/StationEvents/Events/GlimmerMobSpawnRule.cs b/Content.Server/DeltaV/StationEvents/Events/GlimmerMobSpawnRule.cs index 1cb3809e21..22d96a5414 100644 --- a/Content.Server/DeltaV/StationEvents/Events/GlimmerMobSpawnRule.cs +++ b/Content.Server/DeltaV/StationEvents/Events/GlimmerMobSpawnRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Robust.Shared.Random; using Content.Server.GameTicking.Rules.Components; using Content.Server.NPC.Components; diff --git a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs b/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs index c5d199164b..ba042d8966 100644 --- a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs +++ b/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs @@ -19,7 +19,6 @@ using Content.Shared.Salvage; using Content.Shared.Random.Helpers; using System.Linq; -using Content.Server.GameTicking.Components; using Content.Shared.CCVar; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/Destructible/Thresholds/MinMax.cs b/Content.Server/Destructible/Thresholds/MinMax.cs index c44864183a..b438e7c0e8 100644 --- a/Content.Server/Destructible/Thresholds/MinMax.cs +++ b/Content.Server/Destructible/Thresholds/MinMax.cs @@ -1,6 +1,4 @@ -using Robust.Shared.Random; - -namespace Content.Server.Destructible.Thresholds +namespace Content.Server.Destructible.Thresholds { [Serializable] [DataDefinition] @@ -11,16 +9,5 @@ public partial struct MinMax [DataField("max")] public int Max; - - public MinMax(int min, int max) - { - Min = min; - Max = max; - } - - public int Next(IRobustRandom random) - { - return random.Next(Min, Max + 1); - } } } diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs index 48a6597349..a04f274491 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -5,9 +5,9 @@ using Content.Server.Afk; using Content.Server.Chat.Managers; using Content.Server.Connection; +using Content.Server.DiscordAuth; using Content.Server.JoinQueue; using Content.Server.Database; -using Content.Server.DiscordAuth; using Content.Server.EUI; using Content.Server.GameTicking; using Content.Server.GhostKick; diff --git a/Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs b/Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs deleted file mode 100644 index de4be83627..0000000000 --- a/Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; - -namespace Content.Server.GameTicking.Components; - -/// -/// Generic component used to track a gamerule that's start has been delayed. -/// -[RegisterComponent, AutoGenerateComponentPause] -public sealed partial class DelayedStartRuleComponent : Component -{ - /// - /// The time at which the rule will start properly. - /// - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField] - public TimeSpan RuleStartTime; -} diff --git a/Content.Server/GameTicking/GameTicker.GameRule.cs b/Content.Server/GameTicking/GameTicker.GameRule.cs index f52a3cb296..4ebe946af4 100644 --- a/Content.Server/GameTicking/GameTicker.GameRule.cs +++ b/Content.Server/GameTicking/GameTicker.GameRule.cs @@ -1,6 +1,6 @@ using System.Linq; using Content.Server.Administration; -using Content.Server.GameTicking.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Shared.Administration; using Content.Shared.Database; using Content.Shared.Prototypes; @@ -102,22 +102,6 @@ public bool StartGameRule(EntityUid ruleEntity, GameRuleComponent? ruleData = nu if (MetaData(ruleEntity).EntityPrototype?.ID is not { } id) // you really fucked up return false; - // If we already have it, then we just skip the delay as it has already happened. - if (!RemComp(ruleEntity) && ruleData.Delay != null) - { - var delayTime = TimeSpan.FromSeconds(ruleData.Delay.Value.Next(_robustRandom)); - - if (delayTime > TimeSpan.Zero) - { - _sawmill.Info($"Queued start for game rule {ToPrettyString(ruleEntity)} with delay {delayTime}"); - _adminLogger.Add(LogType.EventStarted, $"Queued start for game rule {ToPrettyString(ruleEntity)} with delay {delayTime}"); - - var delayed = EnsureComp(ruleEntity); - delayed.RuleStartTime = _gameTiming.CurTime + (delayTime); - return true; - } - } - _allPreviousGameRules.Add((RoundDuration(), id)); _sawmill.Info($"Started game rule {ToPrettyString(ruleEntity)}"); _adminLogger.Add(LogType.EventStarted, $"Started game rule {ToPrettyString(ruleEntity)}"); @@ -271,18 +255,6 @@ public IEnumerable GetAllGameRulePrototypes() } } - private void UpdateGameRules() - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var delay, out var rule)) - { - if (_gameTiming.CurTime < delay.RuleStartTime) - continue; - - StartGameRule(uid, rule); - } - } - #region Command Implementations [AdminCommand(AdminFlags.Fun)] @@ -351,3 +323,38 @@ private void ClearGameRulesCommand(IConsoleShell shell, string argstr, string[] #endregion } + +/* +/// +/// Raised broadcast when a game rule is selected, but not started yet. +/// +public sealed class GameRuleAddedEvent +{ + public GameRulePrototype Rule { get; } + + public GameRuleAddedEvent(GameRulePrototype rule) + { + Rule = rule; + } +} + +public sealed class GameRuleStartedEvent +{ + public GameRulePrototype Rule { get; } + + public GameRuleStartedEvent(GameRulePrototype rule) + { + Rule = rule; + } +} + +public sealed class GameRuleEndedEvent +{ + public GameRulePrototype Rule { get; } + + public GameRuleEndedEvent(GameRulePrototype rule) + { + Rule = rule; + } +} +*/ diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index fa23312268..efda3df0ca 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -133,7 +133,6 @@ public override void Update(float frameTime) return; base.Update(frameTime); UpdateRoundFlow(frameTime); - UpdateGameRules(); } } } diff --git a/Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ActiveGameRuleComponent.cs similarity index 84% rename from Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs rename to Content.Server/GameTicking/Rules/Components/ActiveGameRuleComponent.cs index b9e6fa5d4b..956768bdd9 100644 --- a/Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/ActiveGameRuleComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.GameTicking.Components; +namespace Content.Server.GameTicking.Rules.Components; /// /// Added to game rules before and removed before . diff --git a/Content.Server/GameTicking/Components/EndedGameRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/EndedGameRuleComponent.cs similarity index 81% rename from Content.Server/GameTicking/Components/EndedGameRuleComponent.cs rename to Content.Server/GameTicking/Rules/Components/EndedGameRuleComponent.cs index 3234bfff3a..4484abd4d0 100644 --- a/Content.Server/GameTicking/Components/EndedGameRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/EndedGameRuleComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.GameTicking.Components; +namespace Content.Server.GameTicking.Rules.Components; /// /// Added to game rules before . diff --git a/Content.Server/GameTicking/Components/GameRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/GameRuleComponent.cs similarity index 83% rename from Content.Server/GameTicking/Components/GameRuleComponent.cs rename to Content.Server/GameTicking/Rules/Components/GameRuleComponent.cs index 1e6c3f0ab1..6309b97402 100644 --- a/Content.Server/GameTicking/Components/GameRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/GameRuleComponent.cs @@ -1,7 +1,6 @@ -using Content.Server.Destructible.Thresholds; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.GameTicking.Components; +namespace Content.Server.GameTicking.Rules.Components; /// /// Component attached to all gamerule entities. @@ -21,12 +20,6 @@ public sealed partial class GameRuleComponent : Component /// [DataField] public int MinPlayers; - - /// - /// A delay for when the rule the is started and when the starting logic actually runs. - /// - [DataField] - public MinMax? Delay; } /// diff --git a/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs deleted file mode 100644 index 463aecbff5..0000000000 --- a/Content.Server/GameTicking/Rules/Components/LoadMapRuleComponent.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Content.Server.Maps; -using Content.Shared.Whitelist; -using Robust.Shared.Map; -using Robust.Shared.Prototypes; -using Robust.Shared.Utility; - -namespace Content.Server.GameTicking.Rules.Components; - -/// -/// This is used for a game rule that loads a map when activated. -/// -[RegisterComponent] -public sealed partial class LoadMapRuleComponent : Component -{ - [DataField] - public MapId? Map; - - [DataField] - public ProtoId? GameMap ; - - [DataField] - public ResPath? MapPath; - - [DataField] - public List MapGrids = new(); - - [DataField] - public EntityWhitelist? SpawnerWhitelist; -} diff --git a/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs index fa352eb320..e6966c1e37 100644 --- a/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs @@ -8,7 +8,7 @@ namespace Content.Server.GameTicking.Rules.Components; /// /// Stores some configuration used by the ninja system. -/// Objectives and roundend summary are handled by . +/// Objectives and roundend summary are handled by . /// [RegisterComponent, Access(typeof(SpaceNinjaSystem))] public sealed partial class NinjaRuleComponent : Component diff --git a/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs b/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs index bb1b7c8746..e02d90c18b 100644 --- a/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NukeOperativeSpawnerComponent.cs @@ -1,3 +1,6 @@ +using Content.Shared.Roles; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + namespace Content.Server.GameTicking.Rules.Components; /// @@ -6,5 +9,11 @@ namespace Content.Server.GameTicking.Rules.Components; /// TODO: Remove once systems can request spawns from the ghost role system directly. /// [RegisterComponent] -public sealed partial class NukeOperativeSpawnerComponent : Component; +public sealed partial class NukeOperativeSpawnerComponent : Component +{ + [DataField("name", required:true)] + public string OperativeName = default!; + [DataField] + public NukeopSpawnPreset SpawnDetails = default!; +} diff --git a/Content.Server/GameTicking/Rules/Components/NukeOpsShuttleComponent.cs b/Content.Server/GameTicking/Rules/Components/NukeOpsShuttleComponent.cs index 3d097cd7c7..358b157cdf 100644 --- a/Content.Server/GameTicking/Rules/Components/NukeOpsShuttleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NukeOpsShuttleComponent.cs @@ -6,6 +6,4 @@ [RegisterComponent] public sealed partial class NukeOpsShuttleComponent : Component { - [DataField] - public EntityUid AssociatedRule; } diff --git a/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs index f64947e286..8efd61b469 100644 --- a/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/NukeopsRuleComponent.cs @@ -1,9 +1,10 @@ using Content.Server.Maps; using Content.Server.NPC.Components; using Content.Server.RoundEnd; +using Content.Server.StationEvents.Events; using Content.Shared.Dataset; using Content.Shared.Roles; -using Robust.Shared.Audio; +using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; @@ -13,9 +14,18 @@ namespace Content.Server.GameTicking.Rules.Components; -[RegisterComponent, Access(typeof(NukeopsRuleSystem))] +[RegisterComponent, Access(typeof(NukeopsRuleSystem), typeof(LoneOpsSpawnRule))] public sealed partial class NukeopsRuleComponent : Component { + /// + /// This INCLUDES the operatives. So a value of 3 is satisfied by 2 players & 1 operative + /// + [DataField] + public int PlayersPerOperative = 10; + + [DataField] + public int MaxOps = 5; + /// /// What will happen if all of the nuclear operatives will die. Used by LoneOpsSpawn event. /// @@ -46,6 +56,12 @@ public sealed partial class NukeopsRuleComponent : Component [DataField] public TimeSpan EvacShuttleTime = TimeSpan.FromMinutes(3); + /// + /// Whether or not to spawn the nuclear operative outpost. Used by LoneOpsSpawn event. + /// + [DataField] + public bool SpawnOutpost = true; + /// /// Whether or not nukie left their outpost /// @@ -68,7 +84,7 @@ public sealed partial class NukeopsRuleComponent : Component /// This amount of TC will be given to each nukie /// [DataField] - public int WarTcAmountPerNukie = 40; + public int WarTCAmountPerNukie = 40; /// /// Delay between war declaration and nuke ops arrival on station map. Gives crew time to prepare @@ -83,22 +99,48 @@ public sealed partial class NukeopsRuleComponent : Component public int WarDeclarationMinOps = 4; [DataField] - public WinType WinType = WinType.Neutral; + public EntProtoId SpawnPointProto = "SpawnPointNukies"; [DataField] - public List WinConditions = new (); + public EntProtoId GhostSpawnPointProto = "SpawnPointGhostNukeOperative"; [DataField] - public EntityUid? TargetStation; + public string OperationName = "Test Operation"; [DataField] - public ProtoId Faction = "Syndicate"; + public ProtoId OutpostMapPrototype = "NukieOutpost"; + + [DataField] + public WinType WinType = WinType.Neutral; + + [DataField] + public List WinConditions = new (); + + public MapId? NukiePlanet; + + // TODO: use components, don't just cache entity UIDs + // There have been (and probably still are) bugs where these refer to deleted entities from old rounds. + public EntityUid? NukieOutpost; + public EntityUid? NukieShuttle; + public EntityUid? TargetStation; /// - /// Path to antagonist alert sound. + /// Data to be used in for an operative once the Mind has been added. /// [DataField] - public SoundSpecifier GreetSoundNotification = new SoundPathSpecifier("/Audio/Ambience/Antag/nukeops_start.ogg"); + public Dictionary OperativeMindPendingData = new(); + + [DataField(required: true)] + public ProtoId Faction = default!; + + [DataField] + public NukeopSpawnPreset CommanderSpawnDetails = new() { AntagRoleProto = "NukeopsCommander", GearProto = "SyndicateCommanderGearFull", NamePrefix = "nukeops-role-commander", NameList = "SyndicateNamesElite" }; + + [DataField] + public NukeopSpawnPreset AgentSpawnDetails = new() { AntagRoleProto = "NukeopsMedic", GearProto = "SyndicateOperativeMedicFull", NamePrefix = "nukeops-role-agent", NameList = "SyndicateNamesNormal" }; + + [DataField] + public NukeopSpawnPreset OperativeSpawnDetails = new(); } /// diff --git a/Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs new file mode 100644 index 0000000000..1d03b41d77 --- /dev/null +++ b/Content.Server/GameTicking/Rules/Components/PiratesRuleComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.Audio; + +namespace Content.Server.GameTicking.Rules.Components; + +[RegisterComponent, Access(typeof(PiratesRuleSystem))] +public sealed partial class PiratesRuleComponent : Component +{ + [ViewVariables] + public List Pirates = new(); + [ViewVariables] + public EntityUid PirateShip = EntityUid.Invalid; + [ViewVariables] + public HashSet InitialItems = new(); + [ViewVariables] + public double InitialShipValue; + + /// + /// Path to antagonist alert sound. + /// + [DataField("pirateAlertSound")] + public SoundSpecifier PirateAlertSound = new SoundPathSpecifier( + "/Audio/Ambience/Antag/pirate_start.ogg", + AudioParams.Default.WithVolume(4)); +} diff --git a/Content.Server/GameTicking/Rules/Components/RevolutionaryRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/RevolutionaryRuleComponent.cs index 3b19bbffb6..2ce3f1f9a6 100644 --- a/Content.Server/GameTicking/Rules/Components/RevolutionaryRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/RevolutionaryRuleComponent.cs @@ -22,6 +22,43 @@ public sealed partial class RevolutionaryRuleComponent : Component [DataField] public TimeSpan TimerWait = TimeSpan.FromSeconds(20); + /// + /// Stores players minds + /// + [DataField] + public Dictionary HeadRevs = new(); + + [DataField] + public ProtoId HeadRevPrototypeId = "HeadRev"; + + /// + /// Min players needed for Revolutionary gamemode to start. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MinPlayers = 15; + + /// + /// Max Head Revs allowed during selection. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int MaxHeadRevs = 3; + + /// + /// The amount of Head Revs that will spawn per this amount of players. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public int PlayersPerHeadRev = 15; + + /// + /// The gear head revolutionaries are given on spawn. + /// + [DataField] + public List StartingGear = new() + { + "Flash", + "ClothingEyesGlassesSunglasses" + }; + /// /// The time it takes after the last head is killed for the shuttle to arrive. /// diff --git a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs index 01a078625a..9dfd6e6627 100644 --- a/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/ThiefRuleComponent.cs @@ -1,11 +1,12 @@ using Content.Shared.Random; +using Content.Shared.Roles; using Robust.Shared.Audio; using Robust.Shared.Prototypes; namespace Content.Server.GameTicking.Rules.Components; /// -/// Stores data for . +/// Stores data for . /// [RegisterComponent, Access(typeof(ThiefRuleSystem))] public sealed partial class ThiefRuleComponent : Component @@ -22,9 +23,42 @@ public sealed partial class ThiefRuleComponent : Component [DataField] public float BigObjectiveChance = 0.7f; + /// + /// Add a Pacified comp to thieves + /// + [DataField] + public bool PacifistThieves = true; + + [DataField] + public ProtoId ThiefPrototypeId = "Thief"; + [DataField] public float MaxObjectiveDifficulty = 2.5f; [DataField] public int MaxStealObjectives = 10; + + /// + /// Things that will be given to thieves + /// + [DataField] + public List StarterItems = new() { "ToolboxThief", "ClothingHandsChameleonThief" }; + + /// + /// All Thieves created by this rule + /// + [DataField] + public List ThievesMinds = new(); + + /// + /// Max Thiefs created by rule on roundstart + /// + [DataField] + public int MaxAllowThief = 3; + + /// + /// Sound played when making the player a thief via antag control or ghost role + /// + [DataField] + public SoundSpecifier? GreetingSound = new SoundPathSpecifier("/Audio/Misc/thief_greeting.ogg"); } diff --git a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs index dd359969b6..62619db76a 100644 --- a/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/TraitorRuleComponent.cs @@ -57,19 +57,4 @@ public enum SelectionState /// [DataField] public SoundSpecifier GreetSoundNotification = new SoundPathSpecifier("/Audio/Ambience/Antag/traitor_start.ogg"); - - /// - /// The amount of codewords that are selected. - /// - [DataField] - public int CodewordCount = 4; - - /// - /// The amount of TC traitors start with. - /// - [DataField] - public int StartingBalance = 20; - - [DataField] - public int MaxDifficulty = 5; } diff --git a/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs index 59d1940eaf..4fe91e3a5f 100644 --- a/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs +++ b/Content.Server/GameTicking/Rules/Components/ZombieRuleComponent.cs @@ -8,6 +8,12 @@ namespace Content.Server.GameTicking.Rules.Components; [RegisterComponent, Access(typeof(ZombieRuleSystem))] public sealed partial class ZombieRuleComponent : Component { + [DataField] + public Dictionary InitialInfectedNames = new(); + + [DataField] + public ProtoId PatientZeroPrototypeId = "InitialInfected"; + /// /// When the round will next check for round end. /// @@ -20,9 +26,61 @@ public sealed partial class ZombieRuleComponent : Component [DataField] public TimeSpan EndCheckDelay = TimeSpan.FromSeconds(30); + /// + /// The time at which the initial infected will be chosen. + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan? StartTime; + + /// + /// The minimum amount of time after the round starts that the initial infected will be chosen. + /// + [DataField] + public TimeSpan MinStartDelay = TimeSpan.FromMinutes(10); + + /// + /// The maximum amount of time after the round starts that the initial infected will be chosen. + /// + [DataField] + public TimeSpan MaxStartDelay = TimeSpan.FromMinutes(15); + + /// + /// The sound that plays when someone becomes an initial infected. + /// todo: this should have a unique sound instead of reusing the zombie one. + /// + [DataField] + public SoundSpecifier InitialInfectedSound = new SoundPathSpecifier("/Audio/Ambience/Antag/zombie_start.ogg"); + + /// + /// The minimum amount of time initial infected have before they start taking infection damage. + /// + [DataField] + public TimeSpan MinInitialInfectedGrace = TimeSpan.FromMinutes(12.5f); + + /// + /// The maximum amount of time initial infected have before they start taking damage. + /// + [DataField] + public TimeSpan MaxInitialInfectedGrace = TimeSpan.FromMinutes(15f); + + /// + /// How many players for each initial infected. + /// + [DataField] + public int PlayersPerInfected = 10; + + /// + /// The maximum number of initial infected. + /// + [DataField] + public int MaxInitialInfected = 6; + /// /// After this amount of the crew become zombies, the shuttle will be automatically called. /// [DataField] public float ZombieShuttleCallPercentage = 0.7f; + + [DataField] + public EntProtoId ZombifySelfActionPrototype = "ActionTurnUndead"; } diff --git a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs index 78b8a8a85c..82ac755592 100644 --- a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs @@ -1,6 +1,5 @@ using System.Linq; using Content.Server.Administration.Commands; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.KillTracking; using Content.Server.Mind; @@ -34,6 +33,7 @@ public override void Initialize() SubscribeLocalEvent(OnSpawnComplete); SubscribeLocalEvent(OnKillReported); SubscribeLocalEvent(OnPointChanged); + SubscribeLocalEvent(OnRoundEndTextAppend); } private void OnBeforeSpawn(PlayerBeforeSpawnEvent ev) @@ -113,17 +113,21 @@ private void OnPointChanged(EntityUid uid, DeathMatchRuleComponent component, re _roundEnd.EndRound(component.RestartDelay); } - protected override void AppendRoundEndText(EntityUid uid, DeathMatchRuleComponent component, GameRuleComponent gameRule, ref RoundEndTextAppendEvent args) + private void OnRoundEndTextAppend(RoundEndTextAppendEvent ev) { - if (!TryComp(uid, out var point)) - return; - - if (component.Victor != null && _player.TryGetPlayerData(component.Victor.Value, out var data)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var dm, out var point, out var rule)) { - args.AddLine(Loc.GetString("point-scoreboard-winner", ("player", data.UserName))); - args.AddLine(""); + if (!GameTicker.IsGameRuleAdded(uid, rule)) + continue; + + if (dm.Victor != null && _player.TryGetPlayerData(dm.Victor.Value, out var data)) + { + ev.AddLine(Loc.GetString("point-scoreboard-winner", ("player", data.UserName))); + ev.AddLine(""); + } + ev.AddLine(Loc.GetString("point-scoreboard-header")); + ev.AddLine(new FormattedMessage(point.Scoreboard).ToMarkup()); } - args.AddLine(Loc.GetString("point-scoreboard-header")); - args.AddLine(new FormattedMessage(point.Scoreboard).ToMarkup()); } } diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs index 27a9edbad7..a60a2bfe22 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Robust.Shared.Collections; @@ -16,12 +15,29 @@ protected EntityQueryEnumerator Q return EntityQueryEnumerator(); } - /// - /// Queries all gamerules, regardless of if they're active or not. - /// - protected EntityQueryEnumerator QueryAllRules() + protected bool TryRoundStartAttempt(RoundStartAttemptEvent ev, string localizedPresetName) { - return EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out _, out _, out var gameRule)) + { + var minPlayers = gameRule.MinPlayers; + if (!ev.Forced && ev.Players.Length < minPlayers) + { + ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players", + ("readyPlayersCount", ev.Players.Length), ("minimumPlayers", minPlayers), + ("presetName", localizedPresetName))); + ev.Cancel(); + continue; + } + + if (ev.Players.Length == 0) + { + ChatManager.DispatchServerAnnouncement(Loc.GetString("preset-no-one-ready")); + ev.Cancel(); + } + } + + return !ev.Cancelled; } /// diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.cs index c167ae7b6c..363c2ad7f7 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.cs @@ -1,6 +1,6 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; +using Content.Server.GameTicking.Rules.Components; using Robust.Server.GameObjects; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -22,31 +22,9 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnStartAttempt); SubscribeLocalEvent(OnGameRuleAdded); SubscribeLocalEvent(OnGameRuleStarted); SubscribeLocalEvent(OnGameRuleEnded); - SubscribeLocalEvent(OnRoundEndTextAppend); - } - - private void OnStartAttempt(RoundStartAttemptEvent args) - { - if (args.Forced || args.Cancelled) - return; - - var query = QueryAllRules(); - while (query.MoveNext(out var uid, out _, out var gameRule)) - { - var minPlayers = gameRule.MinPlayers; - if (args.Players.Length >= minPlayers) - continue; - - ChatManager.SendAdminAnnouncement(Loc.GetString("preset-not-enough-ready-players", - ("readyPlayersCount", args.Players.Length), - ("minimumPlayers", minPlayers), - ("presetName", ToPrettyString(uid)))); - args.Cancel(); - } } private void OnGameRuleAdded(EntityUid uid, T component, ref GameRuleAddedEvent args) @@ -70,12 +48,6 @@ private void OnGameRuleEnded(EntityUid uid, T component, ref GameRuleEndedEvent Ended(uid, component, ruleData, args); } - private void OnRoundEndTextAppend(Entity ent, ref RoundEndTextAppendEvent args) - { - if (!TryComp(ent, out var ruleData)) - return; - AppendRoundEndText(ent, ent, ruleData, ref args); - } /// /// Called when the gamerule is added @@ -101,14 +73,6 @@ protected virtual void Ended(EntityUid uid, T component, GameRuleComponent gameR } - /// - /// Called at the end of a round when text needs to be added for a game rule. - /// - protected virtual void AppendRoundEndText(EntityUid uid, T component, GameRuleComponent gameRule, ref RoundEndTextAppendEvent args) - { - - } - /// /// Called on an active gamerule entity in the Update function /// diff --git a/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs b/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs index 01fa387595..b775b7af56 100644 --- a/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs @@ -1,6 +1,5 @@ using System.Threading; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Robust.Server.Player; using Robust.Shared.Player; diff --git a/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs b/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs index 3da55e30c9..01fd97d9a7 100644 --- a/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.KillTracking; using Content.Shared.Chat; diff --git a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs deleted file mode 100644 index aba9ed9e58..0000000000 --- a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Content.Server.Antag; -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Spawners.Components; -using Robust.Server.GameObjects; -using Robust.Server.Maps; -using Robust.Shared.Prototypes; - -namespace Content.Server.GameTicking.Rules; - -public sealed class LoadMapRuleSystem : GameRuleSystem -{ - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly MapSystem _map = default!; - [Dependency] private readonly MapLoaderSystem _mapLoader = default!; - [Dependency] private readonly TransformSystem _transform = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSelectLocation); - SubscribeLocalEvent(OnGridSplit); - } - - private void OnGridSplit(ref GridSplitEvent args) - { - var rule = QueryActiveRules(); - while (rule.MoveNext(out _, out var mapComp, out _)) - { - if (!mapComp.MapGrids.Contains(args.Grid)) - continue; - - mapComp.MapGrids.AddRange(args.NewGrids); - break; - } - } - - protected override void Added(EntityUid uid, LoadMapRuleComponent comp, GameRuleComponent rule, GameRuleAddedEvent args) - { - if (comp.Map != null) - return; - - _map.CreateMap(out var mapId); - comp.Map = mapId; - - if (comp.GameMap != null) - { - var gameMap = _prototypeManager.Index(comp.GameMap.Value); - comp.MapGrids.AddRange(GameTicker.LoadGameMap(gameMap, comp.Map.Value, new MapLoadOptions())); - } - else if (comp.MapPath != null) - { - if (_mapLoader.TryLoad(comp.Map.Value, comp.MapPath.Value.ToString(), out var roots, new MapLoadOptions { LoadMap = true })) - comp.MapGrids.AddRange(roots); - } - else - { - Log.Error($"No valid map prototype or map path associated with the rule {ToPrettyString(uid)}"); - } - } - - private void OnSelectLocation(Entity ent, ref AntagSelectLocationEvent args) - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out _, out var xform)) - { - if (xform.MapID != ent.Comp.Map) - continue; - - if (xform.GridUid == null || !ent.Comp.MapGrids.Contains(xform.GridUid.Value)) - continue; - - if (ent.Comp.SpawnerWhitelist != null && !ent.Comp.SpawnerWhitelist.IsValid(uid, EntityManager)) - continue; - - args.Coordinates.Add(_transform.GetMapCoordinates(xform)); - } - } -} diff --git a/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs b/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs index ee3a025533..e792a004df 100644 --- a/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs @@ -1,6 +1,5 @@ using System.Threading; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Timer = Robust.Shared.Timing.Timer; diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index d06b9fb899..46040e2945 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -1,51 +1,77 @@ +using Content.Server.Administration.Commands; +using Content.Server.Administration.Managers; using Content.Server.Antag; using Content.Server.Communications; using Content.Server.GameTicking.Rules.Components; +using Content.Server.Ghost.Roles.Components; +using Content.Server.Ghost.Roles.Events; using Content.Server.Humanoid; +using Content.Server.Mind; +using Content.Server.NPC.Components; +using Content.Server.NPC.Systems; using Content.Server.Nuke; using Content.Server.NukeOps; using Content.Server.Popups; using Content.Server.Preferences.Managers; +using Content.Server.RandomMetadata; using Content.Server.Roles; using Content.Server.RoundEnd; using Content.Server.Shuttles.Events; using Content.Server.Shuttles.Systems; +using Content.Server.Spawners.Components; using Content.Server.Station.Components; +using Content.Server.Station.Systems; using Content.Server.Store.Components; using Content.Server.Store.Systems; +using Content.Shared.CCVar; +using Content.Shared.Dataset; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Mind.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Nuke; using Content.Shared.NukeOps; using Content.Shared.Preferences; +using Content.Shared.Roles; using Content.Shared.Store; using Content.Shared.Tag; using Content.Shared.Zombies; +using Robust.Server.Player; +using Robust.Shared.Configuration; using Robust.Shared.Map; +using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; using System.Linq; -using Content.Server.GameTicking.Components; -using Content.Server.NPC.Components; -using Content.Server.NPC.Systems; namespace Content.Server.GameTicking.Rules; public sealed class NukeopsRuleSystem : GameRuleSystem { + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IServerPreferencesManager _prefs = default!; + [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly EmergencyShuttleSystem _emergency = default!; [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly RandomMetadataSystem _randomMetadata = default!; + [Dependency] private readonly MindSystem _mind = 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 SharedRoleSystem _roles = default!; + [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; [Dependency] private readonly StoreSystem _store = default!; [Dependency] private readonly TagSystem _tag = default!; + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; + + private ISawmill _sawmill = default!; [ValidatePrototypeId] private const string TelecrystalCurrencyPrototype = "Telecrystal"; @@ -53,67 +79,141 @@ public sealed class NukeopsRuleSystem : GameRuleSystem [ValidatePrototypeId] private const string NukeOpsUplinkTagPrototype = "NukeOpsUplink"; + [ValidatePrototypeId] + public const string NukeopsId = "Nukeops"; + + [ValidatePrototypeId] + private const string OperationPrefixDataset = "operationPrefix"; + + [ValidatePrototypeId] + private const string OperationSuffixDataset = "operationSuffix"; + public override void Initialize() { base.Initialize(); + _sawmill = _logManager.GetSawmill("NukeOps"); + + SubscribeLocalEvent(OnStartAttempt); + SubscribeLocalEvent(OnPlayersSpawning); + SubscribeLocalEvent(OnRoundEndText); SubscribeLocalEvent(OnNukeExploded); SubscribeLocalEvent(OnRunLevelChanged); SubscribeLocalEvent(OnNukeDisarm); SubscribeLocalEvent(OnComponentRemove); SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnPlayersGhostSpawning); + SubscribeLocalEvent(OnMindAdded); SubscribeLocalEvent(OnOperativeZombified); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnShuttleFTLAttempt); SubscribeLocalEvent(OnWarDeclared); SubscribeLocalEvent(OnShuttleCallAttempt); - - SubscribeLocalEvent(OnAntagSelectEntity); - SubscribeLocalEvent(OnAfterAntagEntSelected); } protected override void Started(EntityUid uid, NukeopsRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { - var eligible = new List>(); - var eligibleQuery = EntityQueryEnumerator(); - while (eligibleQuery.MoveNext(out var eligibleUid, out var eligibleComp, out var member)) + base.Started(uid, component, gameRule, args); + + if (GameTicker.RunLevel == GameRunLevel.InRound) + SpawnOperativesForGhostRoles(uid, component); + } + + #region Event Handlers + + private void OnStartAttempt(RoundStartAttemptEvent ev) + { + TryRoundStartAttempt(ev, Loc.GetString("nukeops-title")); + } + + private void OnPlayersSpawning(RulePlayerSpawningEvent ev) + { + var query = QueryActiveRules(); + while (query.MoveNext(out var uid, out _, out var nukeops, out _)) { - if (!_npcFaction.IsFactionHostile(component.Faction, eligibleUid, member)) + if (!SpawnMap((uid, nukeops))) + { + _sawmill.Info("Failed to load map for nukeops"); continue; + } - eligible.Add((eligibleUid, eligibleComp, member)); - } + //Handle there being nobody readied up + if (ev.PlayerPool.Count == 0) + continue; - if (eligible.Count == 0) - return; + var commanderEligible = _antagSelection.GetEligibleSessions(ev.PlayerPool, nukeops.CommanderSpawnDetails.AntagRoleProto); + var agentEligible = _antagSelection.GetEligibleSessions(ev.PlayerPool, nukeops.AgentSpawnDetails.AntagRoleProto); + var operativeEligible = _antagSelection.GetEligibleSessions(ev.PlayerPool, nukeops.OperativeSpawnDetails.AntagRoleProto); + //Calculate how large the nukeops team needs to be + var nukiesToSelect = _antagSelection.CalculateAntagCount(_playerManager.PlayerCount, nukeops.PlayersPerOperative, nukeops.MaxOps); + + //Select Nukies + //Select Commander, priority : commanderEligible, agentEligible, operativeEligible, all players + var selectedCommander = _antagSelection.ChooseAntags(1, commanderEligible, agentEligible, operativeEligible, ev.PlayerPool).FirstOrDefault(); + //Select Agent, priority : agentEligible, operativeEligible, all players + var selectedAgent = _antagSelection.ChooseAntags(1, agentEligible, operativeEligible, ev.PlayerPool).FirstOrDefault(); + //Select Operatives, priority : operativeEligible, all players + var selectedOperatives = _antagSelection.ChooseAntags(nukiesToSelect - 2, operativeEligible, ev.PlayerPool); + + //Create the team! + //If the session is null, they will be spawned as ghost roles (provided the cvar is set) + var operatives = new List { new NukieSpawn(selectedCommander, nukeops.CommanderSpawnDetails) }; + if (nukiesToSelect > 1) + operatives.Add(new NukieSpawn(selectedAgent, nukeops.AgentSpawnDetails)); + + for (var i = 0; i < nukiesToSelect - 2; i++) + { + //Use up all available sessions first, then spawn the rest as ghost roles (if enabled) + if (selectedOperatives.Count > i) + { + operatives.Add(new NukieSpawn(selectedOperatives[i], nukeops.OperativeSpawnDetails)); + } + else + { + operatives.Add(new NukieSpawn(null, nukeops.OperativeSpawnDetails)); + } + } - component.TargetStation = RobustRandom.Pick(eligible); + SpawnOperatives(operatives, _cfg.GetCVar(CCVars.NukeopsSpawnGhostRoles), nukeops); + + foreach (var nukieSpawn in operatives) + { + if (nukieSpawn.Session == null) + continue; + + GameTicker.PlayerJoinGame(nukieSpawn.Session); + } + } } - #region Event Handlers - protected override void AppendRoundEndText(EntityUid uid, NukeopsRuleComponent component, GameRuleComponent gameRule, - ref RoundEndTextAppendEvent args) + private void OnRoundEndText(RoundEndTextAppendEvent ev) { - var winText = Loc.GetString($"nukeops-{component.WinType.ToString().ToLower()}"); - args.AddLine(winText); - - foreach (var cond in component.WinConditions) + var ruleQuery = QueryActiveRules(); + while (ruleQuery.MoveNext(out _, out _, out var nukeops, out _)) { - var text = Loc.GetString($"nukeops-cond-{cond.ToString().ToLower()}"); - args.AddLine(text); - } + var winText = Loc.GetString($"nukeops-{nukeops.WinType.ToString().ToLower()}"); + ev.AddLine(winText); - args.AddLine(Loc.GetString("nukeops-list-start")); + foreach (var cond in nukeops.WinConditions) + { + var text = Loc.GetString($"nukeops-cond-{cond.ToString().ToLower()}"); + ev.AddLine(text); + } + } - var antags =_antag.GetAntagIdentifiers(uid); + ev.AddLine(Loc.GetString("nukeops-list-start")); - foreach (var (_, sessionData, name) in antags) + var nukiesQuery = EntityQueryEnumerator(); + while (nukiesQuery.MoveNext(out var nukeopsUid, out _, out var mindContainer)) { - args.AddLine(Loc.GetString("nukeops-list-name-user", ("name", name), ("user", sessionData.UserName))); + if (!_mind.TryGetMind(nukeopsUid, out _, out var mind, mindContainer)) + continue; + + ev.AddLine(mind.Session != null + ? Loc.GetString("nukeops-list-name-user", ("name", Name(nukeopsUid)), ("user", mind.Session.Name)) + : Loc.GetString("nukeops-list-name", ("name", Name(nukeopsUid)))); } } @@ -124,10 +224,10 @@ private void OnNukeExploded(NukeExplodedEvent ev) { if (ev.OwningStation != null) { - if (ev.OwningStation == GetOutpost(uid)) + if (ev.OwningStation == nukeops.NukieOutpost) { nukeops.WinConditions.Add(WinCondition.NukeExplodedOnNukieOutpost); - SetWinType((uid, nukeops), WinType.CrewMajor); + SetWinType(uid, WinType.CrewMajor, nukeops); continue; } @@ -142,7 +242,7 @@ private void OnNukeExploded(NukeExplodedEvent ev) } nukeops.WinConditions.Add(WinCondition.NukeExplodedOnCorrectStation); - SetWinType((uid, nukeops), WinType.OpsMajor); + SetWinType(uid, WinType.OpsMajor, nukeops); correctStation = true; } @@ -163,85 +263,19 @@ private void OnNukeExploded(NukeExplodedEvent ev) private void OnRunLevelChanged(GameRunLevelChangedEvent ev) { - if (ev.New is not GameRunLevel.PostRound) - return; - var query = QueryActiveRules(); while (query.MoveNext(out var uid, out _, out var nukeops, out _)) { - OnRoundEnd((uid, nukeops)); - } - } - - private void OnRoundEnd(Entity ent) - { - // If the win condition was set to operative/crew major win, ignore. - if (ent.Comp.WinType == WinType.OpsMajor || ent.Comp.WinType == WinType.CrewMajor) - return; - - var nukeQuery = AllEntityQuery(); - var centcomms = _emergency.GetCentcommMaps(); - - while (nukeQuery.MoveNext(out var nuke, out var nukeTransform)) - { - if (nuke.Status != NukeStatus.ARMED) - continue; - - // UH OH - if (nukeTransform.MapUid != null && centcomms.Contains(nukeTransform.MapUid.Value)) + switch (ev.New) { - ent.Comp.WinConditions.Add(WinCondition.NukeActiveAtCentCom); - SetWinType((ent, ent), WinType.OpsMajor); - return; + case GameRunLevel.InRound: + OnRoundStart(uid, nukeops); + break; + case GameRunLevel.PostRound: + OnRoundEnd(uid, nukeops); + break; } - - if (nukeTransform.GridUid == null || ent.Comp.TargetStation == null) - continue; - - if (!TryComp(ent.Comp.TargetStation.Value, out StationDataComponent? data)) - continue; - - foreach (var grid in data.Grids) - { - if (grid != nukeTransform.GridUid) - continue; - - ent.Comp.WinConditions.Add(WinCondition.NukeActiveInStation); - SetWinType(ent, WinType.OpsMajor); - return; - } - } - - if (_antag.AllAntagsAlive(ent.Owner)) - { - SetWinType(ent, WinType.OpsMinor); - ent.Comp.WinConditions.Add(WinCondition.AllNukiesAlive); - return; } - - ent.Comp.WinConditions.Add(_antag.AnyAliveAntags(ent.Owner) - ? WinCondition.SomeNukiesAlive - : WinCondition.AllNukiesDead); - - var diskAtCentCom = false; - var diskQuery = AllEntityQuery(); - while (diskQuery.MoveNext(out _, out var transform)) - { - diskAtCentCom = transform.MapUid != null && centcomms.Contains(transform.MapUid.Value); - - // TODO: The target station should be stored, and the nuke disk should store its original station. - // This is fine for now, because we can assume a single station in base SS14. - break; - } - - // 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 - ? WinType.CrewMinor - : WinType.OpsMinor); - ent.Comp.WinConditions.Add(diskAtCentCom - ? WinCondition.NukeDiskOnCentCom - : WinCondition.NukeDiskNotOnCentCom); } private void OnNukeDisarm(NukeDisarmSuccessEvent ev) @@ -260,31 +294,66 @@ private void OnMobStateChanged(EntityUid uid, NukeOperativeComponent component, CheckRoundShouldEnd(); } - private void OnOperativeZombified(EntityUid uid, NukeOperativeComponent component, ref EntityZombifiedEvent args) + private void OnPlayersGhostSpawning(EntityUid uid, NukeOperativeComponent component, GhostRoleSpawnerUsedEvent args) { - RemCompDeferred(uid, component); + var spawner = args.Spawner; + + if (!TryComp(spawner, out var nukeOpSpawner)) + return; + + HumanoidCharacterProfile? profile = null; + if (TryComp(args.Spawned, out ActorComponent? actor)) + profile = _prefs.GetPreferences(actor.PlayerSession.UserId).SelectedCharacter as HumanoidCharacterProfile; + + // TODO: this is kinda awful for multi-nukies + foreach (var nukeops in EntityQuery()) + { + SetupOperativeEntity(uid, nukeOpSpawner.OperativeName, nukeOpSpawner.SpawnDetails, profile); + + nukeops.OperativeMindPendingData.Add(uid, nukeOpSpawner.SpawnDetails.AntagRoleProto); + } } - private void OnMapInit(Entity ent, ref MapInitEvent args) + private void OnMindAdded(EntityUid uid, NukeOperativeComponent component, MindAddedMessage args) { - var map = Transform(ent).MapID; + if (!_mind.TryGetMind(uid, out var mindId, out var mind)) + return; - var rules = EntityQueryEnumerator(); - while (rules.MoveNext(out var uid, out _, out var mapRule)) + var query = QueryActiveRules(); + while (query.MoveNext(out _, out _, out var nukeops, out _)) { - if (map != mapRule.Map) - continue; - ent.Comp.AssociatedRule = uid; - break; + if (nukeops.OperativeMindPendingData.TryGetValue(uid, out var role) || !nukeops.SpawnOutpost || + nukeops.RoundEndBehavior == RoundEndBehavior.Nothing) + { + role ??= nukeops.OperativeSpawnDetails.AntagRoleProto; + _roles.MindAddRole(mindId, new NukeopsRoleComponent { PrototypeId = role }); + nukeops.OperativeMindPendingData.Remove(uid); + } + + if (mind.Session is not { } playerSession) + return; + + if (GameTicker.RunLevel != GameRunLevel.InRound) + return; + + if (nukeops.TargetStation != null && !string.IsNullOrEmpty(Name(nukeops.TargetStation.Value))) + { + NotifyNukie(playerSession, component, nukeops); + } } } + private void OnOperativeZombified(EntityUid uid, NukeOperativeComponent component, ref EntityZombifiedEvent args) + { + RemCompDeferred(uid, component); + } + private void OnShuttleFTLAttempt(ref ConsoleFTLAttemptEvent ev) { var query = QueryActiveRules(); - while (query.MoveNext(out var uid, out _, out var nukeops, out _)) + while (query.MoveNext(out _, out _, out var nukeops, out _)) { - if (ev.Uid != GetShuttle((uid, nukeops))) + if (ev.Uid != nukeops.NukieShuttle) continue; if (nukeops.WarDeclaredTime != null) @@ -328,12 +397,12 @@ private void OnWarDeclared(ref WarDeclaredEvent ev) { // TODO: this is VERY awful for multi-nukies var query = QueryActiveRules(); - while (query.MoveNext(out var uid, out _, out var nukeops, out _)) + while (query.MoveNext(out _, out _, out var nukeops, out _)) { if (nukeops.WarDeclaredTime != null) continue; - if (TryComp(uid, out var mapComp) && Transform(ev.DeclaratorEntity).MapID != mapComp.Map) + if (Transform(ev.DeclaratorEntity).MapID != nukeops.NukiePlanet) continue; var newStatus = GetWarCondition(nukeops, ev.Status); @@ -344,7 +413,7 @@ private void OnWarDeclared(ref WarDeclaredEvent ev) var timeRemain = nukeops.WarNukieArriveDelay + Timing.CurTime; ev.DeclaratorEntity.Comp.ShuttleDisabledTime = timeRemain; - DistributeExtraTc((uid, nukeops)); + DistributeExtraTc(nukeops); } } } @@ -371,7 +440,7 @@ public WarConditionStatus GetWarCondition(NukeopsRuleComponent nukieRule, WarCon return WarConditionStatus.YesWar; } - private void DistributeExtraTc(Entity nukieRule) + private void DistributeExtraTc(NukeopsRuleComponent nukieRule) { var enumerator = EntityQueryEnumerator(); while (enumerator.MoveNext(out var uid, out var component)) @@ -379,22 +448,161 @@ private void DistributeExtraTc(Entity nukieRule) if (!_tag.HasTag(uid, NukeOpsUplinkTagPrototype)) continue; - if (GetOutpost(nukieRule.Owner) is not { } outpost) + if (!nukieRule.NukieOutpost.HasValue) continue; - if (Transform(uid).MapID != Transform(outpost).MapID) // Will receive bonus TC only on their start outpost + if (Transform(uid).MapID != Transform(nukieRule.NukieOutpost.Value).MapID) // Will receive bonus TC only on their start outpost continue; - _store.TryAddCurrency(new () { { TelecrystalCurrencyPrototype, nukieRule.Comp.WarTcAmountPerNukie } }, uid, component); + _store.TryAddCurrency(new () { { TelecrystalCurrencyPrototype, nukieRule.WarTCAmountPerNukie } }, uid, component); var msg = Loc.GetString("store-currency-war-boost-given", ("target", uid)); _popupSystem.PopupEntity(msg, uid); } } - private void SetWinType(Entity ent, WinType type, bool endRound = true) + private void OnRoundStart(EntityUid uid, NukeopsRuleComponent? component = null) { - ent.Comp.WinType = type; + if (!Resolve(uid, ref component)) + return; + + // TODO: This needs to try and target a Nanotrasen station. At the very least, + // we can only currently guarantee that NT stations are the only station to + // exist in the base game. + + var eligible = new List>(); + var eligibleQuery = EntityQueryEnumerator(); + while (eligibleQuery.MoveNext(out var eligibleUid, out var eligibleComp, out var member)) + { + if (!_npcFaction.IsFactionHostile(component.Faction, eligibleUid, member)) + continue; + + eligible.Add((eligibleUid, eligibleComp, member)); + } + + if (eligible.Count == 0) + return; + + component.TargetStation = RobustRandom.Pick(eligible); + component.OperationName = _randomMetadata.GetRandomFromSegments([OperationPrefixDataset, OperationSuffixDataset], " "); + + var filter = Filter.Empty(); + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out var nukeops, out var actor)) + { + NotifyNukie(actor.PlayerSession, nukeops, component); + filter.AddPlayer(actor.PlayerSession); + } + } + + private void OnRoundEnd(EntityUid uid, NukeopsRuleComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + // If the win condition was set to operative/crew major win, ignore. + if (component.WinType == WinType.OpsMajor || component.WinType == WinType.CrewMajor) + return; + + var nukeQuery = AllEntityQuery(); + var centcomms = _emergency.GetCentcommMaps(); + + while (nukeQuery.MoveNext(out var nuke, out var nukeTransform)) + { + if (nuke.Status != NukeStatus.ARMED) + continue; + + // UH OH + if (nukeTransform.MapUid != null && centcomms.Contains(nukeTransform.MapUid.Value)) + { + component.WinConditions.Add(WinCondition.NukeActiveAtCentCom); + SetWinType(uid, WinType.OpsMajor, component); + return; + } + + if (nukeTransform.GridUid == null || component.TargetStation == null) + continue; + + if (!TryComp(component.TargetStation.Value, out StationDataComponent? data)) + continue; + + foreach (var grid in data.Grids) + { + if (grid != nukeTransform.GridUid) + continue; + + component.WinConditions.Add(WinCondition.NukeActiveInStation); + SetWinType(uid, WinType.OpsMajor, component); + return; + } + } + + var allAlive = true; + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var nukeopsUid, out _, out var mindContainer, out var mobState)) + { + // mind got deleted somehow so ignore it + if (!_mind.TryGetMind(nukeopsUid, out _, out var mind, mindContainer)) + continue; + + // check if player got gibbed or ghosted or something - count as dead + if (mind.OwnedEntity != null && + // if the player somehow isn't a mob anymore that also counts as dead + // have to be alive, not crit or dead + mobState.CurrentState is MobState.Alive) + { + continue; + } + + allAlive = false; + break; + } + + // If all nuke ops were alive at the end of the round, + // the nuke ops win. This is to prevent people from + // running away the moment nuke ops appear. + if (allAlive) + { + SetWinType(uid, WinType.OpsMinor, component); + component.WinConditions.Add(WinCondition.AllNukiesAlive); + return; + } + + component.WinConditions.Add(WinCondition.SomeNukiesAlive); + + var diskAtCentCom = false; + var diskQuery = AllEntityQuery(); + + while (diskQuery.MoveNext(out _, out var transform)) + { + diskAtCentCom = transform.MapUid != null && centcomms.Contains(transform.MapUid.Value); + + // TODO: The target station should be stored, and the nuke disk should store its original station. + // This is fine for now, because we can assume a single station in base SS14. + break; + } + + // If the disk is currently at Central Command, the crew wins - just slightly. + // This also implies that some nuclear operatives have died. + if (diskAtCentCom) + { + SetWinType(uid, WinType.CrewMinor, component); + component.WinConditions.Add(WinCondition.NukeDiskOnCentCom); + } + // Otherwise, the nuke ops win. + else + { + SetWinType(uid, WinType.OpsMinor, component); + component.WinConditions.Add(WinCondition.NukeDiskNotOnCentCom); + } + } + + private void SetWinType(EntityUid uid, WinType type, NukeopsRuleComponent? component = null, bool endRound = true) + { + if (!Resolve(uid, ref component)) + return; + + component.WinType = type; if (endRound && (type == WinType.CrewMajor || type == WinType.OpsMajor)) _roundEndSystem.EndRound(); @@ -405,130 +613,243 @@ private void CheckRoundShouldEnd() var query = QueryActiveRules(); while (query.MoveNext(out var uid, out _, out var nukeops, out _)) { - CheckRoundShouldEnd((uid, nukeops)); + if (nukeops.RoundEndBehavior == RoundEndBehavior.Nothing || nukeops.WinType == WinType.CrewMajor || nukeops.WinType == WinType.OpsMajor) + continue; + + // If there are any nuclear bombs that are active, immediately return. We're not over yet. + var armed = false; + foreach (var nuke in EntityQuery()) + { + if (nuke.Status == NukeStatus.ARMED) + { + armed = true; + break; + } + } + if (armed) + continue; + + MapId? shuttleMapId = Exists(nukeops.NukieShuttle) + ? Transform(nukeops.NukieShuttle.Value).MapID + : null; + + MapId? targetStationMap = null; + if (nukeops.TargetStation != null && TryComp(nukeops.TargetStation, out StationDataComponent? data)) + { + var grid = data.Grids.FirstOrNull(); + targetStationMap = grid != null + ? Transform(grid.Value).MapID + : null; + } + + // Check if there are nuke operatives still alive on the same map as the shuttle, + // or on the same map as the station. + // If there are, the round can continue. + var operatives = EntityQuery(true); + var operativesAlive = operatives + .Where(ent => + ent.Item3.MapID == shuttleMapId + || ent.Item3.MapID == targetStationMap) + .Any(ent => ent.Item2.CurrentState == MobState.Alive && ent.Item1.Running); + + if (operativesAlive) + continue; // There are living operatives than can access the shuttle, or are still on the station's map. + + // Check that there are spawns available and that they can access the shuttle. + var spawnsAvailable = EntityQuery(true).Any(); + if (spawnsAvailable && shuttleMapId == nukeops.NukiePlanet) + continue; // Ghost spawns can still access the shuttle. Continue the round. + + // The shuttle is inaccessible to both living nuke operatives and yet to spawn nuke operatives, + // and there are no nuclear operatives on the target station's map. + nukeops.WinConditions.Add(spawnsAvailable + ? WinCondition.NukiesAbandoned + : WinCondition.AllNukiesDead); + + SetWinType(uid, WinType.CrewMajor, nukeops, false); + _roundEndSystem.DoRoundEndBehavior( + nukeops.RoundEndBehavior, nukeops.EvacShuttleTime, nukeops.RoundEndTextSender, nukeops.RoundEndTextShuttleCall, nukeops.RoundEndTextAnnouncement); + + // prevent it called multiple times + nukeops.RoundEndBehavior = RoundEndBehavior.Nothing; + } + } + + private bool SpawnMap(Entity ent) + { + if (!ent.Comp.SpawnOutpost + || ent.Comp.NukiePlanet != null) + return true; + + ent.Comp.NukiePlanet = _mapManager.CreateMap(); + var gameMap = _prototypeManager.Index(ent.Comp.OutpostMapPrototype); + ent.Comp.NukieOutpost = GameTicker.LoadGameMap(gameMap, ent.Comp.NukiePlanet.Value, null)[0]; + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var grid, out _, out var shuttleTransform)) + { + if (shuttleTransform.MapID != ent.Comp.NukiePlanet) + continue; + + ent.Comp.NukieShuttle = grid; + break; } + + return true; } - private void CheckRoundShouldEnd(Entity ent) + /// + /// Adds missing nuke operative components, equips starting gear and renames the entity. + /// + private void SetupOperativeEntity(EntityUid mob, string name, NukeopSpawnPreset spawnDetails, HumanoidCharacterProfile? profile) { - var nukeops = ent.Comp; + _metaData.SetEntityName(mob, name); + EnsureComp(mob); + + if (profile != null) + _humanoid.LoadProfile(mob, profile); + + var gear = _prototypeManager.Index(spawnDetails.GearProto); + _stationSpawning.EquipStartingGear(mob, gear, profile); - if (nukeops.RoundEndBehavior == RoundEndBehavior.Nothing || nukeops.WinType == WinType.CrewMajor || nukeops.WinType == WinType.OpsMajor) + _npcFaction.RemoveFaction(mob, "NanoTrasen", false); + _npcFaction.AddFaction(mob, "Syndicate"); + } + + private void SpawnOperatives(List sessions, bool spawnGhostRoles, NukeopsRuleComponent component) + { + if (component.NukieOutpost is not { Valid: true } outpostUid) return; + var spawns = new List(); + foreach (var (_, meta, xform) in EntityQuery(true)) + { + if (meta.EntityPrototype?.ID != component.SpawnPointProto.Id) + continue; + + if (xform.ParentUid != component.NukieOutpost) + continue; + + spawns.Add(xform.Coordinates); + break; + } - // If there are any nuclear bombs that are active, immediately return. We're not over yet. - foreach (var nuke in EntityQuery()) + //Fallback, spawn at the centre of the map + if (spawns.Count == 0) { - if (nuke.Status == NukeStatus.ARMED) - return; + spawns.Add(Transform(outpostUid).Coordinates); + _sawmill.Warning($"Fell back to default spawn for nukies!"); } - var shuttle = GetShuttle((ent, ent)); + //Spawn the team + foreach (var nukieSession in sessions) + { + var name = $"{Loc.GetString(nukieSession.Type.NamePrefix)} {RobustRandom.PickAndTake(_prototypeManager.Index(nukieSession.Type.NameList).Values.ToList())}"; - MapId? shuttleMapId = Exists(shuttle) - ? Transform(shuttle.Value).MapID - : null; + var nukeOpsAntag = _prototypeManager.Index(nukieSession.Type.AntagRoleProto); - MapId? targetStationMap = null; - if (nukeops.TargetStation != null && TryComp(nukeops.TargetStation, out StationDataComponent? data)) - { - var grid = data.Grids.FirstOrNull(); - targetStationMap = grid != null - ? Transform(grid.Value).MapID - : null; + //If a session is available, spawn mob and transfer mind into it + if (nukieSession.Session != null) + { + var profile = _prefs.GetPreferences(nukieSession.Session.UserId).SelectedCharacter as HumanoidCharacterProfile; + if (!_prototypeManager.TryIndex(profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies, out SpeciesPrototype? species)) + { + species = _prototypeManager.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); + } + + var mob = Spawn(species.Prototype, RobustRandom.Pick(spawns)); + SetupOperativeEntity(mob, name, nukieSession.Type, profile); + + var newMind = _mind.CreateMind(nukieSession.Session.UserId, name); + _mind.SetUserId(newMind, nukieSession.Session.UserId); + _roles.MindAddRole(newMind, new NukeopsRoleComponent { PrototypeId = nukieSession.Type.AntagRoleProto }); + + _mind.TransferTo(newMind, mob); + } + //Otherwise, spawn as a ghost role + else if (spawnGhostRoles) + { + var spawnPoint = Spawn(component.GhostSpawnPointProto, RobustRandom.Pick(spawns)); + var ghostRole = EnsureComp(spawnPoint); + EnsureComp(spawnPoint); + ghostRole.RoleName = Loc.GetString(nukeOpsAntag.Name); + ghostRole.RoleDescription = Loc.GetString(nukeOpsAntag.Objective); + + var nukeOpSpawner = EnsureComp(spawnPoint); + nukeOpSpawner.OperativeName = name; + nukeOpSpawner.SpawnDetails = nukieSession.Type; + } } + } - // Check if there are nuke operatives still alive on the same map as the shuttle, - // or on the same map as the station. - // If there are, the round can continue. - var operatives = EntityQuery(true); - var operativesAlive = operatives - .Where(op => - op.Item3.MapID == shuttleMapId - || op.Item3.MapID == targetStationMap) - .Any(op => op.Item2.CurrentState == MobState.Alive && op.Item1.Running); - - if (operativesAlive) - return; // There are living operatives than can access the shuttle, or are still on the station's map. - - // Check that there are spawns available and that they can access the shuttle. - var spawnsAvailable = EntityQuery(true).Any(); - if (spawnsAvailable && CompOrNull(ent)?.Map == shuttleMapId) - return; // Ghost spawns can still access the shuttle. Continue the round. - - // The shuttle is inaccessible to both living nuke operatives and yet to spawn nuke operatives, - // and there are no nuclear operatives on the target station's map. - nukeops.WinConditions.Add(spawnsAvailable - ? WinCondition.NukiesAbandoned - : WinCondition.AllNukiesDead); - - SetWinType(ent, WinType.CrewMajor, false); - _roundEndSystem.DoRoundEndBehavior( - nukeops.RoundEndBehavior, nukeops.EvacShuttleTime, nukeops.RoundEndTextSender, nukeops.RoundEndTextShuttleCall, nukeops.RoundEndTextAnnouncement); - - // prevent it called multiple times - nukeops.RoundEndBehavior = RoundEndBehavior.Nothing; + /// + /// Display a greeting message and play a sound for a nukie + /// + private void NotifyNukie(ICommonSession session, NukeOperativeComponent nukeop, NukeopsRuleComponent nukeopsRule) + { + if (nukeopsRule.TargetStation is not { } station) + return; + + _antagSelection.SendBriefing(session, Loc.GetString("nukeops-welcome", ("station", station), ("name", nukeopsRule.OperationName)), Color.Red, nukeop.GreetSoundNotification); } - // this should really go anywhere else but im tired. - private void OnAntagSelectEntity(Entity ent, ref AntagSelectEntityEvent args) + /// + /// Spawn nukie ghost roles if this gamerule was started mid round + /// + private void SpawnOperativesForGhostRoles(EntityUid uid, NukeopsRuleComponent? component = null) { - if (args.Handled) + if (!Resolve(uid, ref component)) return; - var profile = args.Session != null - ? _prefs.GetPreferences(args.Session.UserId).SelectedCharacter as HumanoidCharacterProfile - : HumanoidCharacterProfile.RandomWithSpecies(); - if (!_prototypeManager.TryIndex(profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies, out SpeciesPrototype? species)) + if (!SpawnMap((uid, component))) { - species = _prototypeManager.Index(SharedHumanoidAppearanceSystem.DefaultSpecies); + _sawmill.Info("Failed to load map for nukeops"); + return; } - args.Entity = Spawn(species.Prototype); - _humanoid.LoadProfile(args.Entity.Value, profile); - } + var numNukies = _antagSelection.CalculateAntagCount(_playerManager.PlayerCount, component.PlayersPerOperative, component.MaxOps); - private void OnAfterAntagEntSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) - { - if (ent.Comp.TargetStation is not { } station) + //Dont continue if we have no nukies to spawn + if (numNukies == 0) return; - _antag.SendBriefing(args.Session, Loc.GetString("nukeops-welcome", - ("station", station), - ("name", Name(ent))), - Color.Red, - ent.Comp.GreetSoundNotification); + //Fill the ranks, commander first, then agent, then operatives + //TODO: Possible alternative team compositions? Like multiple commanders or agents + var operatives = new List(); + if (numNukies >= 1) + operatives.Add(new NukieSpawn(null, component.CommanderSpawnDetails)); + if (numNukies >= 2) + operatives.Add(new NukieSpawn(null, component.AgentSpawnDetails)); + if (numNukies >= 3) + { + for (var i = 2; i < numNukies; i++) + { + operatives.Add(new NukieSpawn(null, component.OperativeSpawnDetails)); + } + } + + SpawnOperatives(operatives, true, component); } - /// - /// Is this method the shitty glue holding together the last of my sanity? yes. - /// Do i have a better solution? not presently. - /// - private EntityUid? GetOutpost(Entity ent) + //For admins forcing someone to nukeOps. + public void MakeLoneNukie(EntityUid entity) { - if (!Resolve(ent, ref ent.Comp, false)) - return null; + if (!_mind.TryGetMind(entity, out var mindId, out var mindComponent)) + return; - return ent.Comp.MapGrids.Where(e => HasComp(e) && !HasComp(e)).FirstOrNull(); + //ok hardcoded value bad but so is everything else here + _roles.MindAddRole(mindId, new NukeopsRoleComponent { PrototypeId = NukeopsId }, mindComponent); + SetOutfitCommand.SetOutfit(entity, "SyndicateOperativeGearFull", EntityManager); } - /// - /// Is this method the shitty glue holding together the last of my sanity? yes. - /// Do i have a better solution? not presently. - /// - private EntityUid? GetShuttle(Entity ent) + private sealed class NukieSpawn { - if (!Resolve(ent, ref ent.Comp, false)) - return null; + public ICommonSession? Session { get; private set; } + public NukeopSpawnPreset Type { get; private set; } - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) + public NukieSpawn(ICommonSession? session, NukeopSpawnPreset type) { - if (comp.AssociatedRule == ent.Owner) - return uid; + Session = session; + Type = type; } - - return null; } } diff --git a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs index e69de29bb2..128f112304 100644 --- a/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/PiratesRuleSystem.cs @@ -0,0 +1,321 @@ +using System.Linq; +using System.Numerics; +using Content.Server.Administration.Commands; +using Content.Server.Cargo.Systems; +using Content.Server.Chat.Managers; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.NPC.Components; +using Content.Server.NPC.Systems; +using Content.Server.Preferences.Managers; +using Content.Server.Spawners.Components; +using Content.Server.Station.Components; +using Content.Server.Station.Systems; +using Content.Shared.CCVar; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Mind; +using Content.Shared.Preferences; +using Content.Shared.Roles; +using Robust.Server.GameObjects; +using Robust.Server.Maps; +using Robust.Server.Player; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Configuration; +using Robust.Shared.Enums; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Utility; + +namespace Content.Server.GameTicking.Rules; + +/// +/// This handles the Pirates minor antag, which is designed to coincide with other modes on occasion. +/// +public sealed class PiratesRuleSystem : GameRuleSystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IServerPreferencesManager _prefs = default!; + [Dependency] private readonly StationSpawningSystem _stationSpawningSystem = default!; + [Dependency] private readonly PricingSystem _pricingSystem = default!; + [Dependency] private readonly MapLoaderSystem _map = default!; + [Dependency] private readonly NamingSystem _namingSystem = default!; + [Dependency] private readonly NpcFactionSystem _npcFaction = default!; + [Dependency] private readonly SharedMindSystem _mindSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + + [ValidatePrototypeId] + private const string GameRuleId = "Pirates"; + + [ValidatePrototypeId] + private const string MobId = "MobHuman"; + + [ValidatePrototypeId] + private const string SpeciesId = "Human"; + + [ValidatePrototypeId] + private const string PirateFactionId = "Syndicate"; + + [ValidatePrototypeId] + private const string EnemyFactionId = "NanoTrasen"; + + [ValidatePrototypeId] + private const string GearId = "PirateGear"; + + [ValidatePrototypeId] + private const string SpawnPointId = "SpawnPointPirates"; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPlayerSpawningEvent); + SubscribeLocalEvent(OnRoundEndTextEvent); + SubscribeLocalEvent(OnStartAttempt); + } + + private void OnRoundEndTextEvent(RoundEndTextAppendEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var pirates, out var gameRule)) + { + if (Deleted(pirates.PirateShip)) + { + // Major loss, the ship somehow got annihilated. + ev.AddLine(Loc.GetString("pirates-no-ship")); + } + else + { + List<(double, EntityUid)> mostValuableThefts = new(); + + var comp1 = pirates; + var finalValue = _pricingSystem.AppraiseGrid(pirates.PirateShip, uid => + { + foreach (var mindId in comp1.Pirates) + { + if (TryComp(mindId, out MindComponent? mind) && mind.CurrentEntity == uid) + return false; // Don't appraise the pirates twice, we count them in separately. + } + + return true; + }, (uid, price) => + { + if (comp1.InitialItems.Contains(uid)) + return; + + mostValuableThefts.Add((price, uid)); + mostValuableThefts.Sort((i1, i2) => i2.Item1.CompareTo(i1.Item1)); + if (mostValuableThefts.Count > 5) + mostValuableThefts.Pop(); + }); + + foreach (var mindId in pirates.Pirates) + { + if (TryComp(mindId, out MindComponent? mind) && mind.CurrentEntity is not null) + finalValue += _pricingSystem.GetPrice(mind.CurrentEntity.Value); + } + + var score = finalValue - pirates.InitialShipValue; + + ev.AddLine(Loc.GetString("pirates-final-score", ("score", $"{score:F2}"))); + ev.AddLine(Loc.GetString("pirates-final-score-2", ("finalPrice", $"{finalValue:F2}"))); + + ev.AddLine(""); + ev.AddLine(Loc.GetString("pirates-most-valuable")); + + foreach (var (price, obj) in mostValuableThefts) + { + ev.AddLine(Loc.GetString("pirates-stolen-item-entry", ("entity", obj), ("credits", $"{price:F2}"))); + } + + if (mostValuableThefts.Count == 0) + ev.AddLine(Loc.GetString("pirates-stole-nothing")); + } + + ev.AddLine(""); + ev.AddLine(Loc.GetString("pirates-list-start")); + foreach (var pirate in pirates.Pirates) + { + if (TryComp(pirate, out MindComponent? mind)) + { + ev.AddLine($"- {mind.CharacterName} ({mind.Session?.Name})"); + } + } + } + } + + private void OnPlayerSpawningEvent(RulePlayerSpawningEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var pirates, out var gameRule)) + { + // Forgive me for copy-pasting nukies. + if (!GameTicker.IsGameRuleAdded(uid, gameRule)) + return; + + pirates.Pirates.Clear(); + pirates.InitialItems.Clear(); + + // Between 1 and : needs at least n players per op. + var numOps = Math.Max(1, + (int) Math.Min( + Math.Floor((double) ev.PlayerPool.Count / _cfg.GetCVar(CCVars.PiratesPlayersPerOp)), + _cfg.GetCVar(CCVars.PiratesMaxOps))); + var ops = new ICommonSession[numOps]; + for (var i = 0; i < numOps; i++) + { + ops[i] = _random.PickAndTake(ev.PlayerPool); + } + + var map = "/Maps/Shuttles/pirate.yml"; + var xformQuery = GetEntityQuery(); + + var aabbs = EntityQuery().SelectMany(x => + x.Grids.Select(x => + xformQuery.GetComponent(x).WorldMatrix.TransformBox(Comp(x).LocalAABB))) + .ToArray(); + + var aabb = aabbs[0]; + + for (var i = 1; i < aabbs.Length; i++) + { + aabb.Union(aabbs[i]); + } + + // (Not commented?) + var a = MathF.Max(aabb.Height / 2f, aabb.Width / 2f) * 2.5f; + + var gridId = _map.LoadGrid(GameTicker.DefaultMap, map, new MapLoadOptions + { + Offset = aabb.Center + new Vector2(a, a), + LoadMap = false, + }); + + if (!gridId.HasValue) + { + Log.Error($"Gridid was null when loading \"{map}\", aborting."); + foreach (var session in ops) + { + ev.PlayerPool.Add(session); + } + + return; + } + + pirates.PirateShip = gridId.Value; + + // TODO: Loot table or something + var pirateGear = _prototypeManager.Index(GearId); // YARRR + + var spawns = new List(); + + // Forgive me for hardcoding prototypes + foreach (var (_, meta, xform) in + EntityQuery(true)) + { + if (meta.EntityPrototype?.ID != SpawnPointId || xform.ParentUid != pirates.PirateShip) + continue; + + spawns.Add(xform.Coordinates); + } + + if (spawns.Count == 0) + { + spawns.Add(Transform(pirates.PirateShip).Coordinates); + Log.Warning($"Fell back to default spawn for pirates!"); + } + + for (var i = 0; i < ops.Length; i++) + { + var sex = _random.Prob(0.5f) ? Sex.Male : Sex.Female; + var gender = sex == Sex.Male ? Gender.Male : Gender.Female; + + var name = _namingSystem.GetName(SpeciesId, gender); + + var session = ops[i]; + var newMind = _mindSystem.CreateMind(session.UserId, name); + _mindSystem.SetUserId(newMind, session.UserId); + + var mob = Spawn(MobId, _random.Pick(spawns)); + _metaData.SetEntityName(mob, name); + + _mindSystem.TransferTo(newMind, mob); + var profile = _prefs.GetPreferences(session.UserId).SelectedCharacter as HumanoidCharacterProfile; + _stationSpawningSystem.EquipStartingGear(mob, pirateGear, profile); + + _npcFaction.RemoveFaction(mob, EnemyFactionId, false); + _npcFaction.AddFaction(mob, PirateFactionId); + + pirates.Pirates.Add(newMind); + + // Notificate every player about a pirate antagonist role with sound + _audioSystem.PlayGlobal(pirates.PirateAlertSound, session); + + GameTicker.PlayerJoinGame(session); + } + + pirates.InitialShipValue = _pricingSystem.AppraiseGrid(pirates.PirateShip, uid => + { + pirates.InitialItems.Add(uid); + return true; + }); // Include the players in the appraisal. + } + } + + //Forcing one player to be a pirate. + public void MakePirate(EntityUid entity) + { + if (!_mindSystem.TryGetMind(entity, out var mindId, out var mind)) + return; + + SetOutfitCommand.SetOutfit(entity, GearId, EntityManager); + + var pirateRule = EntityQuery().FirstOrDefault(); + if (pirateRule == null) + { + //todo fuck me this shit is awful + GameTicker.StartGameRule(GameRuleId, out var ruleEntity); + pirateRule = Comp(ruleEntity); + } + + // Notificate every player about a pirate antagonist role with sound + if (mind.Session != null) + { + _audioSystem.PlayGlobal(pirateRule.PirateAlertSound, mind.Session); + } + } + + private void OnStartAttempt(RoundStartAttemptEvent ev) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var pirates, out var gameRule)) + { + if (!GameTicker.IsGameRuleActive(uid, gameRule)) + return; + + var minPlayers = _cfg.GetCVar(CCVars.PiratesMinPlayers); + if (!ev.Forced && ev.Players.Length < minPlayers) + { + _chatManager.SendAdminAnnouncement(Loc.GetString("nukeops-not-enough-ready-players", + ("readyPlayersCount", ev.Players.Length), ("minimumPlayers", minPlayers))); + ev.Cancel(); + return; + } + + if (ev.Players.Length == 0) + { + _chatManager.DispatchServerAnnouncement(Loc.GetString("nukeops-no-one-ready")); + ev.Cancel(); + } + } + } +} diff --git a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs index 5215da96aa..b11c28fb2b 100644 --- a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Systems; using Content.Shared.Chat; diff --git a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs index 7e6901e6c4..d20775c734 100644 --- a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs @@ -16,6 +16,7 @@ using Content.Shared.Database; using Content.Shared.Humanoid; using Content.Shared.IdentityManagement; +using Content.Shared.Inventory; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Mindshield.Components; @@ -23,11 +24,12 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Revolutionary.Components; +using Content.Shared.Roles; using Content.Shared.Stunnable; using Content.Shared.Zombies; using Robust.Shared.Prototypes; using Robust.Shared.Timing; -using Content.Server.GameTicking.Components; +using System.Linq; namespace Content.Server.GameTicking.Rules; @@ -38,7 +40,7 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem RevolutionaryNpcFaction = "Revolutionary"; @@ -57,12 +60,23 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem(OnStartAttempt); + SubscribeLocalEvent(OnPlayerJobAssigned); SubscribeLocalEvent(OnCommandMobStateChanged); SubscribeLocalEvent(OnHeadRevMobStateChanged); + SubscribeLocalEvent(OnRoundEndText); SubscribeLocalEvent(OnGetBriefing); SubscribeLocalEvent(OnPostFlash); } + //Set miniumum players + protected override void Added(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) + { + base.Added(uid, component, gameRule, args); + + gameRule.MinPlayers = component.MinPlayers; + } + protected override void Started(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { base.Started(uid, component, gameRule, args); @@ -84,29 +98,40 @@ protected override void ActiveTick(EntityUid uid, RevolutionaryRuleComponent com } } - protected override void AppendRoundEndText(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, - ref RoundEndTextAppendEvent args) + private void OnRoundEndText(RoundEndTextAppendEvent ev) { - base.AppendRoundEndText(uid, component, gameRule, ref args); - var revsLost = CheckRevsLose(); var commandLost = CheckCommandLose(); - // This is (revsLost, commandsLost) concatted together - // (moony wrote this comment idk what it means) - var index = (commandLost ? 1 : 0) | (revsLost ? 2 : 0); - args.AddLine(Loc.GetString(Outcomes[index])); - - var sessionData = _antag.GetAntagIdentifiers(uid); - args.AddLine(Loc.GetString("rev-headrev-count", ("initialCount", sessionData.Count))); - foreach (var (mind, data, name) in sessionData) + var query = AllEntityQuery(); + while (query.MoveNext(out var headrev)) { - var count = CompOrNull(mind)?.ConvertedCount ?? 0; - args.AddLine(Loc.GetString("rev-headrev-name-user", - ("name", name), - ("username", data.UserName), - ("count", count))); + // This is (revsLost, commandsLost) concatted together + // (moony wrote this comment idk what it means) + var index = (commandLost ? 1 : 0) | (revsLost ? 2 : 0); + ev.AddLine(Loc.GetString(Outcomes[index])); + + ev.AddLine(Loc.GetString("rev-headrev-count", ("initialCount", headrev.HeadRevs.Count))); + foreach (var player in headrev.HeadRevs) + { + // TODO: when role entities are a thing this has to change + var count = CompOrNull(player.Value)?.ConvertedCount ?? 0; + + _mind.TryGetSession(player.Value, out var session); + var username = session?.Name; + if (username != null) + { + ev.AddLine(Loc.GetString("rev-headrev-name-user", + ("name", player.Key), + ("username", username), ("count", count))); + } + else + { + ev.AddLine(Loc.GetString("rev-headrev-name", + ("name", player.Key), ("count", count))); + } - // TODO: someone suggested listing all alive? revs maybe implement at some point + // TODO: someone suggested listing all alive? revs maybe implement at some point + } } } @@ -119,6 +144,57 @@ private void OnGetBriefing(EntityUid uid, RevolutionaryRoleComponent comp, ref G args.Append(Loc.GetString(head ? "head-rev-briefing" : "rev-briefing")); } + //Check for enough players to start rule + private void OnStartAttempt(RoundStartAttemptEvent ev) + { + TryRoundStartAttempt(ev, Loc.GetString("roles-antag-rev-name")); + } + + private void OnPlayerJobAssigned(RulePlayerJobsAssignedEvent ev) + { + var query = QueryActiveRules(); + while (query.MoveNext(out var uid, out var activeGameRule, out var comp, out var gameRule)) + { + var eligiblePlayers = _antagSelection.GetEligiblePlayers(ev.Players, comp.HeadRevPrototypeId); + + if (eligiblePlayers.Count == 0) + continue; + + var headRevCount = _antagSelection.CalculateAntagCount(ev.Players.Length, comp.PlayersPerHeadRev, comp.MaxHeadRevs); + + var headRevs = _antagSelection.ChooseAntags(headRevCount, eligiblePlayers); + + GiveHeadRev(headRevs, comp.HeadRevPrototypeId, comp); + } + } + + private void GiveHeadRev(IEnumerable chosen, ProtoId antagProto, RevolutionaryRuleComponent comp) + { + foreach (var headRev in chosen) + GiveHeadRev(headRev, antagProto, comp); + } + private void GiveHeadRev(EntityUid chosen, ProtoId antagProto, RevolutionaryRuleComponent comp) + { + RemComp(chosen); + + var inCharacterName = MetaData(chosen).EntityName; + + if (!_mind.TryGetMind(chosen, out var mind, out _)) + return; + + if (!_role.MindHasRole(mind)) + { + _role.MindAddRole(mind, new RevolutionaryRoleComponent { PrototypeId = antagProto }, silent: true); + } + + comp.HeadRevs.Add(inCharacterName, mind); + _inventory.SpawnItemsOnEntity(chosen, comp.StartingGear); + var revComp = EnsureComp(chosen); + EnsureComp(chosen); + + _antagSelection.SendBriefing(chosen, Loc.GetString("head-rev-role-greeting"), Color.CornflowerBlue, revComp.RevStartSound); + } + /// /// Called when a Head Rev uses a flash in melee to convert somebody else. /// @@ -157,7 +233,22 @@ private void OnPostFlash(EntityUid uid, HeadRevolutionaryComponent comp, ref Aft } if (mind?.Session != null) - _antag.SendBriefing(mind.Session, Loc.GetString("rev-role-greeting"), Color.Red, revComp.RevStartSound); + _antagSelection.SendBriefing(mind.Session, Loc.GetString("rev-role-greeting"), Color.Red, revComp.RevStartSound); + } + + public void OnHeadRevAdmin(EntityUid entity) + { + if (HasComp(entity)) + return; + + var revRule = EntityQuery().FirstOrDefault(); + if (revRule == null) + { + GameTicker.StartGameRule("Revolutionary", out var ruleEnt); + revRule = Comp(ruleEnt); + } + + GiveHeadRev(entity, revRule.HeadRevPrototypeId, revRule); } //TODO: Enemies of the revolution @@ -218,7 +309,7 @@ private bool CheckRevsLose() _popup.PopupEntity(Loc.GetString("rev-break-control", ("name", Identity.Entity(uid, EntityManager))), uid); _adminLogManager.Add(LogType.Mind, LogImpact.Medium, $"{ToPrettyString(uid)} was deconverted due to all Head Revolutionaries dying."); - if (!_mind.TryGetMind(uid, out var mindId, out _, mc)) + if (!_mind.TryGetMind(uid, out var mindId, out var mind, mc)) continue; // remove their antag role diff --git a/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs b/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs index f09ed3ebc3..7755f684be 100644 --- a/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Shuttles.Systems; using Content.Server.Station.Components; diff --git a/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs b/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs index c60670a3ad..a26a2d783c 100644 --- a/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Sandbox; diff --git a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs index d5adb8fdb7..fa5f17b4f3 100644 --- a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Administration.Logs; -using Content.Server.GameTicking.Components; using Content.Server.Chat.Managers; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; diff --git a/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs b/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs index 4486ee40fb..42e7e82335 100644 --- a/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs +++ b/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Shared.Storage; diff --git a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs index 083085fa0d..32f9040f89 100644 --- a/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ThiefRuleSystem.cs @@ -3,38 +3,118 @@ using Content.Server.Mind; using Content.Server.Objectives; using Content.Server.Roles; +using Content.Shared.Antag; +using Content.Shared.CombatMode.Pacification; using Content.Shared.Humanoid; +using Content.Shared.Inventory; using Content.Shared.Mind; using Content.Shared.Objectives.Components; +using Content.Shared.Roles; using Robust.Shared.Random; +using System.Linq; namespace Content.Server.GameTicking.Rules; public sealed class ThiefRuleSystem : GameRuleSystem { [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; [Dependency] private readonly MindSystem _mindSystem = default!; - [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly SharedRoleSystem _roleSystem = default!; [Dependency] private readonly ObjectivesSystem _objectives = default!; + [Dependency] private readonly InventorySystem _inventory = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(AfterAntagSelected); + SubscribeLocalEvent(OnPlayersSpawned); SubscribeLocalEvent(OnGetBriefing); SubscribeLocalEvent(OnObjectivesTextGetInfo); } - private void AfterAntagSelected(Entity ent, ref AfterAntagEntitySelectedEvent args) + private void OnPlayersSpawned(RulePlayerJobsAssignedEvent ev) { - if (!_mindSystem.TryGetMind(args.EntityUid, out var mindId, out var mind)) + var query = QueryActiveRules(); + while (query.MoveNext(out var uid, out _, out var comp, out var gameRule)) + { + //Get all players eligible for this role, allow selecting existing antags + //TO DO: When voxes specifies are added, increase their chance of becoming a thief by 4 times >:) + var eligiblePlayers = _antagSelection.GetEligiblePlayers(ev.Players, comp.ThiefPrototypeId, acceptableAntags: AntagAcceptability.All, allowNonHumanoids: true); + + //Abort if there are none + if (eligiblePlayers.Count == 0) + { + Log.Warning($"No eligible thieves found, ending game rule {ToPrettyString(uid):rule}"); + GameTicker.EndGameRule(uid, gameRule); + continue; + } + + //Calculate number of thieves to choose + var thiefCount = _random.Next(1, comp.MaxAllowThief + 1); + + //Select our theives + var thieves = _antagSelection.ChooseAntags(thiefCount, eligiblePlayers); + + MakeThief(thieves, comp, comp.PacifistThieves); + } + } + + public void MakeThief(List players, ThiefRuleComponent thiefRule, bool addPacified) + { + foreach (var thief in players) + { + MakeThief(thief, thiefRule, addPacified); + } + } + + public void MakeThief(EntityUid thief, ThiefRuleComponent thiefRule, bool addPacified) + { + if (!_mindSystem.TryGetMind(thief, out var mindId, out var mind)) return; + if (HasComp(mindId)) + return; + + // Assign thief roles + _roleSystem.MindAddRole(mindId, new ThiefRoleComponent + { + PrototypeId = thiefRule.ThiefPrototypeId, + }, silent: true); + + //Add Pacified + //To Do: Long-term this should just be using the antag code to add components. + if (addPacified) //This check is important because some servers may want to disable the thief's pacifism. Do not remove. + { + EnsureComp(thief); + } + //Generate objectives - GenerateObjectives(mindId, mind, ent); - _antag.SendBriefing(args.EntityUid, MakeBriefing(args.EntityUid), null, null); + GenerateObjectives(mindId, mind, thiefRule); + + //Send briefing here to account for humanoid/animal + _antagSelection.SendBriefing(thief, MakeBriefing(thief), null, thiefRule.GreetingSound); + + // Give starting items + _inventory.SpawnItemsOnEntity(thief, thiefRule.StarterItems); + + thiefRule.ThievesMinds.Add(mindId); + } + + public void AdminMakeThief(EntityUid entity, bool addPacified) + { + var thiefRule = EntityQuery().FirstOrDefault(); + if (thiefRule == null) + { + GameTicker.StartGameRule("Thief", out var ruleEntity); + thiefRule = Comp(ruleEntity); + } + + if (HasComp(entity)) + return; + + MakeThief(entity, thiefRule, addPacified); } private void GenerateObjectives(EntityUid mindId, MindComponent mind, ThiefRuleComponent thiefRule) @@ -80,7 +160,8 @@ private void OnGetBriefing(Entity thief, ref GetBriefingEven private string MakeBriefing(EntityUid thief) { var isHuman = HasComp(thief); - var briefing = isHuman + var briefing = "\n"; + briefing = isHuman ? Loc.GetString("thief-role-greeting-human") : Loc.GetString("thief-role-greeting-animal"); @@ -88,9 +169,9 @@ private string MakeBriefing(EntityUid thief) return briefing; } - private void OnObjectivesTextGetInfo(Entity ent, ref ObjectivesTextGetInfoEvent args) + private void OnObjectivesTextGetInfo(Entity thiefs, ref ObjectivesTextGetInfoEvent args) { - args.Minds = _antag.GetAntagMindEntityUids(ent.Owner); + args.Minds = thiefs.Comp.ThievesMinds; args.AgentName = Loc.GetString("thief-round-end-agent-name"); } } diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 1cc5e57704..fc9f0a9a9f 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -6,61 +6,96 @@ using Content.Server.PDA.Ringer; using Content.Server.Roles; using Content.Server.Traitor.Uplink; +using Content.Shared.CCVar; +using Content.Shared.Dataset; using Content.Shared.Mind; +using Content.Shared.Mobs.Systems; using Content.Shared.Objectives.Components; using Content.Shared.PDA; using Content.Shared.Roles; using Content.Shared.Roles.Jobs; +using Robust.Server.Player; +using Robust.Shared.Configuration; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; using System.Linq; using System.Text; -using Content.Server.GameTicking.Components; -using Content.Server.Traitor.Components; namespace Content.Server.GameTicking.Rules; public sealed class TraitorRuleSystem : GameRuleSystem { + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!; - [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = 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 ObjectivesSystem _objectives = default!; + [Dependency] private readonly IGameTiming _timing = default!; - public const int MaxPicks = 20; + private int PlayersPerTraitor => _cfg.GetCVar(CCVars.TraitorPlayersPerTraitor); + private int MaxTraitors => _cfg.GetCVar(CCVars.TraitorMaxTraitors); public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(AfterEntitySelected); + SubscribeLocalEvent(OnStartAttempt); + SubscribeLocalEvent(OnPlayersSpawned); + SubscribeLocalEvent(HandleLatejoin); SubscribeLocalEvent(OnObjectivesTextGetInfo); SubscribeLocalEvent(OnObjectivesTextPrepend); } + //Set min players on game rule protected override void Added(EntityUid uid, TraitorRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { base.Added(uid, component, gameRule, args); + + gameRule.MinPlayers = _cfg.GetCVar(CCVars.TraitorMinPlayers); + } + + protected override void Started(EntityUid uid, TraitorRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); MakeCodewords(component); } - private void AfterEntitySelected(Entity ent, ref AfterAntagEntitySelectedEvent args) + protected override void ActiveTick(EntityUid uid, TraitorRuleComponent component, GameRuleComponent gameRule, float frameTime) + { + base.ActiveTick(uid, component, gameRule, frameTime); + + if (component.SelectionStatus < TraitorRuleComponent.SelectionState.Started && component.AnnounceAt < _timing.CurTime) + { + DoTraitorStart(component); + component.SelectionStatus = TraitorRuleComponent.SelectionState.Started; + } + } + + /// + /// Check for enough players + /// + /// + private void OnStartAttempt(RoundStartAttemptEvent ev) { - MakeTraitor(args.EntityUid, ent); + TryRoundStartAttempt(ev, Loc.GetString("traitor-title")); } private void MakeCodewords(TraitorRuleComponent component) { - var adjectives = _prototypeManager.Index(component.CodewordAdjectives).Values; - var verbs = _prototypeManager.Index(component.CodewordVerbs).Values; + var codewordCount = _cfg.GetCVar(CCVars.TraitorCodewordCount); + var adjectives = _prototypeManager.Index(component.CodewordAdjectives).Values; + var verbs = _prototypeManager.Index(component.CodewordVerbs).Values; var codewordPool = adjectives.Concat(verbs).ToList(); - var finalCodewordCount = Math.Min(component.CodewordCount, codewordPool.Count); + var finalCodewordCount = Math.Min(codewordCount, codewordPool.Count); component.Codewords = new string[finalCodewordCount]; for (var i = 0; i < finalCodewordCount; i++) { @@ -68,25 +103,66 @@ private void MakeCodewords(TraitorRuleComponent component) } } + private void DoTraitorStart(TraitorRuleComponent component) + { + var eligiblePlayers = _antagSelection.GetEligiblePlayers(_playerManager.Sessions, component.TraitorPrototypeId); + + if (eligiblePlayers.Count == 0) + return; + + var traitorsToSelect = _antagSelection.CalculateAntagCount(_playerManager.PlayerCount, PlayersPerTraitor, MaxTraitors); + + var selectedTraitors = _antagSelection.ChooseAntags(traitorsToSelect, eligiblePlayers); + + MakeTraitor(selectedTraitors, component); + } + + private void OnPlayersSpawned(RulePlayerJobsAssignedEvent ev) + { + //Start the timer + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var comp, out var gameRuleComponent)) + { + var delay = TimeSpan.FromSeconds( + _cfg.GetCVar(CCVars.TraitorStartDelay) + + _random.NextFloat(0f, _cfg.GetCVar(CCVars.TraitorStartDelayVariance))); + + //Set the delay for choosing traitors + comp.AnnounceAt = _timing.CurTime + delay; + + comp.SelectionStatus = TraitorRuleComponent.SelectionState.ReadyToStart; + } + } + + public bool MakeTraitor(List traitors, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true) + { + foreach (var traitor in traitors) + { + MakeTraitor(traitor, component, giveUplink, giveObjectives); + } + + return true; + } + public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool giveUplink = true, bool giveObjectives = true) { //Grab the mind if it wasnt provided if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind)) return false; - var briefing = Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", component.Codewords))); - - if (TryComp(traitor, out var autoTraitorComponent)) + if (HasComp(mindId)) { - giveUplink = autoTraitorComponent.GiveUplink; - giveObjectives = autoTraitorComponent.GiveObjectives; + Log.Error($"Player {mind.CharacterName} is already a traitor."); + return false; } + var briefing = Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", component.Codewords))); + Note[]? code = null; if (giveUplink) { // Calculate the amount of currency on the uplink. - var startingBalance = component.StartingBalance; + var startingBalance = _cfg.GetCVar(CCVars.TraitorStartingBalance); if (_jobs.MindTryGetJob(mindId, out _, out var prototype)) startingBalance = Math.Max(startingBalance - prototype.AntagAdvantage, 0); @@ -104,14 +180,19 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#")))); } - _antag.SendBriefing(traitor, GenerateBriefing(component.Codewords, code), null, component.GreetSoundNotification); + _antagSelection.SendBriefing(traitor, GenerateBriefing(component.Codewords, code), null, component.GreetSoundNotification); component.TraitorMinds.Add(mindId); + // Assign traitor roles + _roleSystem.MindAddRole(mindId, new TraitorRoleComponent + { + PrototypeId = component.TraitorPrototypeId + }, mind, true); // Assign briefing _roleSystem.MindAddRole(mindId, new RoleBriefingComponent { - Briefing = briefing + Briefing = briefing.ToString() }, mind, true); // Change the faction @@ -121,8 +202,11 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool // Give traitors their objectives if (giveObjectives) { + var maxDifficulty = _cfg.GetCVar(CCVars.TraitorMaxDifficulty); + var maxPicks = _cfg.GetCVar(CCVars.TraitorMaxPicks); var difficulty = 0f; - for (var pick = 0; pick < MaxPicks && component.MaxDifficulty > difficulty; pick++) + Log.Debug($"Attempting {maxPicks} objective picks with {maxDifficulty} difficulty"); + for (var pick = 0; pick < maxPicks && maxDifficulty > difficulty; pick++) { var objective = _objectives.GetRandomObjective(mindId, mind, component.ObjectiveGroup); if (objective == null) @@ -138,9 +222,53 @@ public bool MakeTraitor(EntityUid traitor, TraitorRuleComponent component, bool return true; } + private void HandleLatejoin(PlayerSpawnCompleteEvent ev) + { + var query = QueryActiveRules(); + while (query.MoveNext(out _, out var comp, out _)) + { + if (comp.TotalTraitors >= MaxTraitors) + continue; + + if (!ev.LateJoin) + continue; + + if (!_antagSelection.IsPlayerEligible(ev.Player, comp.TraitorPrototypeId)) + continue; + + //If its before we have selected traitors, continue + if (comp.SelectionStatus < TraitorRuleComponent.SelectionState.Started) + continue; + + // the nth player we adjust our probabilities around + var target = PlayersPerTraitor * comp.TotalTraitors + 1; + var chance = 1f / PlayersPerTraitor; + + // If we have too many traitors, divide by how many players below target for next traitor we are. + if (ev.JoinOrder < target) + { + chance /= (target - ev.JoinOrder); + } + else // Tick up towards 100% chance. + { + chance *= ((ev.JoinOrder + 1) - target); + } + + if (chance > 1) + chance = 1; + + // Now that we've calculated our chance, roll and make them a traitor if we roll under. + // You get one shot. + if (_random.Prob(chance)) + { + MakeTraitor(ev.Mob, comp); + } + } + } + private void OnObjectivesTextGetInfo(EntityUid uid, TraitorRuleComponent comp, ref ObjectivesTextGetInfoEvent args) { - args.Minds = _antag.GetAntagMindEntityUids(uid); + args.Minds = comp.TraitorMinds; args.AgentName = Loc.GetString("traitor-round-end-agent-name"); } @@ -149,6 +277,27 @@ private void OnObjectivesTextPrepend(EntityUid uid, TraitorRuleComponent comp, r args.Text += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", comp.Codewords))); } + /// + /// Start this game rule manually + /// + public TraitorRuleComponent StartGameRule() + { + var comp = EntityQuery().FirstOrDefault(); + if (comp == null) + { + GameTicker.StartGameRule("Traitor", out var ruleEntity); + comp = Comp(ruleEntity); + } + + return comp; + } + + public void MakeTraitorAdmin(EntityUid entity, bool giveUplink, bool giveObjectives) + { + var traitorRule = StartGameRule(); + MakeTraitor(entity, traitorRule, giveUplink, giveObjectives); + } + private string GenerateBriefing(string[] codewords, Note[]? uplinkCode) { var sb = new StringBuilder(); @@ -163,11 +312,9 @@ private string GenerateBriefing(string[] codewords, Note[]? uplinkCode) public List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind) { List<(EntityUid Id, MindComponent Mind)> allTraitors = new(); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var traitor)) + foreach (var traitor in EntityQuery()) { - foreach (var role in GetOtherTraitorMindsAliveAndConnected(ourMind, (uid, traitor))) + foreach (var role in GetOtherTraitorMindsAliveAndConnected(ourMind, traitor)) { if (!allTraitors.Contains(role)) allTraitors.Add(role); @@ -177,15 +324,20 @@ private string GenerateBriefing(string[] codewords, Note[]? uplinkCode) return allTraitors; } - private List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind, Entity rule) + private List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind, TraitorRuleComponent component) { var traitors = new List<(EntityUid Id, MindComponent Mind)>(); - foreach (var mind in _antag.GetAntagMinds(rule.Owner)) + foreach (var traitor in component.TraitorMinds) { - if (mind.Comp == ourMind) - continue; - - traitors.Add((mind, mind)); + if (TryComp(traitor, out MindComponent? mind) && + mind.OwnedEntity != null && + mind.Session != null && + mind != ourMind && + _mobStateSystem.IsAlive(mind.OwnedEntity.Value) && + mind.CurrentEntity == mind.OwnedEntity) + { + traitors.Add((traitor, mind)); + } } return traitors; diff --git a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs index f62d0b79ff..5714337d4d 100644 --- a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs @@ -1,35 +1,46 @@ +using Content.Server.Actions; using Content.Server.Antag; using Content.Server.Chat.Systems; using Content.Server.GameTicking.Rules.Components; using Content.Server.Popups; +using Content.Server.Roles; using Content.Server.RoundEnd; using Content.Server.Station.Components; using Content.Server.Station.Systems; using Content.Server.Zombies; +using Content.Shared.CCVar; using Content.Shared.Humanoid; 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.Server.Player; +using Robust.Shared.Configuration; using Robust.Shared.Player; +using Robust.Shared.Random; using Robust.Shared.Timing; using System.Globalization; using Content.Server.Announcements.Systems; -using Content.Server.GameTicking.Components; namespace Content.Server.GameTicking.Rules; public sealed class ZombieRuleSystem : GameRuleSystem { + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly RoundEndSystem _roundEnd = default!; [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly ActionsSystem _action = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly ZombieSystem _zombie = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!; + [Dependency] private readonly SharedRoleSystem _roles = default!; [Dependency] private readonly StationSystem _station = default!; - [Dependency] private readonly AntagSelectionSystem _antag = default!; + [Dependency] private readonly AntagSelectionSystem _antagSelection = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly AnnouncerSystem _announcer = default!; [Dependency] private readonly GameTicker _gameTicker = default!; @@ -38,56 +49,67 @@ public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnStartAttempt); + SubscribeLocalEvent(OnRoundEndText); SubscribeLocalEvent(OnZombifySelf); } - protected override void AppendRoundEndText(EntityUid uid, ZombieRuleComponent component, GameRuleComponent gameRule, - ref RoundEndTextAppendEvent args) + /// + /// Set the required minimum players for this gamemode to start + /// + protected override void Added(EntityUid uid, ZombieRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { - base.AppendRoundEndText(uid, component, gameRule, ref args); - - // This is just the general condition thing used for determining the win/lose text - var fraction = GetInfectedFraction(true, true); - - if (fraction <= 0) - args.AddLine(Loc.GetString("zombie-round-end-amount-none")); - else if (fraction <= 0.25) - args.AddLine(Loc.GetString("zombie-round-end-amount-low")); - else if (fraction <= 0.5) - args.AddLine(Loc.GetString("zombie-round-end-amount-medium", ("percent", Math.Round((fraction * 100), 2).ToString(CultureInfo.InvariantCulture)))); - else if (fraction < 1) - args.AddLine(Loc.GetString("zombie-round-end-amount-high", ("percent", Math.Round((fraction * 100), 2).ToString(CultureInfo.InvariantCulture)))); - else - args.AddLine(Loc.GetString("zombie-round-end-amount-all")); - - var antags = _antag.GetAntagIdentifiers(uid); - args.AddLine(Loc.GetString("zombie-round-end-initial-count", ("initialCount", antags.Count))); - foreach (var (_, data, entName) in antags) - { - args.AddLine(Loc.GetString("zombie-round-end-user-was-initial", - ("name", entName), - ("username", data.UserName))); - } + base.Added(uid, component, gameRule, args); - var healthy = GetHealthyHumans(); - // Gets a bunch of the living players and displays them if they're under a threshold. - // InitialInfected is used for the threshold because it scales with the player count well. - if (healthy.Count <= 0 || healthy.Count > 2 * antags.Count) - return; - args.AddLine(""); - args.AddLine(Loc.GetString("zombie-round-end-survivor-count", ("count", healthy.Count))); - foreach (var survivor in healthy) + gameRule.MinPlayers = _cfg.GetCVar(CCVars.ZombieMinPlayers); + } + + private void OnRoundEndText(RoundEndTextAppendEvent ev) + { + foreach (var zombie in EntityQuery()) { - var meta = MetaData(survivor); - var username = string.Empty; - if (_mindSystem.TryGetMind(survivor, out _, out var mind) && mind.Session != null) + // This is just the general condition thing used for determining the win/lose text + var fraction = GetInfectedFraction(true, true); + + if (fraction <= 0) + ev.AddLine(Loc.GetString("zombie-round-end-amount-none")); + else if (fraction <= 0.25) + ev.AddLine(Loc.GetString("zombie-round-end-amount-low")); + else if (fraction <= 0.5) + ev.AddLine(Loc.GetString("zombie-round-end-amount-medium", ("percent", Math.Round((fraction * 100), 2).ToString(CultureInfo.InvariantCulture)))); + else if (fraction < 1) + ev.AddLine(Loc.GetString("zombie-round-end-amount-high", ("percent", Math.Round((fraction * 100), 2).ToString(CultureInfo.InvariantCulture)))); + else + ev.AddLine(Loc.GetString("zombie-round-end-amount-all")); + + ev.AddLine(Loc.GetString("zombie-round-end-initial-count", ("initialCount", zombie.InitialInfectedNames.Count))); + foreach (var player in zombie.InitialInfectedNames) { - username = mind.Session.Name; + ev.AddLine(Loc.GetString("zombie-round-end-user-was-initial", + ("name", player.Key), + ("username", player.Value))); } - args.AddLine(Loc.GetString("zombie-round-end-user-was-survivor", - ("name", meta.EntityName), - ("username", username))); + var healthy = GetHealthyHumans(true); + // Gets a bunch of the living players and displays them if they're under a threshold. + // InitialInfected is used for the threshold because it scales with the player count well. + if (healthy.Count <= 0 || healthy.Count > 2 * zombie.InitialInfectedNames.Count) + continue; + ev.AddLine(""); + ev.AddLine(Loc.GetString("zombie-round-end-survivor-count", ("count", healthy.Count))); + foreach (var survivor in healthy) + { + var meta = MetaData(survivor); + var username = string.Empty; + if (_mindSystem.TryGetMind(survivor, out _, out var mind) && mind.Session != null) + { + username = mind.Session.Name; + } + + ev.AddLine(Loc.GetString("zombie-round-end-user-was-survivor", + ("name", meta.EntityName), + ("username", username))); + } } } @@ -117,20 +139,38 @@ private void CheckRoundEnd(ZombieRuleComponent zombieRuleComponent) _roundEnd.EndRound(); } + /// + /// Check we have enough players to start this game mode, if not - cancel and announce + /// + private void OnStartAttempt(RoundStartAttemptEvent ev) + { + TryRoundStartAttempt(ev, Loc.GetString("zombie-title")); + } + protected override void Started(EntityUid uid, ZombieRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { base.Started(uid, component, gameRule, args); - component.NextRoundEndCheck = _timing.CurTime + component.EndCheckDelay; + var delay = _random.Next(component.MinStartDelay, component.MaxStartDelay); + component.StartTime = _timing.CurTime + delay; } protected override void ActiveTick(EntityUid uid, ZombieRuleComponent component, GameRuleComponent gameRule, float frameTime) { base.ActiveTick(uid, component, gameRule, frameTime); - if (!component.NextRoundEndCheck.HasValue || component.NextRoundEndCheck > _timing.CurTime) - return; - CheckRoundEnd(component); - component.NextRoundEndCheck = _timing.CurTime + component.EndCheckDelay; + + if (component.StartTime.HasValue && component.StartTime < _timing.CurTime) + { + InfectInitialPlayers(component); + component.StartTime = null; + component.NextRoundEndCheck = _timing.CurTime + component.EndCheckDelay; + } + + if (component.NextRoundEndCheck.HasValue && component.NextRoundEndCheck < _timing.CurTime) + { + CheckRoundEnd(component); + component.NextRoundEndCheck = _timing.CurTime + component.EndCheckDelay; + } } private void OnZombifySelf(EntityUid uid, PendingZombieComponent component, ZombifySelfActionEvent args) @@ -195,4 +235,81 @@ private List GetHealthyHumans(bool includeOffStation = false) } return healthy; } + + /// + /// Infects the first players with the passive zombie virus. + /// Also records their names for the end of round screen. + /// + /// + /// The reason this code is written separately is to facilitate + /// allowing this gamemode to be started midround. As such, it doesn't need + /// any information besides just running. + /// + private void InfectInitialPlayers(ZombieRuleComponent component) + { + //Get all players with initial infected enabled, and exclude those with the ZombieImmuneComponent and roles with CanBeAntag = False + var eligiblePlayers = _antagSelection.GetEligiblePlayers( + _playerManager.Sessions, + component.PatientZeroPrototypeId, + includeAllJobs: false, + customExcludeCondition: player => HasComp(player) || HasComp(player) + ); + + //And get all players, excluding ZombieImmune and roles with CanBeAntag = False - to fill any leftover initial infected slots + var allPlayers = _antagSelection.GetEligiblePlayers( + _playerManager.Sessions, + component.PatientZeroPrototypeId, + acceptableAntags: Shared.Antag.AntagAcceptability.All, + includeAllJobs: false , + ignorePreferences: true, + customExcludeCondition: HasComp + ); + + //If there are no players to choose, abort + if (allPlayers.Count == 0) + return; + + //How many initial infected should we select + var initialInfectedCount = _antagSelection.CalculateAntagCount(_playerManager.PlayerCount, component.PlayersPerInfected, component.MaxInitialInfected); + + //Choose the required number of initial infected from the eligible players, making up any shortfall by choosing from all players + var initialInfected = _antagSelection.ChooseAntags(initialInfectedCount, eligiblePlayers, allPlayers); + + //Make brain craving + MakeZombie(initialInfected, component); + + //Send the briefing, play greeting sound + _antagSelection.SendBriefing(initialInfected, Loc.GetString("zombie-patientzero-role-greeting"), Color.Plum, component.InitialInfectedSound); + } + + private void MakeZombie(List entities, ZombieRuleComponent component) + { + foreach (var entity in entities) + { + MakeZombie(entity, component); + } + } + private void MakeZombie(EntityUid entity, ZombieRuleComponent component) + { + if (!_mindSystem.TryGetMind(entity, out var mind, out var mindComponent)) + return; + + //Add the role to the mind silently (to avoid repeating job assignment) + _roles.MindAddRole(mind, new InitialInfectedRoleComponent { PrototypeId = component.PatientZeroPrototypeId }, silent: true); + EnsureComp(entity); + + //Add the zombie components and grace period + var pending = EnsureComp(entity); + pending.GracePeriod = _random.Next(component.MinInitialInfectedGrace, component.MaxInitialInfectedGrace); + EnsureComp(entity); + EnsureComp(entity); + + //Add the zombify action + _action.AddAction(entity, ref pending.Action, component.ZombifySelfActionPrototype, entity); + + //Get names for the round end screen, incase they leave mid-round + var inCharacterName = MetaData(entity).EntityName; + var accountName = mindComponent.Session == null ? string.Empty : mindComponent.Session.Name; + component.InitialInfectedNames.Add(inCharacterName, accountName); + } } diff --git a/Content.Server/Goobstation/Changeling/ChangelingSystem.Abilities.cs b/Content.Server/Goobstation/Changeling/ChangelingSystem.Abilities.cs deleted file mode 100644 index cd9ffaee6b..0000000000 --- a/Content.Server/Goobstation/Changeling/ChangelingSystem.Abilities.cs +++ /dev/null @@ -1,618 +0,0 @@ -using Content.Shared.Changeling; -using Content.Shared.Chemistry.Components; -using Content.Shared.Cuffs.Components; -using Content.Shared.DoAfter; -using Content.Shared.FixedPoint; -using Content.Shared.IdentityManagement; -using Content.Shared.Mobs; -using Content.Server.Store.Components; -using Content.Shared.Popups; -using Content.Shared.Damage; -using Robust.Shared.Prototypes; -using Content.Shared.Damage.Prototypes; -using Content.Server.Objectives.Components; -using Content.Server.Light.Components; -using Content.Shared.Eye.Blinding.Systems; -using Content.Shared.Eye.Blinding.Components; -using Content.Server.Flash.Components; -using Content.Shared.Movement.Pulling.Components; -using Content.Shared.Stealth.Components; -using Content.Shared.Damage.Components; -using Content.Server.Radio.Components; - -namespace Content.Server.Changeling; - -public sealed partial class ChangelingSystem : EntitySystem -{ - public void SubscribeAbilities() - { - SubscribeLocalEvent(OnOpenEvolutionMenu); - SubscribeLocalEvent(OnAbsorb); - SubscribeLocalEvent(OnAbsorbDoAfter); - SubscribeLocalEvent(OnStingExtractDNA); - SubscribeLocalEvent(OnTransformCycle); - SubscribeLocalEvent(OnTransform); - SubscribeLocalEvent(OnEnterStasis); - SubscribeLocalEvent(OnExitStasis); - - SubscribeLocalEvent(OnToggleArmblade); - SubscribeLocalEvent(OnCreateBoneShard); - SubscribeLocalEvent(OnToggleArmor); - SubscribeLocalEvent(OnToggleShield); - SubscribeLocalEvent(OnShriekDissonant); - SubscribeLocalEvent(OnShriekResonant); - SubscribeLocalEvent(OnToggleStrainedMuscles); - - SubscribeLocalEvent(OnStingBlind); - SubscribeLocalEvent(OnStingCryo); - SubscribeLocalEvent(OnStingLethargic); - SubscribeLocalEvent(OnStingMute); - SubscribeLocalEvent(OnStingTransform); - SubscribeLocalEvent(OnStingFakeArmblade); - - SubscribeLocalEvent(OnAnatomicPanacea); - SubscribeLocalEvent(OnAugmentedEyesight); - SubscribeLocalEvent(OnBiodegrade); - SubscribeLocalEvent(OnChameleonSkin); - SubscribeLocalEvent(OnEphedrineOverdose); - SubscribeLocalEvent(OnHealUltraSwag); - SubscribeLocalEvent(OnLastResort); - SubscribeLocalEvent(OnLesserForm); - SubscribeLocalEvent(OnSpacesuit); - SubscribeLocalEvent(OnHivemindAccess); - } - - #region Basic Abilities - - private void OnOpenEvolutionMenu(EntityUid uid, ChangelingComponent comp, ref OpenEvolutionMenuEvent args) - { - if (!TryComp(uid, out var store)) - return; - - _store.ToggleUi(uid, uid, store); - } - - private void OnAbsorb(EntityUid uid, ChangelingComponent comp, ref AbsorbDNAEvent args) - { - var target = args.Target; - - if (!IsIncapacitated(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-absorb-fail-incapacitated"), uid, uid); - return; - } - if (HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-absorb-fail-absorbed"), uid, uid); - return; - } - if (!HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-absorb-fail-unabsorbable"), uid, uid); - return; - } - - if (!TryUseAbility(uid, comp, args)) - return; - - var popupOthers = Loc.GetString("changeling-absorb-start", ("user", Identity.Entity(uid, EntityManager)), ("target", Identity.Entity(target, EntityManager))); - _popup.PopupEntity(popupOthers, uid, PopupType.LargeCaution); - PlayMeatySound(uid, comp); - var dargs = new DoAfterArgs(EntityManager, uid, TimeSpan.FromSeconds(15), new AbsorbDNADoAfterEvent(), uid, target) - { - DistanceThreshold = 1.5f, - BreakOnDamage = true, - BreakOnHandChange = false, - BreakOnUserMove = true, - BreakOnTargetMove = true, - BreakOnWeightlessMove = true, - AttemptFrequency = AttemptFrequency.StartAndEnd - }; - _doAfter.TryStartDoAfter(dargs); - } - public ProtoId AbsorbedDamageGroup = "Genetic"; - private void OnAbsorbDoAfter(EntityUid uid, ChangelingComponent comp, ref AbsorbDNADoAfterEvent args) - { - if (args.Args.Target == null) - return; - - var target = args.Args.Target.Value; - - if (args.Cancelled || !IsIncapacitated(target) || HasComp(target)) - return; - - PlayMeatySound(args.User, comp); - - UpdateBiomass(uid, comp, comp.MaxBiomass - comp.TotalAbsorbedEntities); - - var dmg = new DamageSpecifier(_proto.Index(AbsorbedDamageGroup), 200); - _damage.TryChangeDamage(target, dmg, false, false); - _blood.ChangeBloodReagent(target, "FerrochromicAcid"); - _blood.SpillAllSolutions(target); - - EnsureComp(target); - - var popup = Loc.GetString("changeling-absorb-end-self-ling"); - var bonusChemicals = 0f; - var bonusEvolutionPoints = 0f; - if (TryComp(target, out var targetComp)) - { - bonusChemicals += targetComp.MaxChemicals / 2; - bonusEvolutionPoints += 10; - comp.MaxBiomass += targetComp.MaxBiomass / 2; - } - else - { - popup = Loc.GetString("changeling-absorb-end-self"); - bonusChemicals += 10; - bonusEvolutionPoints += 2; - } - TryStealDNA(uid, target, comp, true); - comp.TotalAbsorbedEntities++; - - _popup.PopupEntity(popup, args.User, args.User); - comp.MaxChemicals += bonusChemicals; - - if (TryComp(args.User, out var store)) - { - _store.TryAddCurrency(new Dictionary { { "EvolutionPoint", bonusEvolutionPoints } }, args.User, store); - _store.UpdateUserInterface(args.User, args.User, store); - } - - if (_mind.TryGetMind(uid, out var mindId, out var mind)) - if (_mind.TryGetObjectiveComp(mindId, out var objective, mind)) - objective.Absorbed += 1; - } - - private void OnStingExtractDNA(EntityUid uid, ChangelingComponent comp, ref StingExtractDNAEvent args) - { - if (!TrySting(uid, comp, args, true)) - return; - - var target = args.Target; - if (!TryStealDNA(uid, target, comp, true)) - { - _popup.PopupEntity(Loc.GetString("changeling-sting-extract-fail"), uid, uid); - // royal cashback - comp.Chemicals += Comp(args.Action).ChemicalCost; - } - else _popup.PopupEntity(Loc.GetString("changeling-sting", ("target", Identity.Entity(target, EntityManager))), uid, uid); - } - - private void OnTransformCycle(EntityUid uid, ChangelingComponent comp, ref ChangelingTransformCycleEvent args) - { - comp.AbsorbedDNAIndex += 1; - if (comp.AbsorbedDNAIndex >= comp.MaxAbsorbedDNA || comp.AbsorbedDNAIndex >= comp.AbsorbedDNA.Count) - comp.AbsorbedDNAIndex = 0; - - if (comp.AbsorbedDNA.Count == 0) - { - _popup.PopupEntity(Loc.GetString("changeling-transform-cycle-empty"), uid, uid); - return; - } - - var selected = comp.AbsorbedDNA.ToArray()[comp.AbsorbedDNAIndex]; - comp.SelectedForm = selected; - _popup.PopupEntity(Loc.GetString("changeling-transform-cycle", ("target", selected.Name)), uid, uid); - } - private void OnTransform(EntityUid uid, ChangelingComponent comp, ref ChangelingTransformEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryTransform(uid, comp)) - comp.Chemicals += Comp(args.Action).ChemicalCost; - } - - private void OnEnterStasis(EntityUid uid, ChangelingComponent comp, ref EnterStasisEvent args) - { - if (comp.IsInStasis || HasComp(uid)) - { - _popup.PopupEntity(Loc.GetString("changeling-stasis-enter-fail"), uid, uid); - return; - } - - if (!TryUseAbility(uid, comp, args)) - return; - - comp.Chemicals = 0f; - - if (_mobState.IsAlive(uid)) - { - // fake our death - var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", uid)); - _popup.PopupEntity(othersMessage, uid, Robust.Shared.Player.Filter.PvsExcept(uid), true); - - var selfMessage = Loc.GetString("changeling-stasis-enter"); - _popup.PopupEntity(selfMessage, uid, uid); - } - - if (!_mobState.IsDead(uid)) - _mobState.ChangeMobState(uid, MobState.Dead); - - comp.IsInStasis = true; - } - private void OnExitStasis(EntityUid uid, ChangelingComponent comp, ref ExitStasisEvent args) - { - if (!comp.IsInStasis) - { - _popup.PopupEntity(Loc.GetString("changeling-stasis-exit-fail"), uid, uid); - return; - } - if (HasComp(uid)) - { - _popup.PopupEntity(Loc.GetString("changeling-stasis-exit-fail-dead"), uid, uid); - return; - } - - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryComp(uid, out var damageable)) - return; - - // heal of everything - _damage.SetAllDamage(uid, damageable, 0); - _mobState.ChangeMobState(uid, MobState.Alive); - _blood.TryModifyBloodLevel(uid, 1000); - _blood.TryModifyBleedAmount(uid, -1000); - - _popup.PopupEntity(Loc.GetString("changeling-stasis-exit"), uid, uid); - - comp.IsInStasis = false; - } - - #endregion - - #region Combat Abilities - - private void OnToggleArmblade(EntityUid uid, ChangelingComponent comp, ref ToggleArmbladeEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryToggleItem(uid, ArmbladePrototype, comp)) - return; - - PlayMeatySound(uid, comp); - } - private void OnCreateBoneShard(EntityUid uid, ChangelingComponent comp, ref CreateBoneShardEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - var star = Spawn(BoneShardPrototype, Transform(uid).Coordinates); - _hands.TryPickupAnyHand(uid, star); - - PlayMeatySound(uid, comp); - } - private void OnToggleArmor(EntityUid uid, ChangelingComponent comp, ref ToggleChitinousArmorEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryToggleItem(uid, ArmorPrototype, comp, "outerClothing") - || !TryToggleItem(uid, ArmorHelmetPrototype, comp, "head")) - { - _popup.PopupEntity(Loc.GetString("changeling-equip-armor-fail"), uid, uid); - comp.Chemicals += Comp(args.Action).ChemicalCost; - return; - } - - PlayMeatySound(uid, comp); - } - private void OnToggleShield(EntityUid uid, ChangelingComponent comp, ref ToggleOrganicShieldEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryToggleItem(uid, ShieldPrototype, comp)) - return; - - PlayMeatySound(uid, comp); - } - private void OnShriekDissonant(EntityUid uid, ChangelingComponent comp, ref ShriekDissonantEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - DoScreech(uid, comp); - - var pos = _transform.GetMapCoordinates(uid); - var power = comp.ShriekPower; - _emp.EmpPulse(pos, power, 5000f, power * 2); - } - private void OnShriekResonant(EntityUid uid, ChangelingComponent comp, ref ShriekResonantEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - DoScreech(uid, comp); - - var power = comp.ShriekPower; - _flash.FlashArea(uid, uid, power, power * 2f * 1000f); - - var lookup = _lookup.GetEntitiesInRange(uid, power); - var lights = GetEntityQuery(); - - foreach (var ent in lookup) - if (lights.HasComponent(ent)) - _light.TryDestroyBulb(ent); - } - private void OnToggleStrainedMuscles(EntityUid uid, ChangelingComponent comp, ref ToggleStrainedMusclesEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - ToggleStrainedMuscles(uid, comp); - } - private void ToggleStrainedMuscles(EntityUid uid, ChangelingComponent comp) - { - if (!comp.StrainedMusclesActive) - { - _popup.PopupEntity(Loc.GetString("changeling-muscles-start"), uid, uid); - comp.StrainedMusclesActive = true; - } - else - { - _popup.PopupEntity(Loc.GetString("changeling-muscles-end"), uid, uid); - comp.StrainedMusclesActive = false; - } - - PlayMeatySound(uid, comp); - _speed.RefreshMovementSpeedModifiers(uid); - } - - #endregion - - #region Stings - - private void OnStingBlind(EntityUid uid, ChangelingComponent comp, ref StingBlindEvent args) - { - if (!TrySting(uid, comp, args)) - return; - - var target = args.Target; - if (!TryComp(target, out var blindable) || blindable.IsBlind) - return; - - _blindable.AdjustEyeDamage((target, blindable), 2); - var timeSpan = TimeSpan.FromSeconds(5f); - _statusEffect.TryAddStatusEffect(target, TemporaryBlindnessSystem.BlindingStatusEffect, timeSpan, false, TemporaryBlindnessSystem.BlindingStatusEffect); - } - private void OnStingCryo(EntityUid uid, ChangelingComponent comp, ref StingCryoEvent args) - { - var reagents = new List<(string, FixedPoint2)>() - { - ("Fresium", 20f), - ("ChloralHydrate", 10f) - }; - - if (!TryReagentSting(uid, comp, args, reagents)) - return; - } - private void OnStingLethargic(EntityUid uid, ChangelingComponent comp, ref StingLethargicEvent args) - { - var reagents = new List<(string, FixedPoint2)>() - { - ("Impedrezene", 10f), - ("MuteToxin", 5f) - }; - - if (!TryReagentSting(uid, comp, args, reagents)) - return; - } - private void OnStingMute(EntityUid uid, ChangelingComponent comp, ref StingMuteEvent args) - { - var reagents = new List<(string, FixedPoint2)>() - { - ("MuteToxin", 15f) - }; - - if (!TryReagentSting(uid, comp, args, reagents)) - return; - } - private void OnStingTransform(EntityUid uid, ChangelingComponent comp, ref StingTransformEvent args) - { - if (!TrySting(uid, comp, args, true)) - return; - - var target = args.Target; - if (!TryTransform(target, comp, true, true)) - comp.Chemicals += Comp(args.Action).ChemicalCost; - } - private void OnStingFakeArmblade(EntityUid uid, ChangelingComponent comp, ref StingFakeArmbladeEvent args) - { - if (!TrySting(uid, comp, args)) - return; - - var target = args.Target; - var fakeArmblade = EntityManager.SpawnEntity(FakeArmbladePrototype, Transform(target).Coordinates); - if (!_hands.TryPickupAnyHand(target, fakeArmblade)) - { - QueueDel(fakeArmblade); - comp.Chemicals += Comp(args.Action).ChemicalCost; - _popup.PopupEntity(Loc.GetString("changeling-sting-fail-simplemob"), uid, uid); - return; - } - - PlayMeatySound(target, comp); - } - - #endregion - - #region Utilities - - public void OnAnatomicPanacea(EntityUid uid, ChangelingComponent comp, ref ActionAnatomicPanaceaEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - var reagents = new List<(string, FixedPoint2)>() - { - ("Diphenhydramine", 5f), - ("Arithrazine", 5f), - ("Ethylredoxrazine", 5f) - }; - if (TryInjectReagents(uid, reagents)) - _popup.PopupEntity(Loc.GetString("changeling-panacea"), uid, uid); - else return; - PlayMeatySound(uid, comp); - } - public void OnAugmentedEyesight(EntityUid uid, ChangelingComponent comp, ref ActionAugmentedEyesightEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (HasComp(uid)) - { - _popup.PopupEntity(Loc.GetString("changeling-passive-active"), uid, uid); - return; - } - - EnsureComp(uid); - _popup.PopupEntity(Loc.GetString("changeling-passive-activate"), uid, uid); - } - public void OnBiodegrade(EntityUid uid, ChangelingComponent comp, ref ActionBiodegradeEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (TryComp(uid, out var cuffs) && cuffs.Container.ContainedEntities.Count > 0) - { - var cuff = cuffs.LastAddedCuffs; - - _cuffs.Uncuff(uid, cuffs.LastAddedCuffs, cuff); - QueueDel(cuff); - } - - var soln = new Solution(); - soln.AddReagent("PolytrinicAcid", 10f); - - if (_pull.IsPulled(uid)) - { - var puller = Comp(uid).Puller; - if (puller != null) - { - _puddle.TrySplashSpillAt((EntityUid) puller, Transform((EntityUid) puller).Coordinates, soln, out _); - return; - } - } - _puddle.TrySplashSpillAt(uid, Transform(uid).Coordinates, soln, out _); - } - public void OnChameleonSkin(EntityUid uid, ChangelingComponent comp, ref ActionChameleonSkinEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (HasComp(uid) && HasComp(uid)) - { - RemComp(uid); - RemComp(uid); - _popup.PopupEntity(Loc.GetString("changeling-chameleon-end"), uid, uid); - return; - } - - EnsureComp(uid); - EnsureComp(uid); - _popup.PopupEntity(Loc.GetString("changeling-chameleon-start"), uid, uid); - } - public void OnEphedrineOverdose(EntityUid uid, ChangelingComponent comp, ref ActionEphedrineOverdoseEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - var stam = EnsureComp(uid); - stam.StaminaDamage = 0; - - var reagents = new List<(string, FixedPoint2)>() - { - ("Desoxyephedrine", 5f) - }; - if (TryInjectReagents(uid, reagents)) - _popup.PopupEntity(Loc.GetString("changeling-inject"), uid, uid); - else - { - _popup.PopupEntity(Loc.GetString("changeling-inject-fail"), uid, uid); - return; - } - } - // john space made me do this - public void OnHealUltraSwag(EntityUid uid, ChangelingComponent comp, ref ActionFleshmendEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - var reagents = new List<(string, FixedPoint2)>() - { - ("Ichor", 10f), - ("TranexamicAcid", 5f) - }; - if (TryInjectReagents(uid, reagents)) - _popup.PopupEntity(Loc.GetString("changeling-fleshmend"), uid, uid); - else return; - PlayMeatySound(uid, comp); - } - public void OnLastResort(EntityUid uid, ChangelingComponent comp, ref ActionLastResortEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - // todo: implement - } - public void OnLesserForm(EntityUid uid, ChangelingComponent comp, ref ActionLesserFormEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - var newUid = TransformEntity(uid, protoId: "MobMonkey", comp: comp); - if (newUid == null) - { - comp.Chemicals += Comp(args.Action).ChemicalCost; - return; - } - - PlayMeatySound((EntityUid) newUid, comp); - var loc = Loc.GetString("changeling-transform-others", ("user", Identity.Entity((EntityUid) newUid, EntityManager))); - _popup.PopupEntity(loc, (EntityUid) newUid, PopupType.LargeCaution); - - comp.IsInLesserForm = true; - } - public void OnSpacesuit(EntityUid uid, ChangelingComponent comp, ref ActionSpacesuitEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (!TryToggleItem(uid, SpacesuitPrototype, comp, "outerClothing") - || !TryToggleItem(uid, SpacesuitHelmetPrototype, comp, "head")) - { - _popup.PopupEntity(Loc.GetString("changeling-equip-armor-fail"), uid, uid); - comp.Chemicals += Comp(args.Action).ChemicalCost; - return; - } - - PlayMeatySound(uid, comp); - } - public void OnHivemindAccess(EntityUid uid, ChangelingComponent comp, ref ActionHivemindAccessEvent args) - { - if (!TryUseAbility(uid, comp, args)) - return; - - if (HasComp(uid)) - { - _popup.PopupEntity(Loc.GetString("changeling-passive-active"), uid, uid); - return; - } - - EnsureComp(uid); - var reciever = EnsureComp(uid); - var transmitter = EnsureComp(uid); - var radio = EnsureComp(uid); - radio.Channels = new() { "Hivemind" }; - transmitter.Channels = new() { "Hivemind" }; - - _popup.PopupEntity(Loc.GetString("changeling-hivemind-start"), uid, uid); - } - - #endregion -} diff --git a/Content.Server/Goobstation/Changeling/ChangelingSystem.cs b/Content.Server/Goobstation/Changeling/ChangelingSystem.cs deleted file mode 100644 index 8b912004ff..0000000000 --- a/Content.Server/Goobstation/Changeling/ChangelingSystem.cs +++ /dev/null @@ -1,634 +0,0 @@ -using Content.Server.DoAfter; -using Content.Server.Forensics; -using Content.Server.Polymorph.Systems; -using Content.Server.Popups; -using Content.Server.Store.Systems; -using Content.Server.Zombies; -using Content.Shared.Alert; -using Content.Shared.Changeling; -using Content.Shared.Chemistry.Components; -using Content.Shared.Cuffs.Components; -using Content.Shared.FixedPoint; -using Content.Shared.Humanoid; -using Content.Shared.IdentityManagement; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition.Components; -using Content.Server.Store.Components; -using Robust.Server.Audio; -using Robust.Shared.Audio; -using Robust.Shared.Random; -using Content.Shared.Popups; -using Content.Shared.Damage; -using Robust.Shared.Prototypes; -using Content.Server.Body.Systems; -using Content.Shared.Actions; -using Content.Shared.Polymorph; -using Robust.Shared.Serialization.Manager; -using Content.Server.Actions; -using Content.Server.Humanoid; -using Content.Server.Polymorph.Components; -using Content.Shared.Chemistry.EntitySystems; -using Content.Server.Flash; -using Content.Server.Emp; -using Robust.Server.GameObjects; -using Content.Shared.Hands.EntitySystems; -using Content.Shared.Inventory; -using Content.Shared.Movement.Systems; -using Content.Shared.Damage.Systems; -using Content.Shared.Mind; -using Content.Server.Objectives.Components; -using Content.Server.Light.EntitySystems; -using Content.Shared.Eye.Blinding.Systems; -using Content.Shared.StatusEffect; -using Content.Shared.Movement.Pulling.Systems; -using Content.Shared.Cuffs; -using Content.Shared.Fluids; -using Content.Shared.Revolutionary.Components; -using Robust.Shared.Player; -using System.Numerics; -using Content.Shared.Camera; -using Robust.Shared.Timing; -using Content.Shared.Damage.Components; -using Content.Server.Gravity; -using Content.Shared.Mobs.Components; -using Content.Server.Stunnable; -using Content.Shared.Jittering; -using System.Linq; - -namespace Content.Server.Changeling; - -public sealed partial class ChangelingSystem : EntitySystem -{ - // this is one hell of a star wars intro text - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly IRobustRandom _rand = default!; - [Dependency] private readonly ActionsSystem _actions = default!; - [Dependency] private readonly StoreSystem _store = default!; - [Dependency] private readonly AudioSystem _audio = default!; - [Dependency] private readonly PolymorphSystem _polymorph = default!; - [Dependency] private readonly AlertsSystem _alerts = default!; - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly DoAfterSystem _doAfter = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly DamageableSystem _damage = default!; - [Dependency] private readonly BloodstreamSystem _blood = default!; - [Dependency] private readonly ISerializationManager _serialization = default!; - [Dependency] private readonly MetaDataSystem _metaData = default!; - [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; - [Dependency] private readonly TransformSystem _transform = default!; - [Dependency] private readonly FlashSystem _flash = default!; - [Dependency] private readonly EmpSystem _emp = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly PoweredLightSystem _light = default!; - [Dependency] private readonly ISharedPlayerManager _player = default!; - [Dependency] private readonly SharedCameraRecoilSystem _recoil = default!; - [Dependency] private readonly SharedHandsSystem _hands = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly MovementSpeedModifierSystem _speed = default!; - [Dependency] private readonly StaminaSystem _stamina = default!; - [Dependency] private readonly GravitySystem _gravity = default!; - [Dependency] private readonly BlindableSystem _blindable = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffect = default!; - [Dependency] private readonly PullingSystem _pull = default!; - [Dependency] private readonly SharedCuffableSystem _cuffs = default!; - [Dependency] private readonly SharedPuddleSystem _puddle = default!; - [Dependency] private readonly StunSystem _stun = default!; - [Dependency] private readonly SharedJitteringSystem _jitter = default!; - - public EntProtoId ArmbladePrototype = "ArmBladeChangeling"; - public EntProtoId FakeArmbladePrototype = "FakeArmBladeChangeling"; - - public EntProtoId ShieldPrototype = "ChangelingShield"; - public EntProtoId BoneShardPrototype = "ThrowingStarChangeling"; - - public EntProtoId ArmorPrototype = "ChangelingClothingOuterArmor"; - public EntProtoId ArmorHelmetPrototype = "ChangelingClothingHeadHelmet"; - - public EntProtoId SpacesuitPrototype = "ChangelingClothingOuterHardsuit"; - public EntProtoId SpacesuitHelmetPrototype = "ChangelingClothingHeadHelmetHardsuit"; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(OnMobStateChange); - SubscribeLocalEvent(OnDamageChange); - SubscribeLocalEvent(OnComponentRemove); - - SubscribeLocalEvent(OnRefreshSpeed); - - SubscribeAbilities(); - } - - private void OnRefreshSpeed(Entity ent, ref RefreshMovementSpeedModifiersEvent args) - { - if (ent.Comp.StrainedMusclesActive) - args.ModifySpeed(1.25f, 1.5f); - else - args.ModifySpeed(1f, 1f); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - if (!_timing.IsFirstTimePredicted) - return; - - foreach (var comp in EntityManager.EntityQuery()) - { - var uid = comp.Owner; - - if (_timing.CurTime < comp.UpdateTimer) - continue; - - comp.UpdateTimer = _timing.CurTime + TimeSpan.FromSeconds(comp.UpdateCooldown); - - Cycle(uid, comp); - } - } - public void Cycle(EntityUid uid, ChangelingComponent comp) - { - UpdateChemicals(uid, comp); - - comp.BiomassUpdateTimer += 1; - if (comp.BiomassUpdateTimer >= comp.BiomassUpdateCooldown) - { - comp.BiomassUpdateTimer = 0; - UpdateBiomass(uid, comp); - } - - UpdateAbilities(uid, comp); - } - - private void UpdateChemicals(EntityUid uid, ChangelingComponent comp, float? amount = null) - { - var chemicals = comp.Chemicals; - // either amount or regen - chemicals += amount ?? 1 + comp.BonusChemicalRegen; - comp.Chemicals = Math.Clamp(chemicals, 0, comp.MaxChemicals); - Dirty(uid, comp); - _alerts.ShowAlert(uid, AlertType.ChangelingChemicals); - } - private void UpdateBiomass(EntityUid uid, ChangelingComponent comp, float? amount = null) - { - comp.Biomass += amount ?? -1; - comp.Biomass = Math.Clamp(comp.Biomass, 0, comp.MaxBiomass); - Dirty(uid, comp); - _alerts.ShowAlert(uid, AlertType.ChangelingBiomass); - - var random = (int) _rand.Next(1, 3); - - if (comp.Biomass <= 0) - // game over, man - _damage.TryChangeDamage(uid, new DamageSpecifier(_proto.Index(AbsorbedDamageGroup), 50), true); - - if (comp.Biomass <= comp.MaxBiomass / 10) - { - // THE FUNNY ITCH IS REAL!! - comp.BonusChemicalRegen = 3f; - _popup.PopupEntity(Loc.GetString("popup-changeling-biomass-deficit-high"), uid, uid, PopupType.LargeCaution); - _jitter.DoJitter(uid, TimeSpan.FromSeconds(comp.BiomassUpdateCooldown), true, amplitude: 5, frequency: 10); - } - else if (comp.Biomass <= comp.MaxBiomass / 3) - { - // vomit blood - if (random == 1) - { - if (TryComp(uid, out var status)) - _stun.TrySlowdown(uid, TimeSpan.FromSeconds(1.5f), true, 0.5f, 0.5f, status); - - var solution = new Solution(); - - var vomitAmount = 15f; - _blood.TryModifyBloodLevel(uid, -vomitAmount); - solution.AddReagent("Blood", vomitAmount); - - _puddle.TrySplashSpillAt(uid, Transform(uid).Coordinates, solution, out _); - - _popup.PopupEntity(Loc.GetString("disease-vomit", ("person", Identity.Entity(uid, EntityManager))), uid); - } - - // the funny itch is not real - if (random == 3) - { - _popup.PopupEntity(Loc.GetString("popup-changeling-biomass-deficit-medium"), uid, uid, PopupType.MediumCaution); - _jitter.DoJitter(uid, TimeSpan.FromSeconds(.5f), true, amplitude: 5, frequency: 10); - } - } - else if (comp.Biomass <= comp.MaxBiomass / 2 && random == 3) - { - if (random == 1) - _popup.PopupEntity(Loc.GetString("popup-changeling-biomass-deficit-low"), uid, uid, PopupType.SmallCaution); - } - else comp.BonusChemicalRegen = 0f; - } - private void UpdateAbilities(EntityUid uid, ChangelingComponent comp) - { - _speed.RefreshMovementSpeedModifiers(uid); - if (comp.StrainedMusclesActive) - { - var stamina = EnsureComp(uid); - _stamina.TakeStaminaDamage(uid, 7.5f, visual: false); - if (stamina.StaminaDamage >= stamina.CritThreshold || _gravity.IsWeightless(uid)) - ToggleStrainedMuscles(uid, comp); - } - } - - #region Helper Methods - - public void PlayMeatySound(EntityUid uid, ChangelingComponent comp) - { - var rand = _rand.Next(0, comp.SoundPool.Count - 1); - var sound = comp.SoundPool.ToArray()[rand]; - _audio.PlayPvs(sound, uid, AudioParams.Default.WithVolume(-3f)); - } - public void DoScreech(EntityUid uid, ChangelingComponent comp) - { - _audio.PlayPvs(comp.ShriekSound, uid); - - var center = Transform(uid).MapPosition; - var gamers = Filter.Empty(); - gamers.AddInRange(center, comp.ShriekPower, _player, EntityManager); - - foreach (var gamer in gamers.Recipients) - { - if (gamer.AttachedEntity == null) - continue; - - var pos = Transform(gamer.AttachedEntity!.Value).WorldPosition; - var delta = center.Position - pos; - - if (delta.EqualsApprox(Vector2.Zero)) - delta = new(.01f, 0); - - _recoil.KickCamera(uid, -delta.Normalized()); - } - } - - /// - /// Check if a target is crit/dead or cuffed. For absorbing. - /// - public bool IsIncapacitated(EntityUid uid) - { - if (_mobState.IsIncapacitated(uid) - || (TryComp(uid, out var cuffs) && cuffs.CuffedHandCount > 0)) - return true; - - return false; - } - - public bool TryUseAbility(EntityUid uid, ChangelingComponent comp, BaseActionEvent action) - { - if (action.Handled) - return false; - - if (!TryComp(action.Action, out var lingAction)) - return false; - - if (comp.Biomass < 1 && lingAction.RequireBiomass) - { - _popup.PopupEntity(Loc.GetString("changeling-biomass-deficit"), uid, uid); - return false; - } - - if (!lingAction.UseInLesserForm && comp.IsInLesserForm) - { - _popup.PopupEntity(Loc.GetString("changeling-action-fail-lesserform"), uid, uid); - return false; - } - - if (comp.Chemicals < lingAction.ChemicalCost) - { - _popup.PopupEntity(Loc.GetString("changeling-chemicals-deficit"), uid, uid); - return false; - } - - if (lingAction.RequireAbsorbed > comp.TotalAbsorbedEntities) - { - var delta = lingAction.RequireAbsorbed - comp.TotalAbsorbedEntities; - _popup.PopupEntity(Loc.GetString("changeling-action-fail-absorbed", ("number", delta)), uid, uid); - return false; - } - - UpdateChemicals(uid, comp, -lingAction.ChemicalCost); - UpdateBiomass(uid, comp, -lingAction.BiomassCost); - - action.Handled = true; - - return true; - } - public bool TrySting(EntityUid uid, ChangelingComponent comp, EntityTargetActionEvent action, bool overrideMessage = false) - { - if (!TryUseAbility(uid, comp, action)) - return false; - - var target = action.Target; - - // can't get his dna if he doesn't have it! - if (!HasComp(target) || HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-sting-extract-fail"), uid, uid); - return false; - } - - if (HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-sting-fail-self", ("target", Identity.Entity(target, EntityManager))), uid, uid); - _popup.PopupEntity(Loc.GetString("changeling-sting-fail-ling"), target, target); - return false; - } - if (!overrideMessage) - _popup.PopupEntity(Loc.GetString("changeling-sting", ("target", Identity.Entity(target, EntityManager))), uid, uid); - return true; - } - public bool TryInjectReagents(EntityUid uid, List<(string, FixedPoint2)> reagents) - { - var solution = new Solution(); - foreach (var reagent in reagents) - solution.AddReagent(reagent.Item1, reagent.Item2); - - if (!_solution.TryGetInjectableSolution(uid, out var targetSolution, out var _)) - return false; - - if (!_solution.TryAddSolution(targetSolution.Value, solution)) - return false; - - return true; - } - public bool TryReagentSting(EntityUid uid, ChangelingComponent comp, EntityTargetActionEvent action, List<(string, FixedPoint2)> reagents) - { - var target = action.Target; - if (!TrySting(uid, comp, action)) - return false; - - if (!TryInjectReagents(target, reagents)) - return false; - - return true; - } - public bool TryToggleItem(EntityUid uid, EntProtoId proto, ChangelingComponent comp, string? clothingSlot = null) - { - if (!comp.Equipment.TryGetValue(proto.Id, out var item) && item == null) - { - item = Spawn(proto, Transform(uid).Coordinates); - if (clothingSlot != null && !_inventory.TryEquip(uid, (EntityUid) item, clothingSlot, force: true)) - { - QueueDel(item); - return false; - } - else if (!_hands.TryForcePickupAnyHand(uid, (EntityUid) item)) - { - _popup.PopupEntity(Loc.GetString("changeling-fail-hands"), uid, uid); - QueueDel(item); - return false; - } - comp.Equipment.Add(proto.Id, item); - return true; - } - - QueueDel(item); - // assuming that it exists - comp.Equipment.Remove(proto.Id); - - return true; - } - - public bool TryStealDNA(EntityUid uid, EntityUid target, ChangelingComponent comp, bool countObjective = false) - { - if (!TryComp(target, out var appearance) - || !TryComp(target, out var metadata) - || !TryComp(target, out var dna) - || !TryComp(target, out var fingerprint)) - return false; - - foreach (var storedDNA in comp.AbsorbedDNA) - { - if (storedDNA.DNA != null && storedDNA.DNA == dna.DNA) - return false; - } - - var data = new TransformData - { - Name = metadata.EntityName, - DNA = dna.DNA, - Appearance = appearance - }; - - if (fingerprint.Fingerprint != null) - data.Fingerprint = fingerprint.Fingerprint; - - if (comp.AbsorbedDNA.Count >= comp.MaxAbsorbedDNA) - _popup.PopupEntity(Loc.GetString("changeling-sting-extract-max"), uid, uid); - else comp.AbsorbedDNA.Add(data); - - if (countObjective - && _mind.TryGetMind(uid, out var mindId, out var mind) - && _mind.TryGetObjectiveComp(mindId, out var objective, mind)) - { - objective.DNAStolen += 1; - } - - comp.TotalStolenDNA++; - - return true; - } - - private ChangelingComponent? CopyChangelingComponent(EntityUid target, ChangelingComponent comp) - { - var newComp = EnsureComp(target); - newComp.AbsorbedDNA = comp.AbsorbedDNA; - newComp.AbsorbedDNAIndex = comp.AbsorbedDNAIndex; - - newComp.Chemicals = comp.Chemicals; - newComp.MaxChemicals = comp.MaxChemicals; - - newComp.Biomass = comp.Biomass; - newComp.MaxBiomass = comp.MaxBiomass; - - newComp.IsInLesserForm = comp.IsInLesserForm; - newComp.CurrentForm = comp.CurrentForm; - - newComp.TotalAbsorbedEntities = comp.TotalAbsorbedEntities; - newComp.TotalStolenDNA = comp.TotalStolenDNA; - - return comp; - } - private EntityUid? TransformEntity(EntityUid uid, TransformData? data = null, EntProtoId? protoId = null, ChangelingComponent? comp = null, bool persistentDna = false) - { - EntProtoId? pid = null; - - if (data != null) - { - if (!_proto.TryIndex(data.Appearance.Species, out var species)) - return null; - pid = species.Prototype; - } - else if (protoId != null) - pid = protoId; - else return null; - - var config = new PolymorphConfiguration() - { - Entity = (EntProtoId) pid, - TransferDamage = true, - Forced = true, - Inventory = PolymorphInventoryChange.Transfer, - RevertOnCrit = false, - RevertOnDeath = false - }; - var newUid = _polymorph.PolymorphEntity(uid, config); - - if (newUid == null) - return null; - - var newEnt = newUid.Value; - - if (data != null) - { - Comp(newEnt).Fingerprint = data.Fingerprint; - Comp(newEnt).DNA = data.DNA; - _humanoid.CloneAppearance(data.Appearance.Owner, newEnt); - _metaData.SetEntityName(newEnt, data.Name); - var message = Loc.GetString("changeling-transform-finish", ("target", data.Name)); - _popup.PopupEntity(message, newEnt, newEnt); - } - - RemCompDeferred(newEnt); - - if (comp != null) - { - // copy our stuff - var newLingComp = CopyChangelingComponent(newEnt, comp); - if (!persistentDna && data != null) - newLingComp?.AbsorbedDNA.Remove(data); - RemCompDeferred(uid); - - if (TryComp(uid, out var storeComp)) - { - var storeCompCopy = _serialization.CreateCopy(storeComp, notNullableOverride: true); - RemComp(newUid.Value); - EntityManager.AddComponent(newUid.Value, storeCompCopy); - } - } - - // exceptional comps check - // there's no foreach for types i believe so i gotta thug it out yandev style. - if (HasComp(uid)) - EnsureComp(newEnt); - if (HasComp(uid)) - EnsureComp(newEnt); - - QueueDel(uid); - - return newUid; - } - public bool TryTransform(EntityUid target, ChangelingComponent comp, bool sting = false, bool persistentDna = false) - { - if (HasComp(target)) - { - _popup.PopupEntity(Loc.GetString("changeling-transform-fail-absorbed"), target, target); - return false; - } - - var data = comp.SelectedForm; - - if (data == null) - { - _popup.PopupEntity(Loc.GetString("changeling-transform-fail-self"), target, target); - return false; - } - if (data == comp.CurrentForm) - { - _popup.PopupEntity(Loc.GetString("changeling-transform-fail-choose"), target, target); - return false; - } - - var locName = Identity.Entity(target, EntityManager); - EntityUid? newUid = null; - if (sting) - newUid = TransformEntity(target, data: data, persistentDna: persistentDna); - else newUid = TransformEntity(target, data: data, comp: comp, persistentDna: persistentDna); - - if (newUid != null) - { - PlayMeatySound((EntityUid) newUid, comp); - var loc = Loc.GetString("changeling-transform-others", ("user", locName)); - _popup.PopupEntity(loc, (EntityUid) newUid, PopupType.LargeCaution); - } - - return true; - } - - public void RemoveAllChangelingEquipment(EntityUid target, ChangelingComponent comp) - { - // check if there's no entities or all entities are null - if (comp.Equipment.Values.Count == 0 - || comp.Equipment.Values.All(ent => ent == null ? true : false)) - return; - - foreach (var equip in comp.Equipment.Values) - QueueDel(equip); - - PlayMeatySound(target, comp); - } - - #endregion - - #region Event Handlers - - private void OnStartup(EntityUid uid, ChangelingComponent comp, ref ComponentStartup args) - { - RemComp(uid); - RemComp(uid); - EnsureComp(uid); - - // add actions - foreach (var actionId in comp.BaseChangelingActions) - _actions.AddAction(uid, actionId); - - // making sure things are right in this world - comp.Chemicals = comp.MaxChemicals; - comp.Biomass = comp.MaxBiomass; - - // show alerts - UpdateChemicals(uid, comp, 0); - UpdateBiomass(uid, comp, 0); - // make their blood unreal - _blood.ChangeBloodReagent(uid, "BloodChangeling"); - } - - private void OnMobStateChange(EntityUid uid, ChangelingComponent comp, ref MobStateChangedEvent args) - { - if (args.NewMobState == MobState.Dead) - RemoveAllChangelingEquipment(uid, comp); - } - - private void OnDamageChange(Entity ent, ref DamageChangedEvent args) - { - var target = args.Damageable; - - if (!TryComp(ent, out var mobState)) - return; - - if (mobState.CurrentState != MobState.Dead) - return; - - if (!args.DamageIncreased) - return; - - target.Damage.ClampMax(200); // we never die. UNLESS?? - } - - private void OnComponentRemove(Entity ent, ref ComponentRemove args) - { - RemoveAllChangelingEquipment(ent, ent.Comp); - } - - #endregion -} \ No newline at end of file diff --git a/Content.Server/Goobstation/GameTicking/Rules/ChangelingRuleSystem.cs b/Content.Server/Goobstation/GameTicking/Rules/ChangelingRuleSystem.cs deleted file mode 100644 index f70aebbe4d..0000000000 --- a/Content.Server/Goobstation/GameTicking/Rules/ChangelingRuleSystem.cs +++ /dev/null @@ -1,122 +0,0 @@ -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.Changeling; -using Content.Server.NPC.Components; -using Content.Server.NPC.Systems; -using Content.Shared.Roles; -using Content.Shared.Store; -using Content.Server.Store.Components; -using Robust.Shared.Audio; -using Robust.Shared.Prototypes; -using System.Text; - -namespace Content.Server.GameTicking.Rules; - -public sealed partial class ChangelingRuleSystem : GameRuleSystem -{ - [Dependency] private readonly MindSystem _mind = default!; - [Dependency] private readonly AntagSelectionSystem _antag = default!; - [Dependency] private readonly SharedRoleSystem _role = default!; - [Dependency] private readonly NpcFactionSystem _npcFaction = default!; - [Dependency] private readonly ObjectivesSystem _objective = default!; - - public readonly SoundSpecifier BriefingSound = new SoundPathSpecifier("/Audio/Goobstation/Ambience/Antag/changeling_start.ogg"); - - public readonly ProtoId ChangelingPrototypeId = "Changeling"; - - public readonly ProtoId ChangelingFactionId = "Changeling"; - - public readonly ProtoId NanotrasenFactionId = "NanoTrasen"; - - public readonly ProtoId Currency = "EvolutionPoint"; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnSelectAntag); - SubscribeLocalEvent(OnTextPrepend); - } - - private void OnSelectAntag(EntityUid uid, ChangelingRuleComponent comp, ref AfterAntagEntitySelectedEvent args) - { - MakeChangeling(args.EntityUid, comp); - } - public bool MakeChangeling(EntityUid target, ChangelingRuleComponent rule) - { - if (!_mind.TryGetMind(target, out var mindId, out var mind)) - return false; - - // briefing - if (TryComp(target, out var metaData)) - { - var briefing = Loc.GetString("changeling-role-greeting", ("name", metaData?.EntityName ?? "Unknown")); - var briefingShort = Loc.GetString("changeling-role-greeting-short", ("name", metaData?.EntityName ?? "Unknown")); - - _antag.SendBriefing(target, briefing, Color.Yellow, BriefingSound); - _role.MindAddRole(mindId, new RoleBriefingComponent { Briefing = briefingShort }, mind, true); - } - // hivemind stuff - _npcFaction.RemoveFaction(target, NanotrasenFactionId, false); - _npcFaction.AddFaction(target, ChangelingFactionId); - - // make sure it's initial chems are set to max - EnsureComp(target); - - // add store - var store = EnsureComp(target); - foreach (var category in rule.StoreCategories) - store.Categories.Add(category); - store.CurrencyWhitelist.Add(Currency); - store.Balance.Add(Currency, 16); - - rule.ChangelingMinds.Add(mindId); - - foreach (var objective in rule.Objectives) - _mind.TryAddObjective(mindId, mind, objective); - - return true; - } - - private void OnTextPrepend(EntityUid uid, ChangelingRuleComponent comp, ref ObjectivesTextPrependEvent args) - { - var mostAbsorbedName = string.Empty; - var mostStolenName = string.Empty; - var mostAbsorbed = 0f; - var mostStolen = 0f; - - foreach (var ling in EntityQuery()) - { - if (!_mind.TryGetMind(ling.Owner, out var mindId, out var mind)) - continue; - - if (!TryComp(ling.Owner, out var metaData)) - continue; - - if (ling.TotalAbsorbedEntities > mostAbsorbed) - { - mostAbsorbed = ling.TotalAbsorbedEntities; - mostAbsorbedName = _objective.GetTitle(mindId); - if (mostAbsorbedName is null) - mostAbsorbedName = String.Empty; - } - if (ling.TotalStolenDNA > mostStolen) - { - mostStolen = ling.TotalStolenDNA; - mostStolenName = _objective.GetTitle(mindId); - if (mostStolenName is null) - mostStolenName = String.Empty; - - } - } - - var sb = new StringBuilder(); - sb.AppendLine(Loc.GetString($"roundend-prepend-changeling-absorbed{(!string.IsNullOrWhiteSpace(mostAbsorbedName) ? "-named" : "")}", ("name", mostAbsorbedName), ("number", mostAbsorbed))); - sb.AppendLine(Loc.GetString($"roundend-prepend-changeling-stolen{(!string.IsNullOrWhiteSpace(mostStolenName) ? "-named" : "")}", ("name", mostStolenName), ("number", mostStolen))); - - args.Text = sb.ToString(); - } -} diff --git a/Content.Server/Goobstation/GameTicking/Rules/Components/ChangelingRuleComponent.cs b/Content.Server/Goobstation/GameTicking/Rules/Components/ChangelingRuleComponent.cs deleted file mode 100644 index 2a23375ad5..0000000000 --- a/Content.Server/Goobstation/GameTicking/Rules/Components/ChangelingRuleComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Content.Shared.Store; -using Robust.Shared.Prototypes; - -namespace Content.Server.GameTicking.Rules.Components; - -[RegisterComponent, Access(typeof(ChangelingRuleSystem))] -public sealed partial class ChangelingRuleComponent : Component -{ - public readonly List ChangelingMinds = new(); - - public readonly List> StoreCategories = new() - { - "ChangelingAbilityCombat", - "ChangelingAbilitySting", - "ChangelingAbilityUtility" - }; - - public readonly List> Objectives = new() - { - "ChangelingSurviveObjective", - "ChangelingStealDNAObjective", - "EscapeIdentityObjective" - }; -} diff --git a/Content.Server/Goobstation/Objectives/Components/AbsorbConditionComponent.cs b/Content.Server/Goobstation/Objectives/Components/AbsorbConditionComponent.cs deleted file mode 100644 index 764e90d763..0000000000 --- a/Content.Server/Goobstation/Objectives/Components/AbsorbConditionComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Content.Server.Changeling; -using Content.Server.Objectives.Systems; - -namespace Content.Server.Objectives.Components; - -[RegisterComponent, Access(typeof(ChangelingObjectiveSystem), typeof(ChangelingSystem))] -public sealed partial class AbsorbConditionComponent : Component -{ - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float Absorbed = 0f; -} diff --git a/Content.Server/Goobstation/Objectives/Components/ImpersonateConditionComponent.cs b/Content.Server/Goobstation/Objectives/Components/ImpersonateConditionComponent.cs deleted file mode 100644 index f94d5db6f9..0000000000 --- a/Content.Server/Goobstation/Objectives/Components/ImpersonateConditionComponent.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Content.Server.Objectives.Systems; - -namespace Content.Server.Objectives.Components; - -/// -/// Requires that you have the same identity a target for a certain length of time before the round ends. -/// Obviously the agent id will work for this, but it's assumed that you will kill the target to prevent suspicion. -/// Depends on to function. -/// -[RegisterComponent, Access(typeof(ImpersonateConditionSystem))] -public sealed partial class ImpersonateConditionComponent : Component -{ - /// - /// Name that must match your identity for greentext. - /// This is stored once after the objective is assigned: - /// 1. to be a tiny bit more efficient - /// 2. to prevent the name possibly changing when borging or anything else and messing you up - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public string? Name; - - /// - /// Mind this objective got assigned to, used to continiously checkd impersonation. - /// - [DataField, ViewVariables(VVAccess.ReadWrite)] - public EntityUid? MindId; - - public bool Completed = false; -} diff --git a/Content.Server/Goobstation/Objectives/Components/StealDNAConditionComponent.cs b/Content.Server/Goobstation/Objectives/Components/StealDNAConditionComponent.cs deleted file mode 100644 index 409ff8a30a..0000000000 --- a/Content.Server/Goobstation/Objectives/Components/StealDNAConditionComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Content.Server.Changeling; -using Content.Server.Objectives.Systems; - -namespace Content.Server.Objectives.Components; - -[RegisterComponent, Access(typeof(ChangelingObjectiveSystem), typeof(ChangelingSystem))] -public sealed partial class StealDNAConditionComponent : Component -{ - [DataField, ViewVariables(VVAccess.ReadWrite)] - public float DNAStolen = 0f; -} diff --git a/Content.Server/Goobstation/Objectives/Systems/ChangelingObjectiveSystem.cs b/Content.Server/Goobstation/Objectives/Systems/ChangelingObjectiveSystem.cs deleted file mode 100644 index 6633bb07de..0000000000 --- a/Content.Server/Goobstation/Objectives/Systems/ChangelingObjectiveSystem.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Content.Server.Objectives.Components; -using Content.Shared.Objectives.Components; - -namespace Content.Server.Objectives.Systems; - -public sealed partial class ChangelingObjectiveSystem : EntitySystem -{ - [Dependency] private readonly NumberObjectiveSystem _number = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAbsorbGetProgress); - SubscribeLocalEvent(OnStealDNAGetProgress); - } - - private void OnAbsorbGetProgress(EntityUid uid, AbsorbConditionComponent comp, ref ObjectiveGetProgressEvent args) - { - var target = _number.GetTarget(uid); - if (target != 0) - args.Progress = MathF.Min(comp.Absorbed / target, 1f); - else args.Progress = 1f; - } - private void OnStealDNAGetProgress(EntityUid uid, StealDNAConditionComponent comp, ref ObjectiveGetProgressEvent args) - { - var target = _number.GetTarget(uid); - if (target != 0) - args.Progress = MathF.Min(comp.DNAStolen / target, 1f); - else args.Progress = 1f; - } -} diff --git a/Content.Server/Goobstation/Objectives/Systems/ImpersonateConditionSystem.cs b/Content.Server/Goobstation/Objectives/Systems/ImpersonateConditionSystem.cs deleted file mode 100644 index 086f462fb6..0000000000 --- a/Content.Server/Goobstation/Objectives/Systems/ImpersonateConditionSystem.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Content.Server.Objectives.Components; -using Content.Server.Shuttles.Systems; -using Content.Shared.Cuffs.Components; -using Content.Shared.Mind; -using Content.Shared.Objectives.Components; - -namespace Content.Server.Objectives.Systems; - -/// -/// Handles escaping on the shuttle while being another person detection. -/// -public sealed class ImpersonateConditionSystem : EntitySystem -{ - [Dependency] private readonly TargetObjectiveSystem _target = default!; - [Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!; - [Dependency] private readonly SharedMindSystem _mind = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAfterAssign); - SubscribeLocalEvent(OnGetProgress); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) - { - if (comp.Name == null || comp.MindId == null) - continue; - - if (!TryComp(comp.MindId, out var mind) || mind.OwnedEntity == null) - continue; - if (!TryComp(mind.CurrentEntity, out var metaData)) - continue; - - if (metaData.EntityName == comp.Name) - comp.Completed = true; - else comp.Completed = false; - } - } - - private void OnAfterAssign(EntityUid uid, ImpersonateConditionComponent comp, ref ObjectiveAfterAssignEvent args) - { - if (!_target.GetTarget(uid, out var target)) - return; - - if (!TryComp(target, out var targetMind) || targetMind.CharacterName == null) - return; - - comp.Name = targetMind.CharacterName; - comp.MindId = args.MindId; - } - - // copypasta from escape shittle objective. eh. - private void OnGetProgress(EntityUid uid, ImpersonateConditionComponent comp, ref ObjectiveGetProgressEvent args) - { - args.Progress = GetProgress(args.Mind, comp); - } - - public float GetProgress(MindComponent mind, ImpersonateConditionComponent comp) - { - // not escaping alive if you're deleted/dead - if (mind.OwnedEntity == null || _mind.IsCharacterDeadIc(mind)) - return 0f; - // You're not escaping if you're restrained! - if (TryComp(mind.OwnedEntity, out var cuffed) && cuffed.CuffedHandCount > 0) - return 0f; - - return (_emergencyShuttle.IsTargetEscaping(mind.OwnedEntity.Value) ? .5f : 0f) + (comp.Completed ? .5f : 0f); - } -} diff --git a/Content.Server/Goobstation/Roles/ChangelingRoleComponent.cs b/Content.Server/Goobstation/Roles/ChangelingRoleComponent.cs deleted file mode 100644 index a1184c6795..0000000000 --- a/Content.Server/Goobstation/Roles/ChangelingRoleComponent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Shared.Roles; - -namespace Content.Server.Roles; - -[RegisterComponent, ExclusiveAntagonist] -public sealed partial class ChangelingRoleComponent : AntagonistRoleComponent -{ -} diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index bc39997735..fa263e059d 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -5,10 +5,10 @@ using Content.Server.Afk; using Content.Server.Chat.Managers; using Content.Server.Connection; +using Content.Server.DiscordAuth; using Content.Server.JoinQueue; using Content.Server.Database; using Content.Server.Discord; -using Content.Server.DiscordAuth; using Content.Server.EUI; using Content.Server.GhostKick; using Content.Server.Info; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/FreeProberRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/FreeProberRule.cs index 35bcb4c28b..46dff726e5 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/FreeProberRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/FreeProberRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Robust.Shared.Map; using Robust.Shared.Random; using Content.Server.GameTicking.Rules.Components; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs index bf32769c8f..078826604e 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Psionics.Glimmer; using Content.Shared.Psionics.Glimmer; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRandomSentienceRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRandomSentienceRule.cs index 578f8bf48b..c086462b40 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRandomSentienceRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRandomSentienceRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Ghost.Roles.Components; using Content.Server.Psionics; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRevenantSpawnRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRevenantSpawnRule.cs index 152d6d9fe5..8bab321db7 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRevenantSpawnRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerRevenantSpawnRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Robust.Shared.Random; using Content.Server.GameTicking.Rules.Components; using Content.Server.Psionics.Glimmer; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerWispSpawnRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerWispSpawnRule.cs index 90061c501a..89b5a176f2 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerWispSpawnRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerWispSpawnRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Robust.Shared.Random; using Content.Server.GameTicking.Rules.Components; using Content.Server.NPC.Components; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs index 871705b5e8..3be2eed638 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs @@ -1,6 +1,5 @@ using Robust.Shared.Random; using Content.Server.Psionics.Abilities; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Psionics; using Content.Server.StationEvents.Components; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs index 94a488bd84..7abbdcdab3 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Random; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericFryRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericFryRule.cs index b1ec62d190..d7880af903 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericFryRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericFryRule.cs @@ -3,7 +3,6 @@ using Robust.Shared.Player; using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; -using Content.Server.GameTicking.Components; using Content.Shared.Construction.EntitySystems; using Content.Server.GameTicking.Rules.Components; using Content.Server.Popups; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs index 84407954df..021c959102 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs @@ -1,6 +1,5 @@ using Robust.Shared.Random; using Content.Server.Psionics.Abilities; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.Psionics; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs index ae85e16e9e..a11faa0693 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Popups; using Content.Server.Psionics; diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs index 4b9fb6ae53..753b2e2572 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Robust.Shared.Random; using Robust.Shared.Player; using Content.Server.Psionics; diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index 47fe4eb5f8..20205b8b72 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -1,7 +1,10 @@ using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Mind; using Content.Server.Shuttles.Systems; using Content.Shared.Cuffs.Components; using Content.Shared.Mind; +using Content.Shared.Mobs.Systems; using Content.Shared.Objectives.Components; using Content.Shared.Objectives.Systems; using Content.Shared.Random; @@ -9,9 +12,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using System.Linq; -using Content.Server.GameTicking.Components; using System.Text; -using Robust.Server.Player; namespace Content.Server.Objectives; @@ -19,8 +20,8 @@ public sealed class ObjectivesSystem : SharedObjectivesSystem { [Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!; public override void Initialize() @@ -178,9 +179,7 @@ private void AddSummary(StringBuilder result, string agent, List mind .ThenByDescending(x => x.completedObjectives); foreach (var (summary, _, _) in sortedAgents) - { result.AppendLine(summary); - } } public EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, string objectiveGroupProto) @@ -245,14 +244,8 @@ private bool IsInCustody(EntityUid mindId, MindComponent? mind = null) return null; var name = mind.CharacterName; - var username = (string?) null; - - if (mind.OriginalOwnerUserId != null && - _player.TryGetPlayerData(mind.OriginalOwnerUserId.Value, out var sessionData)) - { - username = sessionData.UserName; - } - + _mind.TryGetSession(mindId, out var session); + var username = session?.Name; if (username != null) { diff --git a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs index 0e20f007d7..107d09c898 100644 --- a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs @@ -17,7 +17,6 @@ using Robust.Shared.Utility; using System.Linq; using System.Diagnostics.CodeAnalysis; -using Content.Server.GameTicking.Components; namespace Content.Server.Power.EntitySystems; @@ -724,8 +723,8 @@ private void GetLoadsForNode(EntityUid uid, Node node, out List> GetSelectedProfilesForPlayers(List userIds); bool HavePreferencesLoaded(ICommonSession session); } diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index c3efe14be9..e262fde64d 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -256,20 +256,6 @@ public PlayerPreferences GetPreferences(NetUserId userId) return prefs; } - /// - /// Retrieves preferences for the given username from storage or returns null. - /// Creates and saves default preferences if they are not found, then returns them. - /// - public PlayerPreferences? GetPreferencesOrNull(NetUserId? userId) - { - if (userId == null) - return null; - - if (_cachedPlayerPrefs.TryGetValue(userId.Value, out var pref)) - return pref.Prefs; - return null; - } - private async Task GetOrCreatePreferencesAsync(NetUserId userId) { var prefs = await _db.GetPlayerPreferencesAsync(userId); diff --git a/Content.Server/RandomMetadata/RandomMetadataSystem.cs b/Content.Server/RandomMetadata/RandomMetadataSystem.cs index 0c254c52ac..c088d57fd9 100644 --- a/Content.Server/RandomMetadata/RandomMetadataSystem.cs +++ b/Content.Server/RandomMetadata/RandomMetadataSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Dataset; +using Content.Shared.Dataset; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -47,12 +47,9 @@ public string GetRandomFromSegments(List segments, string? separator) var outputSegments = new List(); foreach (var segment in segments) { - if (_prototype.TryIndex(segment, out var proto)) - outputSegments.Add(_random.Pick(proto.Values)); - else if (Loc.TryGetString(segment, out var localizedSegment)) - outputSegments.Add(localizedSegment); - else - outputSegments.Add(segment); + outputSegments.Add(_prototype.TryIndex(segment, out var proto) + ? Loc.GetString(_random.Pick(proto.Values)) + : Loc.GetString(segment)); } return string.Join(separator, outputSegments); } diff --git a/Content.Server/Roles/RoleSystem.cs b/Content.Server/Roles/RoleSystem.cs index 7b18485787..f7a5177357 100644 --- a/Content.Server/Roles/RoleSystem.cs +++ b/Content.Server/Roles/RoleSystem.cs @@ -19,7 +19,6 @@ public override void Initialize() SubscribeAntagEvents(); SubscribeAntagEvents(); SubscribeAntagEvents(); - SubscribeAntagEvents(); } public string? MindGetBriefing(EntityUid? mindId) diff --git a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs index 75f8618798..506fd61d55 100644 --- a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs +++ b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs @@ -1,6 +1,5 @@ using System.Numerics; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Spawners.Components; using JetBrains.Annotations; diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 2fdfb0fa86..07837361d6 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -174,7 +174,7 @@ public EntityUid SpawnPlayerMob( if (prototype?.StartingGear != null) { var startingGear = _prototypeManager.Index(prototype.StartingGear); - EquipStartingGear(entity.Value, startingGear); + EquipStartingGear(entity.Value, startingGear, profile); if (profile != null) EquipIdCard(entity.Value, profile.Name, prototype, station); InternalEncryptionKeySpawner.TryInsertEncryptionKey(entity.Value, startingGear, EntityManager, profile); // Parkstation - IPC diff --git a/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs b/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs index b9eb3b7b09..0243a00c9a 100644 --- a/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs @@ -1,6 +1,5 @@ using System.Linq; using Content.Server.Administration; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Components/LoneOpsSpawnRuleComponent.cs b/Content.Server/StationEvents/Components/LoneOpsSpawnRuleComponent.cs new file mode 100644 index 0000000000..92911e0858 --- /dev/null +++ b/Content.Server/StationEvents/Components/LoneOpsSpawnRuleComponent.cs @@ -0,0 +1,18 @@ +using Content.Server.StationEvents.Events; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.StationEvents.Components; + +[RegisterComponent, Access(typeof(LoneOpsSpawnRule))] +public sealed partial class LoneOpsSpawnRuleComponent : Component +{ + [DataField("loneOpsShuttlePath")] + public string LoneOpsShuttlePath = "Maps/Shuttles/striker.yml"; + + [DataField("gameRuleProto", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string GameRuleProto = "Nukeops"; + + [DataField("additionalRule")] + public EntityUid? AdditionalRule; +} diff --git a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs index 98d5aa76a6..4cd94d3e71 100644 --- a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs +++ b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs @@ -1,5 +1,4 @@ using Content.Server.Anomaly; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs index 29c1897657..b25c1d6561 100644 --- a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs +++ b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs @@ -1,5 +1,4 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Random; using Content.Server.Announcements.Systems; diff --git a/Content.Server/StationEvents/Events/BluespaceLockerRule.cs b/Content.Server/StationEvents/Events/BluespaceLockerRule.cs index eef9850e73..709b750334 100644 --- a/Content.Server/StationEvents/Events/BluespaceLockerRule.cs +++ b/Content.Server/StationEvents/Events/BluespaceLockerRule.cs @@ -1,5 +1,4 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Server.Resist; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/BreakerFlipRule.cs b/Content.Server/StationEvents/Events/BreakerFlipRule.cs index 3b2368556b..e7574f27ad 100644 --- a/Content.Server/StationEvents/Events/BreakerFlipRule.cs +++ b/Content.Server/StationEvents/Events/BreakerFlipRule.cs @@ -1,5 +1,4 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Station.Components; diff --git a/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs b/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs index 282e28e499..feb88d9b84 100644 --- a/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs +++ b/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.Station.Systems; diff --git a/Content.Server/StationEvents/Events/CargoGiftsRule.cs b/Content.Server/StationEvents/Events/CargoGiftsRule.cs index 62f01f58fe..80af23c6fa 100644 --- a/Content.Server/StationEvents/Events/CargoGiftsRule.cs +++ b/Content.Server/StationEvents/Events/CargoGiftsRule.cs @@ -2,7 +2,6 @@ using Content.Server.Cargo.Components; using Content.Server.Cargo.Systems; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/ClericalErrorRule.cs b/Content.Server/StationEvents/Events/ClericalErrorRule.cs index 854ee685b3..dd4473952c 100644 --- a/Content.Server/StationEvents/Events/ClericalErrorRule.cs +++ b/Content.Server/StationEvents/Events/ClericalErrorRule.cs @@ -1,5 +1,4 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.StationRecords; using Content.Server.StationRecords.Systems; diff --git a/Content.Server/StationEvents/Events/FalseAlarmRule.cs b/Content.Server/StationEvents/Events/FalseAlarmRule.cs index 2d129b3558..cd434a721b 100644 --- a/Content.Server/StationEvents/Events/FalseAlarmRule.cs +++ b/Content.Server/StationEvents/Events/FalseAlarmRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using JetBrains.Annotations; diff --git a/Content.Server/StationEvents/Events/GasLeakRule.cs b/Content.Server/StationEvents/Events/GasLeakRule.cs index 1221612171..68544e416c 100644 --- a/Content.Server/StationEvents/Events/GasLeakRule.cs +++ b/Content.Server/StationEvents/Events/GasLeakRule.cs @@ -1,5 +1,4 @@ using Content.Server.Atmos.EntitySystems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Audio; diff --git a/Content.Server/StationEvents/Events/ImmovableRodRule.cs b/Content.Server/StationEvents/Events/ImmovableRodRule.cs index 781d0368f4..a61c6b69e1 100644 --- a/Content.Server/StationEvents/Events/ImmovableRodRule.cs +++ b/Content.Server/StationEvents/Events/ImmovableRodRule.cs @@ -1,5 +1,4 @@ using System.Numerics; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.ImmovableRod; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/IonStormRule.cs b/Content.Server/StationEvents/Events/IonStormRule.cs index 8361cc6048..cd3cd63ae8 100644 --- a/Content.Server/StationEvents/Events/IonStormRule.cs +++ b/Content.Server/StationEvents/Events/IonStormRule.cs @@ -1,5 +1,5 @@ -using Content.Server.GameTicking.Components; using System.Linq; +using Content.Server.GameTicking.Rules.Components; using Content.Server.Silicons.Laws; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/KudzuGrowthRule.cs b/Content.Server/StationEvents/Events/KudzuGrowthRule.cs index 5b56e03846..3fa12cd4e9 100644 --- a/Content.Server/StationEvents/Events/KudzuGrowthRule.cs +++ b/Content.Server/StationEvents/Events/KudzuGrowthRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/LoneOpsSpawnRule.cs b/Content.Server/StationEvents/Events/LoneOpsSpawnRule.cs new file mode 100644 index 0000000000..4b15e59099 --- /dev/null +++ b/Content.Server/StationEvents/Events/LoneOpsSpawnRule.cs @@ -0,0 +1,47 @@ +using Robust.Server.GameObjects; +using Robust.Server.Maps; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.StationEvents.Components; +using Content.Server.RoundEnd; + +namespace Content.Server.StationEvents.Events; + +public sealed class LoneOpsSpawnRule : StationEventSystem +{ + [Dependency] private readonly MapLoaderSystem _map = default!; + + protected override void Started(EntityUid uid, LoneOpsSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + // Loneops can only spawn if there is no nukeops active + if (GameTicker.IsGameRuleAdded()) + { + ForceEndSelf(uid, gameRule); + return; + } + + var shuttleMap = MapManager.CreateMap(); + var options = new MapLoadOptions + { + LoadMap = true, + }; + + _map.TryLoad(shuttleMap, component.LoneOpsShuttlePath, out _, options); + + var nukeopsEntity = GameTicker.AddGameRule(component.GameRuleProto); + component.AdditionalRule = nukeopsEntity; + var nukeopsComp = Comp(nukeopsEntity); + nukeopsComp.SpawnOutpost = false; + nukeopsComp.RoundEndBehavior = RoundEndBehavior.Nothing; + GameTicker.StartGameRule(nukeopsEntity); + } + + protected override void Ended(EntityUid uid, LoneOpsSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) + { + base.Ended(uid, component, gameRule, args); + + if (component.AdditionalRule != null) + GameTicker.EndGameRule(component.AdditionalRule.Value); + } +} diff --git a/Content.Server/StationEvents/Events/MassHallucinationsRule.cs b/Content.Server/StationEvents/Events/MassHallucinationsRule.cs index 2239db7f70..4fc158f864 100644 --- a/Content.Server/StationEvents/Events/MassHallucinationsRule.cs +++ b/Content.Server/StationEvents/Events/MassHallucinationsRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.Traits.Assorted; diff --git a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs index 455011259d..ad56479b37 100644 --- a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs +++ b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs @@ -1,5 +1,4 @@ using System.Numerics; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Map; diff --git a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs index d9d68a386c..8ad5c8602e 100644 --- a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs +++ b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Ninja.Systems; using Content.Server.Station.Components; diff --git a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs index b0a0bbc9fe..97e8948461 100644 --- a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs +++ b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs @@ -1,5 +1,4 @@ using System.Threading; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; diff --git a/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs b/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs index 87d50fc8b2..c3cd719cc4 100644 --- a/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs +++ b/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.Storage.Components; diff --git a/Content.Server/StationEvents/Events/RandomSentienceRule.cs b/Content.Server/StationEvents/Events/RandomSentienceRule.cs index 7b9173241f..f667ad7975 100644 --- a/Content.Server/StationEvents/Events/RandomSentienceRule.cs +++ b/Content.Server/StationEvents/Events/RandomSentienceRule.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Ghost.Roles.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/RandomSpawnRule.cs b/Content.Server/StationEvents/Events/RandomSpawnRule.cs index 77744d44e4..c514acc623 100644 --- a/Content.Server/StationEvents/Events/RandomSpawnRule.cs +++ b/Content.Server/StationEvents/Events/RandomSpawnRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/SolarFlareRule.cs b/Content.Server/StationEvents/Events/SolarFlareRule.cs index 0370b4ee61..a4ec74b43b 100644 --- a/Content.Server/StationEvents/Events/SolarFlareRule.cs +++ b/Content.Server/StationEvents/Events/SolarFlareRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Radio; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/Events/StationEventSystem.cs b/Content.Server/StationEvents/Events/StationEventSystem.cs index 257babd0d2..6de8024bd0 100644 --- a/Content.Server/StationEvents/Events/StationEventSystem.cs +++ b/Content.Server/StationEvents/Events/StationEventSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Administration.Logs; using Content.Server.Chat.Systems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Systems; diff --git a/Content.Server/StationEvents/Events/VentClogRule.cs b/Content.Server/StationEvents/Events/VentClogRule.cs index 867f41dccc..e263a5f4f6 100644 --- a/Content.Server/StationEvents/Events/VentClogRule.cs +++ b/Content.Server/StationEvents/Events/VentClogRule.cs @@ -6,7 +6,6 @@ using Robust.Shared.Random; using System.Linq; using Content.Server.Fluids.EntitySystems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/VentCrittersRule.cs b/Content.Server/StationEvents/Events/VentCrittersRule.cs index c2605039bc..cdcf2bf6ff 100644 --- a/Content.Server/StationEvents/Events/VentCrittersRule.cs +++ b/Content.Server/StationEvents/Events/VentCrittersRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.StationEvents.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; diff --git a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs index a6c38ef765..aa0c9b214b 100644 --- a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs @@ -1,5 +1,4 @@ using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; diff --git a/Content.Server/Traitor/Components/AutoTraitorComponent.cs b/Content.Server/Traitor/Components/AutoTraitorComponent.cs index 473441ccec..ab4bee2f26 100644 --- a/Content.Server/Traitor/Components/AutoTraitorComponent.cs +++ b/Content.Server/Traitor/Components/AutoTraitorComponent.cs @@ -11,12 +11,12 @@ public sealed partial class AutoTraitorComponent : Component /// /// Whether to give the traitor an uplink or not. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField("giveUplink"), ViewVariables(VVAccess.ReadWrite)] public bool GiveUplink = true; /// /// Whether to give the traitor objectives or not. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField("giveObjectives"), ViewVariables(VVAccess.ReadWrite)] public bool GiveObjectives = true; } diff --git a/Content.Server/Traitor/Systems/AutoTraitorSystem.cs b/Content.Server/Traitor/Systems/AutoTraitorSystem.cs index e9307effbc..15deae2552 100644 --- a/Content.Server/Traitor/Systems/AutoTraitorSystem.cs +++ b/Content.Server/Traitor/Systems/AutoTraitorSystem.cs @@ -1,7 +1,6 @@ -using Content.Server.Antag; +using Content.Server.GameTicking.Rules; using Content.Server.Traitor.Components; using Content.Shared.Mind.Components; -using Robust.Shared.Prototypes; namespace Content.Server.Traitor.Systems; @@ -10,10 +9,7 @@ namespace Content.Server.Traitor.Systems; /// public sealed class AutoTraitorSystem : EntitySystem { - [Dependency] private readonly AntagSelectionSystem _antag = default!; - - [ValidatePrototypeId] - private const string DefaultTraitorRule = "Traitor"; + [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; public override void Initialize() { @@ -24,6 +20,44 @@ public override void Initialize() private void OnMindAdded(EntityUid uid, AutoTraitorComponent comp, MindAddedMessage args) { - _antag.ForceMakeAntag(args.Mind.Comp.Session, DefaultTraitorRule); + TryMakeTraitor(uid, comp); + } + + /// + /// Sets the GiveUplink field. + /// + public void SetGiveUplink(EntityUid uid, bool giveUplink, AutoTraitorComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + comp.GiveUplink = giveUplink; + } + + /// + /// Sets the GiveObjectives field. + /// + public void SetGiveObjectives(EntityUid uid, bool giveObjectives, AutoTraitorComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return; + + comp.GiveObjectives = giveObjectives; + } + + /// + /// Checks if there is a mind, then makes it a traitor using the options. + /// + public bool TryMakeTraitor(EntityUid uid, AutoTraitorComponent? comp = null) + { + if (!Resolve(uid, ref comp)) + return false; + + //Start the rule if it has not already been started + var traitorRuleComponent = _traitorRule.StartGameRule(); + _traitorRule.MakeTraitor(uid, traitorRuleComponent, giveUplink: comp.GiveUplink, giveObjectives: comp.GiveObjectives); + // prevent spamming anything if it fails + RemComp(uid); + return true; } } diff --git a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs index 79192f6b49..cdaed3f928 100644 --- a/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs +++ b/Content.Server/Traitor/Uplink/Commands/AddUplinkCommand.cs @@ -83,9 +83,12 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) uplinkEntity = eUid; } + // Get TC count + var tcCount = _cfgManager.GetCVar(CCVars.TraitorStartingBalance); + Logger.Debug(_entManager.ToPrettyString(user)); // Finally add uplink var uplinkSys = _entManager.System(); - if (!uplinkSys.AddUplink(user, 20, uplinkEntity: uplinkEntity)) + if (!uplinkSys.AddUplink(user, FixedPoint2.New(tcCount), uplinkEntity: uplinkEntity)) { shell.WriteLine(Loc.GetString("add-uplink-command-error-2")); } diff --git a/Content.Server/Zombies/PendingZombieComponent.cs b/Content.Server/Zombies/PendingZombieComponent.cs index 1bb0ef2872..a49b424c53 100644 --- a/Content.Server/Zombies/PendingZombieComponent.cs +++ b/Content.Server/Zombies/PendingZombieComponent.cs @@ -1,5 +1,4 @@ using Content.Shared.Damage; -using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Server.Zombies; @@ -36,21 +35,6 @@ public sealed partial class PendingZombieComponent : Component [DataField("gracePeriod"), ViewVariables(VVAccess.ReadWrite)] public TimeSpan GracePeriod = TimeSpan.Zero; - /// - /// The minimum amount of time initial infected have before they start taking infection damage. - /// - [DataField] - public TimeSpan MinInitialInfectedGrace = TimeSpan.FromMinutes(12.5f); - - /// - /// The maximum amount of time initial infected have before they start taking damage. - /// - [DataField] - public TimeSpan MaxInitialInfectedGrace = TimeSpan.FromMinutes(15f); - - [DataField] - public EntProtoId ZombifySelfActionPrototype = "ActionTurnUndead"; - /// /// The chance each second that a warning will be shown. /// diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index 09c8fa26db..080bef44e7 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.Actions; using Content.Server.Body.Systems; using Content.Server.Chat; using Content.Server.Chat.Systems; @@ -31,7 +30,6 @@ public sealed partial class ZombieSystem : SharedZombieSystem [Dependency] private readonly BloodstreamSystem _bloodstream = default!; [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly AutoEmoteSystem _autoEmote = default!; [Dependency] private readonly EmoteOnDamageSystem _emoteOnDamage = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; @@ -76,8 +74,6 @@ private void OnPendingMapInit(EntityUid uid, PendingZombieComponent component, M } component.NextTick = _timing.CurTime + TimeSpan.FromSeconds(1f); - component.GracePeriod = _random.Next(component.MinInitialInfectedGrace, component.MaxInitialInfectedGrace); - _actions.AddAction(uid, ref component.Action, component.ZombifySelfActionPrototype); } public override void Update(float frameTime) diff --git a/Content.Shared/Actions/ActionContainerSystem.cs b/Content.Shared/Actions/ActionContainerSystem.cs index dec57bdcc7..1c5a3ba0d9 100644 --- a/Content.Shared/Actions/ActionContainerSystem.cs +++ b/Content.Shared/Actions/ActionContainerSystem.cs @@ -237,19 +237,6 @@ public bool AddAction(EntityUid uid, EntityUid actionId, BaseActionComponent? ac DebugTools.AssertOwner(uid, comp); comp ??= EnsureComp(uid); - - // goobstation - changelings - if (!TryComp(actionId, out var actionData)) - return false; - if (!TryPrototype(actionId, out var actionProto, actionData)) - return false; - - if (HasAction(uid, actionProto.ID)) - { - Log.Debug($"Tried to insert action {ToPrettyString(actionId)} into {ToPrettyString(uid)}. Failed due to duplicate actions."); - return false; - } - if (!_container.Insert(actionId, comp.Container)) { Log.Error($"Failed to insert action {ToPrettyString(actionId)} into {ToPrettyString(uid)}"); @@ -263,21 +250,6 @@ public bool AddAction(EntityUid uid, EntityUid actionId, BaseActionComponent? ac return true; } - // goobstation - changelings - public bool HasAction(EntityUid uid, string pId, ActionsContainerComponent? actions = null) - { - if (!Resolve(uid, ref actions, false)) - return false; - - foreach (var act in actions.Container.ContainedEntities) - if (TryComp(act, out var metaData)) - if (TryPrototype(act, out var actProto, metaData)) - if (pId == actProto.ID) - return true; - - return false; - } - /// /// Removes an action from its container and any action-performer and moves the action to null-space /// diff --git a/Content.Shared/Actions/ActionEvents.cs b/Content.Shared/Actions/ActionEvents.cs index 1f1e07f3aa..72a566b8c8 100644 --- a/Content.Shared/Actions/ActionEvents.cs +++ b/Content.Shared/Actions/ActionEvents.cs @@ -154,9 +154,4 @@ public abstract partial class BaseActionEvent : HandledEntityEventArgs /// The user performing the action. /// public EntityUid Performer; - - /// - /// The action that was performed. - /// - public EntityUid Action; } diff --git a/Content.Shared/Actions/BaseActionComponent.cs b/Content.Shared/Actions/BaseActionComponent.cs index 57c145a0ec..6d9242acc1 100644 --- a/Content.Shared/Actions/BaseActionComponent.cs +++ b/Content.Shared/Actions/BaseActionComponent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.Audio; +using Content.Shared.Mobs; +using Robust.Shared.Audio; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -24,11 +25,6 @@ public abstract partial class BaseActionComponent : Component /// [DataField("iconOn")] public SpriteSpecifier? IconOn; - /// - /// For toggle actions only, background to show when toggled on. - /// - [DataField] public SpriteSpecifier? BackgroundOn; - /// /// If not null, this color will modulate the action icon color. /// diff --git a/Content.Shared/Actions/Events/ActionPerformedEvent.cs b/Content.Shared/Actions/Events/ActionPerformedEvent.cs deleted file mode 100644 index 530d7c9335..0000000000 --- a/Content.Shared/Actions/Events/ActionPerformedEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Content.Shared.Actions.Events; - -/// -/// Raised on the action entity when it is used and . -/// -/// The entity that performed this action. -[ByRefEvent] -public readonly record struct ActionPerformedEvent(EntityUid Performer); diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 538726c89d..9f3fb96410 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -145,6 +145,9 @@ public bool ResolveActionData( public void SetCooldown(EntityUid? actionId, TimeSpan start, TimeSpan end) { + if (actionId == null) + return; + if (!TryGetActionData(actionId, out var action)) return; @@ -160,6 +163,9 @@ public void SetCooldown(EntityUid? actionId, TimeSpan cooldown) public void ClearCooldown(EntityUid? actionId) { + if (actionId == null) + return; + if (!TryGetActionData(actionId, out var action)) return; @@ -170,27 +176,6 @@ public void ClearCooldown(EntityUid? actionId) Dirty(actionId.Value, action); } - /// - /// Sets the cooldown for this action only if it is bigger than the one it already has. - /// - public void SetIfBiggerCooldown(EntityUid? actionId, TimeSpan? cooldown) - { - if (cooldown == null || - cooldown.Value <= TimeSpan.Zero || - !TryGetActionData(actionId, out var action)) - { - return; - } - - var start = GameTiming.CurTime; - var end = start + cooldown; - if (action.Cooldown?.End > end) - return; - - action.Cooldown = (start, end.Value); - Dirty(actionId.Value, action); - } - public void StartUseDelay(EntityUid? actionId) { if (actionId == null) @@ -454,10 +439,7 @@ private void OnActionRequest(RequestPerformActionEvent ev, EntitySessionEventArg } if (performEvent != null) - { performEvent.Performer = user; - performEvent.Action = actionEnt; - } // All checks passed. Perform the action! PerformAction(user, component, actionEnt, action, performEvent, curTime); @@ -579,9 +561,6 @@ public void PerformAction(EntityUid performer, ActionsComponent? component, Enti if (dirty && component != null) Dirty(performer, component); - - var ev = new ActionPerformedEvent(performer); - RaiseLocalEvent(actionId, ref ev); } #endregion diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs index 30887d2070..113c96db04 100644 --- a/Content.Shared/Alert/AlertType.cs +++ b/Content.Shared/Alert/AlertType.cs @@ -56,8 +56,6 @@ public enum AlertType : byte BorgDead, Charge, Offer, - ChangelingChemicals, - ChangelingBiomass, } } diff --git a/Content.Shared/Antag/AntagAcceptability.cs b/Content.Shared/Antag/AntagAcceptability.cs index 02d0b5f58f..98abe713eb 100644 --- a/Content.Shared/Antag/AntagAcceptability.cs +++ b/Content.Shared/Antag/AntagAcceptability.cs @@ -20,8 +20,3 @@ public enum AntagAcceptability All } -public enum AntagSelectionTime : byte -{ - PrePlayerSpawn, - PostPlayerSpawn -} diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 3a5c8b2b3f..70e3f27d76 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -537,6 +537,91 @@ public static readonly CVarDef public static readonly CVarDef DiscordAuthApiKey = CVarDef.Create("discord.auth_api_key", "", CVar.SERVERONLY | CVar.CONFIDENTIAL); + + /* + * Suspicion + */ + + public static readonly CVarDef SuspicionMinPlayers = + CVarDef.Create("suspicion.min_players", 5); + + public static readonly CVarDef SuspicionMinTraitors = + CVarDef.Create("suspicion.min_traitors", 2); + + public static readonly CVarDef SuspicionPlayersPerTraitor = + CVarDef.Create("suspicion.players_per_traitor", 6); + + public static readonly CVarDef SuspicionStartingBalance = + CVarDef.Create("suspicion.starting_balance", 20); + + public static readonly CVarDef SuspicionMaxTimeSeconds = + CVarDef.Create("suspicion.max_time_seconds", 300); + + /* + * Traitor + */ + + public static readonly CVarDef TraitorMinPlayers = + CVarDef.Create("traitor.min_players", 5); + + public static readonly CVarDef TraitorMaxTraitors = + CVarDef.Create("traitor.max_traitors", 12); // Assuming average server maxes somewhere from like 50-80 people + + public static readonly CVarDef TraitorPlayersPerTraitor = + CVarDef.Create("traitor.players_per_traitor", 10); + + public static readonly CVarDef TraitorCodewordCount = + CVarDef.Create("traitor.codeword_count", 4); + + public static readonly CVarDef TraitorStartingBalance = + CVarDef.Create("traitor.starting_balance", 20); + + public static readonly CVarDef TraitorMaxDifficulty = + CVarDef.Create("traitor.max_difficulty", 5); + + public static readonly CVarDef TraitorMaxPicks = + CVarDef.Create("traitor.max_picks", 20); + + public static readonly CVarDef TraitorStartDelay = + CVarDef.Create("traitor.start_delay", 4f * 60f); + + public static readonly CVarDef TraitorStartDelayVariance = + CVarDef.Create("traitor.start_delay_variance", 3f * 60f); + + /* + * TraitorDeathMatch + */ + + public static readonly CVarDef TraitorDeathMatchStartingBalance = + CVarDef.Create("traitordm.starting_balance", 20); + + /* + * Zombie + */ + + public static readonly CVarDef ZombieMinPlayers = + CVarDef.Create("zombie.min_players", 20); + + /* + * Pirates + */ + + public static readonly CVarDef PiratesMinPlayers = + CVarDef.Create("pirates.min_players", 25); + + public static readonly CVarDef PiratesMaxOps = + CVarDef.Create("pirates.max_pirates", 6); + + public static readonly CVarDef PiratesPlayersPerOp = + CVarDef.Create("pirates.players_per_pirate", 5); + + /* + * Nukeops + */ + + public static readonly CVarDef NukeopsSpawnGhostRoles = + CVarDef.Create("nukeops.spawn_ghost_roles", false); + /* * Tips */ diff --git a/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs b/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs index 08ca204372..e7a0eef80e 100644 --- a/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs +++ b/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs @@ -34,7 +34,7 @@ private void OnMapInit(EntityUid uid, LoadoutComponent component, MapInitEvent a return; var proto = _prototype.Index(_random.Pick(component.Prototypes)); - _station.EquipStartingGear(uid, proto); + _station.EquipStartingGear(uid, proto, null); } diff --git a/Content.Shared/Content.Shared.csproj b/Content.Shared/Content.Shared.csproj index bd82b9fcd4..9752bfcfe2 100644 --- a/Content.Shared/Content.Shared.csproj +++ b/Content.Shared/Content.Shared.csproj @@ -26,9 +26,6 @@ - - - diff --git a/Content.Shared/Goobstation/Changeling/AbsorbableComponent.cs b/Content.Shared/Goobstation/Changeling/AbsorbableComponent.cs deleted file mode 100644 index 57fa59d1b1..0000000000 --- a/Content.Shared/Goobstation/Changeling/AbsorbableComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Changeling; - -/// -/// Component that indicates that a person can be absorbed by a changeling. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class AbsorbableComponent : Component -{ - -} diff --git a/Content.Shared/Goobstation/Changeling/AbsorbedComponent.cs b/Content.Shared/Goobstation/Changeling/AbsorbedComponent.cs deleted file mode 100644 index 36bed5222a..0000000000 --- a/Content.Shared/Goobstation/Changeling/AbsorbedComponent.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Changeling; - - -/// -/// Component that indicates that a person's DNA has been absorbed by a changeling. -/// -[RegisterComponent, NetworkedComponent, Access(typeof(AbsorbedSystem))] -public sealed partial class AbsorbedComponent : Component -{ - -} diff --git a/Content.Shared/Goobstation/Changeling/AbsorbedSystem.cs b/Content.Shared/Goobstation/Changeling/AbsorbedSystem.cs deleted file mode 100644 index 0496dc63be..0000000000 --- a/Content.Shared/Goobstation/Changeling/AbsorbedSystem.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Content.Shared.Examine; -using Content.Shared.Mobs; - -namespace Content.Shared.Changeling; - -public sealed partial class AbsorbedSystem : EntitySystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnExamine); - SubscribeLocalEvent(OnMobStateChange); - } - - private void OnExamine(Entity ent, ref ExaminedEvent args) - { - args.PushMarkup(Loc.GetString("changeling-absorb-onexamine")); - } - - private void OnMobStateChange(Entity ent, ref MobStateChangedEvent args) - { - // in case one somehow manages to dehusk someone - if (args.NewMobState != MobState.Dead) - RemComp(ent); - } -} diff --git a/Content.Shared/Goobstation/Changeling/Changeling.Actions.cs b/Content.Shared/Goobstation/Changeling/Changeling.Actions.cs deleted file mode 100644 index ef2406b405..0000000000 --- a/Content.Shared/Goobstation/Changeling/Changeling.Actions.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Content.Shared.Actions; -using Robust.Shared.GameStates; - -namespace Content.Shared.Changeling; - -[RegisterComponent, NetworkedComponent] -public sealed partial class ChangelingActionComponent : Component -{ - [DataField] public bool RequireBiomass = true; - - [DataField] public float ChemicalCost = 0; - - [DataField] public float BiomassCost = 0; - - [DataField] public bool UseInLesserForm = false; - - [DataField] public float RequireAbsorbed = 0; -} - -#region Events - Basic - -public sealed partial class OpenEvolutionMenuEvent : InstantActionEvent { } -public sealed partial class AbsorbDNAEvent : EntityTargetActionEvent { } -public sealed partial class StingExtractDNAEvent : EntityTargetActionEvent { } -public sealed partial class ChangelingTransformCycleEvent : InstantActionEvent { } -public sealed partial class ChangelingTransformEvent : InstantActionEvent { } -public sealed partial class EnterStasisEvent : InstantActionEvent { } -public sealed partial class ExitStasisEvent : InstantActionEvent { } - -#endregion - -#region Events - Combat - -public sealed partial class ToggleArmbladeEvent : InstantActionEvent { } -public sealed partial class CreateBoneShardEvent : InstantActionEvent { } -public sealed partial class ToggleChitinousArmorEvent : InstantActionEvent { } -public sealed partial class ToggleOrganicShieldEvent : InstantActionEvent { } -public sealed partial class ShriekDissonantEvent : InstantActionEvent { } -public sealed partial class ShriekResonantEvent : InstantActionEvent { } -public sealed partial class ToggleStrainedMusclesEvent : InstantActionEvent { } - -#endregion - -#region Events - Sting - -public sealed partial class StingBlindEvent : EntityTargetActionEvent { } -public sealed partial class StingCryoEvent : EntityTargetActionEvent { } -public sealed partial class StingLethargicEvent : EntityTargetActionEvent { } -public sealed partial class StingMuteEvent : EntityTargetActionEvent { } -public sealed partial class StingFakeArmbladeEvent : EntityTargetActionEvent { } -public sealed partial class StingTransformEvent : EntityTargetActionEvent { } - -#endregion - -#region Events - Utility - -public sealed partial class ActionAnatomicPanaceaEvent : InstantActionEvent { } -public sealed partial class ActionAugmentedEyesightEvent : InstantActionEvent { } -public sealed partial class ActionBiodegradeEvent : InstantActionEvent { } -public sealed partial class ActionChameleonSkinEvent : InstantActionEvent { } -public sealed partial class ActionEphedrineOverdoseEvent : InstantActionEvent { } -public sealed partial class ActionFleshmendEvent : InstantActionEvent { } -public sealed partial class ActionLastResortEvent : InstantActionEvent { } -public sealed partial class ActionLesserFormEvent : InstantActionEvent { } -public sealed partial class ActionSpacesuitEvent : InstantActionEvent { } -public sealed partial class ActionHivemindAccessEvent : InstantActionEvent { } -public sealed partial class ActionContortBodyEvent : InstantActionEvent { } - -#endregion diff --git a/Content.Shared/Goobstation/Changeling/Changeling.DoAfter.cs b/Content.Shared/Goobstation/Changeling/Changeling.DoAfter.cs deleted file mode 100644 index 4138052954..0000000000 --- a/Content.Shared/Goobstation/Changeling/Changeling.DoAfter.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Content.Shared.DoAfter; -using Robust.Shared.Serialization; - -namespace Content.Shared.Changeling; - -[Serializable, NetSerializable] -public sealed partial class AbsorbDNADoAfterEvent : SimpleDoAfterEvent { } diff --git a/Content.Shared/Goobstation/Changeling/ChangelingComponent.cs b/Content.Shared/Goobstation/Changeling/ChangelingComponent.cs deleted file mode 100644 index 5eaa37b5e7..0000000000 --- a/Content.Shared/Goobstation/Changeling/ChangelingComponent.cs +++ /dev/null @@ -1,161 +0,0 @@ -using Content.Shared.Humanoid; -using Content.Shared.StatusIcon; -using Robust.Shared.Audio; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Changeling; - -[RegisterComponent, NetworkedComponent] -[AutoGenerateComponentState] -public sealed partial class ChangelingComponent : Component -{ - #region Prototypes - - [DataField("soundMeatPool")] - public List SoundPool = new() - { - new SoundPathSpecifier("/Audio/Effects/gib1.ogg"), - new SoundPathSpecifier("/Audio/Effects/gib2.ogg"), - new SoundPathSpecifier("/Audio/Effects/gib3.ogg"), - }; - - [DataField("soundShriek")] - public SoundSpecifier ShriekSound = new SoundPathSpecifier("/Audio/Goobstation/Changeling/Effects/changeling_shriek.ogg"); - - [DataField("shriekPower")] - public float ShriekPower = 2.5f; - - public readonly List> BaseChangelingActions = new() - { - "ActionEvolutionMenu", - "ActionAbsorbDNA", - "ActionStingExtractDNA", - "ActionChangelingTransformCycle", - "ActionChangelingTransform", - "ActionEnterStasis", - "ActionExitStasis" - }; - - /// - /// The status icon corresponding to the Changlings. - /// - - [DataField, ViewVariables(VVAccess.ReadOnly)] - public ProtoId StatusIcon { get; set; } = "HivemindFaction"; - - #endregion - - public bool IsInStasis = false; - - public bool StrainedMusclesActive = false; - - public bool IsInLesserForm = false; - - - public Dictionary Equipment = new(); - - /// - /// Amount of biomass changeling currently has. - /// - [DataField, AutoNetworkedField] - public float Biomass = 30f; - - /// - /// Maximum amount of biomass a changeling can have. - /// - [DataField, AutoNetworkedField] - public float MaxBiomass = 30f; - - /// - /// How much biomass should be removed per cycle. - /// - [DataField, AutoNetworkedField] - public float BiomassDrain = 1f; - - /// - /// Current amount of chemicals changeling currently has. - /// - [DataField, AutoNetworkedField] - public float Chemicals = 100f; - - /// - /// Maximum amount of chemicals changeling can have. - /// - [DataField, AutoNetworkedField] - public float MaxChemicals = 100f; - - /// - /// Bonus chemicals regeneration. In case - /// - [DataField, AutoNetworkedField] - public float BonusChemicalRegen = 0f; - - /// - /// Cooldown between chem regen events. - /// - public TimeSpan UpdateTimer = TimeSpan.Zero; - public float UpdateCooldown = 1f; - - public float BiomassUpdateTimer = 0f; - public float BiomassUpdateCooldown = 60f; - - [ViewVariables(VVAccess.ReadOnly)] - public List AbsorbedDNA = new(); - /// - /// Index of . Used for switching forms. - /// - [ViewVariables(VVAccess.ReadOnly)] - public int AbsorbedDNAIndex = 0; - - /// - /// Maximum amount of DNA a changeling can absorb. - /// - public int MaxAbsorbedDNA = 5; - - /// - /// Total absorbed DNA. Counts towards objectives. - /// - [ViewVariables(VVAccess.ReadWrite)] - public int TotalAbsorbedEntities = 0; - - /// - /// Total stolen DNA. Counts towards objectives. - /// - [ViewVariables(VVAccess.ReadWrite)] - public int TotalStolenDNA = 0; - - [ViewVariables(VVAccess.ReadOnly)] - public TransformData? CurrentForm; - - [ViewVariables(VVAccess.ReadOnly)] - public TransformData? SelectedForm; -} - -[DataDefinition] -public sealed partial class TransformData -{ - /// - /// Entity's name. - /// - [DataField] - public string Name; - - /// - /// Entity's fingerprint, if it exists. - /// - [DataField] - public string? Fingerprint; - - /// - /// Entity's DNA. - /// - [DataField("dna")] - public string DNA; - - /// - /// Entity's humanoid appearance component. - /// - [ViewVariables(VVAccess.ReadOnly), NonSerialized] - public HumanoidAppearanceComponent Appearance; -} diff --git a/Content.Shared/Goobstation/Changeling/HivemindComponent.cs b/Content.Shared/Goobstation/Changeling/HivemindComponent.cs deleted file mode 100644 index bc4e821726..0000000000 --- a/Content.Shared/Goobstation/Changeling/HivemindComponent.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Changeling; - -/// -/// Used for identifying other changelings. -/// Indicates that a changeling has bought the hivemind access ability. -/// -[RegisterComponent, NetworkedComponent] -public sealed partial class HivemindComponent : Component -{ -} diff --git a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs index ce49f80af3..ece4b59e91 100644 --- a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs +++ b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs @@ -328,11 +328,8 @@ public void SetScale(EntityUid uid, Vector2 scale, bool sync = true, HumanoidApp /// The mob's entity UID. /// The character profile to load. /// Humanoid component of the entity - public virtual void LoadProfile(EntityUid uid, HumanoidCharacterProfile? profile, HumanoidAppearanceComponent? humanoid = null) + public virtual void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidAppearanceComponent? humanoid = null) { - if (profile == null) - return; - if (!Resolve(uid, ref humanoid)) { return; diff --git a/Content.Shared/Inventory/InventorySystem.Helpers.cs b/Content.Shared/Inventory/InventorySystem.Helpers.cs index 7e325abe21..811387d375 100644 --- a/Content.Shared/Inventory/InventorySystem.Helpers.cs +++ b/Content.Shared/Inventory/InventorySystem.Helpers.cs @@ -1,6 +1,8 @@ using System.Diagnostics.CodeAnalysis; +using System.Linq; using Content.Shared.Hands.Components; using Content.Shared.Storage.EntitySystems; +using Robust.Shared.Containers; using Robust.Shared.Prototypes; namespace Content.Shared.Inventory; @@ -94,7 +96,7 @@ bool DeleteItem() /// /// The entity that you want to spawn an item on /// A list of prototype IDs that you want to spawn in the bag. - public void SpawnItemsOnEntity(EntityUid entity, List items) + public void SpawnItemsOnEntity(EntityUid entity, List items) { foreach (var item in items) { diff --git a/Content.Shared/NukeOps/NukeOperativeComponent.cs b/Content.Shared/NukeOps/NukeOperativeComponent.cs index d19f0ae3e9..cdbefece9d 100644 --- a/Content.Shared/NukeOps/NukeOperativeComponent.cs +++ b/Content.Shared/NukeOps/NukeOperativeComponent.cs @@ -13,9 +13,14 @@ namespace Content.Shared.NukeOps; [RegisterComponent, NetworkedComponent] public sealed partial class NukeOperativeComponent : Component { + /// + /// Path to antagonist alert sound. + /// + [DataField("greetSoundNotification")] + public SoundSpecifier GreetSoundNotification = new SoundPathSpecifier("/Audio/Ambience/Antag/nukeops_start.ogg"); /// - /// + /// /// [DataField("syndStatusIcon", customTypeSerializer: typeof(PrototypeIdSerializer))] public string SyndStatusIcon = "SyndicateFaction"; diff --git a/Content.Shared/Roles/SharedRoleSystem.cs b/Content.Shared/Roles/SharedRoleSystem.cs index 94ad32164b..e8053e4c67 100644 --- a/Content.Shared/Roles/SharedRoleSystem.cs +++ b/Content.Shared/Roles/SharedRoleSystem.cs @@ -1,4 +1,3 @@ -using System.Linq; using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Mind; @@ -63,64 +62,6 @@ protected void SubscribeAntagEvents() where T : AntagonistRoleComponent _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)}"); - } - /// /// Gives this mind a new role. /// @@ -236,11 +177,6 @@ public bool IsAntagonistRole() return _antagTypes.Contains(typeof(T)); } - public bool IsAntagonistRole(Type component) - { - return _antagTypes.Contains(component); - } - /// /// Play a sound for the mind, if it has a session attached. /// Use this for role greeting sounds. diff --git a/Content.Shared/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index 59c875f30e..715ee2a149 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -1,17 +1,16 @@ using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; +using Content.Shared.Preferences; using Content.Shared.Roles; using Content.Shared.Storage; using Content.Shared.Storage.EntitySystems; using Robust.Shared.Collections; -using Robust.Shared.Prototypes; namespace Content.Shared.Station; public abstract class SharedStationSpawningSystem : EntitySystem { - [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] protected readonly InventorySystem InventorySystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedStorageSystem _storage = default!; @@ -22,27 +21,14 @@ public abstract class SharedStationSpawningSystem : EntitySystem /// /// Entity to load out. /// Starting gear to use. - public void EquipStartingGear(EntityUid entity, ProtoId? startingGear) + /// Character profile to use, if any. + public void EquipStartingGear(EntityUid entity, StartingGearPrototype startingGear, HumanoidCharacterProfile? profile) { - PrototypeManager.TryIndex(startingGear, out var gearProto); - EquipStartingGear(entity, gearProto); - } - - /// - /// Equips starting gear onto the given entity. - /// - /// Entity to load out. - /// Starting gear to use. - public void EquipStartingGear(EntityUid entity, StartingGearPrototype? startingGear) - { - if (startingGear == null) - return; - if (InventorySystem.TryGetSlots(entity, out var slotDefinitions)) { foreach (var slot in slotDefinitions) { - var equipmentStr = startingGear.GetGear(slot.Name, null); + var equipmentStr = startingGear.GetGear(slot.Name, profile); if (string.IsNullOrEmpty(equipmentStr)) continue; diff --git a/Resources/Audio/Goobstation/Ambience/Antag/changeling_start.ogg b/Resources/Audio/Goobstation/Ambience/Antag/changeling_start.ogg deleted file mode 100644 index 1132ccca29..0000000000 Binary files a/Resources/Audio/Goobstation/Ambience/Antag/changeling_start.ogg and /dev/null differ diff --git a/Resources/Audio/Goobstation/Changeling/Effects/changeling_shriek.ogg b/Resources/Audio/Goobstation/Changeling/Effects/changeling_shriek.ogg deleted file mode 100644 index e6daf79555..0000000000 Binary files a/Resources/Audio/Goobstation/Changeling/Effects/changeling_shriek.ogg and /dev/null differ diff --git a/Resources/Locale/en-US/Goobstation/Changeling/alerts/alerts.ftl b/Resources/Locale/en-US/Goobstation/Changeling/alerts/alerts.ftl deleted file mode 100644 index 9429bc7f53..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/alerts/alerts.ftl +++ /dev/null @@ -1,6 +0,0 @@ -alerts-changeling-chemicals-name = Chemicals -alerts-changeling-chemicals-desc = Spend chemicals to use your abilities. Slowly regenerates. - -alerts-changeling-biomass-name = Biomass -alerts-changeling-biomass-desc = - This is your health. If it reaches 0 - it's [color=red]game over[/color]. Absorb humanoids to recover some of it. \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/Changeling/guidebook/guides.ftl b/Resources/Locale/en-US/Goobstation/Changeling/guidebook/guides.ftl deleted file mode 100644 index 75a120878b..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/guidebook/guides.ftl +++ /dev/null @@ -1 +0,0 @@ -guide-entry-changelings = Changelings \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/Changeling/objectives/changeling.ftl b/Resources/Locale/en-US/Goobstation/Changeling/objectives/changeling.ftl deleted file mode 100644 index 47c7a326ae..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/objectives/changeling.ftl +++ /dev/null @@ -1,7 +0,0 @@ -objective-condition-absorb-title = Absorb {$count} humanoids. -objective-condition-absorb-description = I must absorb {$count} humanoids. It is necessary for my survival and further evolution. - -objective-condition-stealdna-title = Extract {$count} compatible genomes. -objective-condition-stealdna-description = I must extract {$count} unique genomes. - -objective-condition-escape-identity-title = Escape on the evacuation shuttle alive and unrestrained while being {$targetName}, {CAPITALIZE($job)}. \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/Changeling/popup/changeling.ftl b/Resources/Locale/en-US/Goobstation/Changeling/popup/changeling.ftl deleted file mode 100644 index 9ad650ccec..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/popup/changeling.ftl +++ /dev/null @@ -1,3 +0,0 @@ -popup-changeling-biomass-deficit-low = Your skin itches. -popup-changeling-biomass-deficit-medium = Must find a food source... -popup-changeling-biomass-deficit-high = Must eat... NOW!! \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/Changeling/radio_channels.ftl b/Resources/Locale/en-US/Goobstation/Changeling/radio_channels.ftl deleted file mode 100644 index a2eeebd9cb..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/radio_channels.ftl +++ /dev/null @@ -1 +0,0 @@ -chat-radio-hivemind = Hivemind \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/Changeling/store/categories.ftl b/Resources/Locale/en-US/Goobstation/Changeling/store/categories.ftl deleted file mode 100644 index c0b4796701..0000000000 --- a/Resources/Locale/en-US/Goobstation/Changeling/store/categories.ftl +++ /dev/null @@ -1,4 +0,0 @@ -# Changeling -store-ling-category-combat = Combat -store-ling-category-sting = Stings -store-ling-category-utility = Utility diff --git a/Resources/Locale/en-US/Goobstation/changeling/abilities/changeling.ftl b/Resources/Locale/en-US/Goobstation/changeling/abilities/changeling.ftl deleted file mode 100644 index 01343e20b5..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/abilities/changeling.ftl +++ /dev/null @@ -1,64 +0,0 @@ -# Abilities -changeling-biomass-deficit = Not enough biomass! -changeling-chemicals-deficit = Not enough chemicals! -changeling-action-fail-lesserform = Can't use it while in lesser form! -changeling-action-fail-absorbed = Need to absorb {$number} more organics to use it! - -changeling-absorb-start = {CAPITALIZE(THE($user))} starts absorbing {CAPITALIZE(THE($target))}'s! -changeling-absorb-fail-incapacitated = You can't absorb it until it's not incapacitated. -changeling-absorb-fail-absorbed = You've already absorbed it. -changeling-absorb-fail-unabsorbable = The target is not absorbable. -changeling-absorb-end-self = Another organic absorbed. You are evolving. -changeling-absorb-end-self-ling = Another changeling absorbed. You are evolving more rapidly. -changeling-absorb-onexamine = [color=red]The body feels hollow.[/color] - -changeling-transform-cycle = Switched to {$target}'s DNA. -changeling-transform-cycle-empty = You don't have any DNA strains! -changeling-transform-others = {CAPITALIZE(THE($user))}'s body twists and takes shape of another being! -changeling-transform-fail-self = You can't transform into your current form! -changeling-transform-fail-choose = You did not choose a form to transform into! -changeling-transform-fail-absorbed = You can't transform a husk! -changeling-transform-finish = You are now {$target}. - -changeling-sting-fail-self = You tried to sting {CAPITALIZE(THE($target))}, but something stopped you from doing it! -changeling-sting-fail-ling = Someone just tried to silently sting you! - -changeling-sting = You silently sting {CAPITALIZE(THE($target))} -changeling-sting-fail-simplemob = You can't sting a lesser creature! -changeling-sting-extract-fail = Unable to extract DNA -changeling-sting-extract-max = Need to get rid of the stored DNA beforehand - -changeling-stasis-enter = You enter regenerative stasis -changeling-stasis-enter-fail = Can't enter stasis! -changeling-stasis-exit = You exit regenerative stasis -changeling-stasis-exit-fail = We're not in a stasis! -changeling-stasis-exit-fail-dead = Can't exit stasis! - -changeling-hand-transform-end = Your arm takes back it's initial form -changeling-fail-hands = Need to drop something beforehand - -changeling-armblade-start = Your arm reforms into a grotesque blade -changeling-shield-start = Your arm reforms into a meat shield - -changeling-muscles-start = Your body feels a lot lighter -changeling-muscles-end = Your legs feel heavier - -changeling-equip-armor-fail = Need to get rid of existing outer clothing beforehand -changeling-equip-armor-start = Your body gets wrapped in a sturdy chitinous shell -changeling-equip-spacesuit-start = Your body transforms into a spaceproof abomination -changeling-equip-end = Your body takes back it's original shape - -changeling-inject = You inject yourself -changeling-inject-fail = Failed to inject yourself! - -changeling-passive-activate = Activated ability -changeling-passive-activate-fail = Failed to activate the ability -changeling-passive-active = Already active! - -changeling-fleshmend = Your body twists, sealing wounds and regenerating dead cells -changeling-panacea = You mutate and alter your DNA for better cell regeneration - -changeling-chameleon-start = You adapt your skin to the environment -changeling-chameleon-end = Your skin is losing it's translucency - -changeling-hivemind-start = We tune our brainwaves to match the hivemind frequency diff --git a/Resources/Locale/en-US/Goobstation/changeling/administration/antag.ftl b/Resources/Locale/en-US/Goobstation/changeling/administration/antag.ftl deleted file mode 100644 index 3ba13f47e5..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/administration/antag.ftl +++ /dev/null @@ -1,3 +0,0 @@ -admin-verb-make-changeling = Make the target into a changeling. - -admin-verb-text-make-changeling = Make Changeling \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/changeling/game-ticking/game-presets/preset-changeling.ftl b/Resources/Locale/en-US/Goobstation/changeling/game-ticking/game-presets/preset-changeling.ftl deleted file mode 100644 index 1e550cb4f1..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/game-ticking/game-presets/preset-changeling.ftl +++ /dev/null @@ -1,20 +0,0 @@ -changeling-roundend-name = changeling - -objective-issuer-hivemind = [color=orange]Hivemind[/color] - -roundend-prepend-changeling-absorbed-named = [color=white]{$name}[/color] has absorbed a total of [color=red]{$number}[/color] organics. -roundend-prepend-changeling-stolen-named = [color=white]{$name}[/color] has extracted a total of [color=orange]{$number}[/color] DNA samples. -roundend-prepend-changeling-absorbed = Someone has absorbed a total of [color=red]{$number}[/color] organics. -roundend-prepend-changeling-stolen = Someone had extracted a total of [color=orange]{$number}[/color] DNA samples. - -changeling-gamemode-title = Changelings -changeling-gamemode-description = - The changeling hive has boarded the station, ready to take anything it desires - be it your equipment, your faces, or your lives! - -changeling-role-greeting = - You are a changeling who has absorbed and taken the form of {$name}! - Your objectives are listed in the character menu. - Absorb, shapeshift and evolve to complete them! - -changeling-role-greeting-short = - You are a changeling who has absorbed and taken the initial form of {$name}. diff --git a/Resources/Locale/en-US/Goobstation/changeling/prototypes/roles/antags.ftl b/Resources/Locale/en-US/Goobstation/changeling/prototypes/roles/antags.ftl deleted file mode 100644 index 5d5e578d60..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/prototypes/roles/antags.ftl +++ /dev/null @@ -1,2 +0,0 @@ -roles-antag-changeling-name = Changeling -roles-antag-changeling-description = Use your shapeshifting abilities to complete your objectives. \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/changeling/store/changeling-catalog.ftl b/Resources/Locale/en-US/Goobstation/changeling/store/changeling-catalog.ftl deleted file mode 100644 index f1897c18de..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/store/changeling-catalog.ftl +++ /dev/null @@ -1,134 +0,0 @@ - -# combat - -evolutionmenu-combat-armblade-name = Arm Blade -evolutionmenu-combat-armblade-desc = - Reform one of your arms into a grotesque blade, composed of bone and flesh, able to pry open airlocks and cut through your foes like butter. - Costs 15 chemicals. - -evolutionmenu-combat-boneshard-name = Bone Shard -evolutionmenu-combat-boneshard-desc = - Break off shards of your bone and shape them into a throwing star which embeds into your foes. But a one timer opportinuty. - Costs 15 chemicals. - -evolutionmenu-combat-armor-name = Chitinous Armor -evolutionmenu-combat-armor-desc = - Inflate your body into an all-consuming chitinous mass of armor. - Provides extensive protection against physical damage, but less against other types. - It massively slows your movement, and maintaining its shape slows chemical generation. - WARNING: Requires you to absorb at least 2 organics to use the ability. - Costs 25 chemicals. - -evolutionmenu-combat-shield-name = Organic Shield -evolutionmenu-combat-shield-desc = - Reforms one of your arms into a large, fleshy shield. - Blocks attacks automatically, but very brittle. - WARNING: Requires you to absorb at least 1 organic to use the ability. - Costs 20 chemicals. - -evolutionmenu-combat-shriek-dissonant-name = Dissonant Shriek -evolutionmenu-combat-shriek-dissonant-desc = - You emit an EMP blast, which disables technology in the surrounding area, including radio headsets. - Good for escaping cyborgs and security. - WARNING: Requires you to absorb at least 1 organic to use the ability. - Costs 30 chemicals. - -evolutionmenu-combat-shriek-resonant-name = Resonant Shriek -evolutionmenu-combat-shriek-resonant-desc = - You emit a tone beyond the range of human hearing, - bursting lights and causing disorientation in an area around yourself. - Good for escaping groups, or hindering people from fleeing. - WARNING: Requires you to absorb at least 1 organic to use the ability. - Costs 30 chemicals. - -evolutionmenu-combat-strainedmuscles-name = Strained Muscles -evolutionmenu-combat-strainedmuscles-desc = - You reduce lactic acid buildup in your leg muscles, allowing you to move at extremely fast speeds. - While active, you will take steadily increments of stamina damage and eventually pass out. - Cost-free. - -# sting - -evolutionmenu-sting-blind-name = Blind Sting -evolutionmenu-sting-blind-desc = - Silently sting an organic target, completely blinding them for a short time, and rendering them near-sighted until oculine is applied. - May be used while under the effects of Lesser Form. - Costs 35 chemicals. - -evolutionmenu-sting-cryo-name = Cryogenic Sting -evolutionmenu-sting-cryo-desc = - Inject an organic target with a cocktail of chemicals that chills the blood. - May be used while under the effects of Lesser Form. - Costs 35 chemicals. - -evolutionmenu-sting-lethargic-name = Lethargic Sting -evolutionmenu-sting-lethargic-desc = - Inject an organic target with a cocktail of anesthetics, slowing the victim down for a decent amount of time. - May be used while under the effects of Lesser Form. - Costs 35 chemicals. - -evolutionmenu-sting-mute-name = Mute Sting -evolutionmenu-sting-mute-desc = - Inject mute toxin into an organic target, completely silencing them for a while. - May be used while under the effects of Lesser Form. - Costs 35 chemicals. - -evolutionmenu-sting-transform-name = Transformation Sting -evolutionmenu-sting-transform-desc = - Inject some of your genome into an organic target, forcing their body to shapeshift into whoever you've chosen using the Cycle DNA ability. - May be used while under the effects of Lesser Form. - Costs 75 chemicals. - -evolutionmenu-sting-armblade-name = Fake Arm Blade Sting -evolutionmenu-sting-armblade-desc = - Inject some of your genome into an organic target, forcing their arm to shapeshift into a dull armblade. - May be used while under the effects of Lesser Form. - Costs 50 chemicals. - -# utility -evolutionmenu-utility-panacea-name = Anatomic Panacea -evolutionmenu-utility-panacea-desc = - Cure yourself of diseases, disabilities, radiation, toxins, drunkenness, and brain damage. Generally covers the things that fleshmend doesn't. - Costs 30 chemicals. - -evolutionmenu-utility-eyesight-name = Augmented Eyesight -evolutionmenu-utility-eyesight-desc = - Evolve additional features in your eyes, such as flash protection. - Cost-free. - -evolutionmenu-utility-biodegrade-name = Biodegrade -evolutionmenu-utility-biodegrade-desc = - Vomit a caustic substance onto any restraints you may be wearing, allowing yourself to break free. - Using this ability while being grabbed will spit acid in your attackers face. - Costs 30 chemicals. - -evolutionmenu-utility-chameleon-name = Chameleon Skin -evolutionmenu-utility-chameleon-desc = - Alter the pigment in your skin to match your surroundings, rendering you invisible.p - Costs 20 chemicals. - -evolutionmenu-utility-stims-name = Ephedrine Overdose -evolutionmenu-utility-stims-desc = - Inject a cocktail of stimulants into yourself, quickly removing any stuns and giving yourself a speed boost. - Continuous injection is poisonous. - Costs 30 chemicals. - -evolutionmenu-utility-fleshmend-name = Fleshmend -evolutionmenu-utility-fleshmend-desc = - Rapidly heal yourself of all bruises and burns. - Costs 35 chemicals. - -evolutionmenu-utility-lesserform-name = Lesser Form -evolutionmenu-utility-lesserform-desc = - Abandon your current form and turn into a sentient monkey. - Costs 20 chemicals. - -evolutionmenu-utility-spacesuit-name = Space Adaptation -evolutionmenu-utility-spacesuit-desc = - Get rid of useless tissue in order to facilitate space travel. A source of oxygen is still required for space walking. - Costs 20 chemicals. - -evolutionmenu-utility-hivemindaccess-name = Hivemind Access -evolutionmenu-utility-hivemindaccess-desc = - Tunes our chemical receptors for hivemind communication, allowing us to recognize and communicate with other changelings who have also evolved this ability. - Default radio key is :g diff --git a/Resources/Locale/en-US/Goobstation/changeling/store/currency.ftl b/Resources/Locale/en-US/Goobstation/changeling/store/currency.ftl deleted file mode 100644 index 0398a8cc4e..0000000000 --- a/Resources/Locale/en-US/Goobstation/changeling/store/currency.ftl +++ /dev/null @@ -1 +0,0 @@ -store-currency-display-evolutionpoints = Evolution Points \ No newline at end of file diff --git a/Resources/Locale/en-US/Goobstation/game-ticking/game-presets/presets-dualantag.ftl b/Resources/Locale/en-US/Goobstation/game-ticking/game-presets/presets-dualantag.ftl deleted file mode 100644 index dcb51e9198..0000000000 --- a/Resources/Locale/en-US/Goobstation/game-ticking/game-presets/presets-dualantag.ftl +++ /dev/null @@ -1,8 +0,0 @@ -traitorling-title = Traitorling -traitorling-description = Attention. Known enemy signals and strange biosigns detected. Confirmed Syndicate Agents and Changelings on board. - -revtraitor-title = Revolutionary Traitors -revtraitor-description = A revolution has been provoked by a member of the Syndicate, but not every agent got the hint... - -revling-title = Revolutionary Changelings -revling-description = A revolution has been provoked by the Syndicate, and opportunistic changelings have come to feast on the carnage. diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-pirates.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-pirates.ftl new file mode 100644 index 0000000000..941643dd9a --- /dev/null +++ b/Resources/Locale/en-US/game-ticking/game-presets/preset-pirates.ftl @@ -0,0 +1,10 @@ +pirates-title = Privateers +pirates-description = A group of privateers has approached your lowly station. Hostile or not, their sole goal is to end the round with as many knicknacks on their ship as they can get. + +pirates-no-ship = Through unknown circumstances, the privateer's ship was completely and utterly destroyed. No score. +pirates-final-score = The privateers successfully obtained {$score} spesos worth +pirates-final-score-2 = of knicknacks, with a total of {$finalPrice} spesos. +pirates-list-start = The privateers were: +pirates-most-valuable = The most valuable stolen items were: +pirates-stolen-item-entry = {$entity} ({$credits} spesos) +pirates-stole-nothing = - The pirates stole absolutely nothing at all. Point and laugh. diff --git a/Resources/Maps/Shuttles/striker.yml b/Resources/Maps/Shuttles/striker.yml index 88b113d7fd..35b6178bd4 100644 --- a/Resources/Maps/Shuttles/striker.yml +++ b/Resources/Maps/Shuttles/striker.yml @@ -1,2389 +1,2389 @@ -meta: - format: 6 - postmapinit: false -tilemap: - 0: Space - 29: FloorDark - 84: FloorShuttleRed - 104: FloorTechMaint - 105: FloorTechMaint2 - 118: FloorWood - 120: Lattice - 121: Plating -entities: -- proto: "" - entities: - - uid: 325 - components: - - type: MetaData - - type: Transform - pos: 0.5638949,0.47865233 - parent: invalid - - type: MapGrid - chunks: - -1,-1: - ind: -1,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAdgAAAAAAdgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAdgAAAAADdgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAADHQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaQAAAAAAaQAAAAAAHQAAAAABHQAAAAABHQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAHQAAAAABHQAAAAACHQAAAAAB - version: 6 - 0,-1: - ind: 0,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAACeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAABeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABHQAAAAABHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADHQAAAAACeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 - -1,0: - ind: -1,0 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAVAAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 - 0,0: - ind: 0,0 - tiles: VAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - version: 6 - - type: Broadphase - - type: Physics - bodyStatus: InAir - angularDamping: 0.05 - linearDamping: 0.05 - fixedRotation: False - bodyType: Dynamic - - type: Fixtures - fixtures: {} - - type: OccluderTree - - type: Shuttle - - type: Gravity - gravityShakeSound: !type:SoundPathSpecifier - path: /Audio/Effects/alert.ogg - - type: DecalGrid - chunkCollection: - version: 2 - nodes: - - node: - color: '#FFFFFFFF' - id: BrickTileDarkCornerNe - decals: - 11: 1,-1 - - node: - color: '#FFFFFFFF' - id: BrickTileDarkCornerNw - decals: - 5: -3,-1 - - node: - color: '#FFFFFFFF' - id: BrickTileDarkCornerSe - decals: - 4: 1,-3 - - node: - color: '#FFFFFFFF' - id: BrickTileDarkCornerSw - decals: - 3: -3,-3 - - node: - color: '#FFFFFFFF' - id: BrickTileDarkLineS - decals: - 0: -1,-3 - 1: -2,-3 - 2: 0,-3 - - node: - color: '#7F1C1FFF' - id: BrickTileWhiteCornerNe - decals: - 13: 1,-1 - - node: - color: '#7F1C1FFF' - id: BrickTileWhiteCornerNw - decals: - 12: -3,-1 - - node: - color: '#7F1C1FFF' - id: BrickTileWhiteCornerSe - decals: - 9: 1,-3 - - node: - color: '#7F1C1FFF' - id: BrickTileWhiteCornerSw - decals: - 10: -3,-3 - - node: - color: '#7F1C1FFF' - id: BrickTileWhiteLineS - decals: - 6: -2,-3 - 7: -1,-3 - 8: 0,-3 - - node: - color: '#FFFFFFFF' - id: Delivery - decals: - 23: 2,-2 - 24: -4,-2 - - node: - color: '#FFFFFFFF' - id: WarnLineE - decals: - 14: 1,-2 - - node: - color: '#FFFFFFFF' - id: WarnLineS - decals: - 16: -3,-2 - - node: - color: '#FFFFFFFF' - id: WarnLineW - decals: - 15: -1,-1 - - node: - color: '#FFFFFFFF' - id: WoodTrimThinLineN - decals: - 17: -1,-5 - 18: 0,-5 - 19: -2,-5 - - node: - color: '#FFFFFFFF' - id: WoodTrimThinLineS - decals: - 20: -2,-6 - 21: -1,-6 - 22: 0,-6 - - type: GridAtmosphere - version: 2 - data: - tiles: - -1,-1: - 0: 65535 - 0,-1: - 0: 65535 - -2,-1: - 0: 52424 - -1,-3: - 0: 65280 - -1,-2: - 0: 65535 - 0,-3: - 0: 30464 - 0,-2: - 0: 30583 - -2,0: - 0: 8 - -1,0: - 0: 3839 - 0,0: - 0: 895 - uniqueMixes: - - volume: 2500 - temperature: 293.15 - moles: - - 21.824879 - - 82.10312 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - chunkSize: 4 - - type: GasTileOverlay - - type: RadiationGridResistance - - type: GravityShake - shakeTimes: 10 - - type: SpreaderGrid - - type: GridPathfinding -- proto: AirCanister - entities: - - uid: 91 - components: - - type: Transform - pos: -0.5,-8.5 - parent: 325 - - type: AtmosDevice - joinedGrid: 325 -- proto: AirlockExternalShuttleSyndicateLocked - entities: - - uid: 142 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -4.5,-1.5 - parent: 325 -- proto: AirlockSyndicateLocked - entities: - - uid: 20 - components: - - type: Transform - pos: -0.5,-3.5 - parent: 325 - - uid: 88 - components: - - type: Transform - pos: -0.5,-6.5 - parent: 325 -- proto: APCBasic - entities: - - uid: 107 - components: - - type: Transform - pos: 0.5,-6.5 - parent: 325 - - type: PowerNetworkBattery - loadingNetworkDemand: 15107 - currentReceiving: 15106.935 - currentSupply: 15107 - supplyRampPosition: 0.064453125 -- proto: AtmosDeviceFanTiny - entities: - - uid: 6 - components: - - type: Transform - pos: -3.5,-1.5 - parent: 325 -- proto: Bed - entities: - - uid: 76 - components: - - type: Transform - pos: 0.5,-5.5 - parent: 325 -- proto: BedsheetSyndie - entities: - - uid: 164 - components: - - type: Transform - pos: 0.5,-5.5 - parent: 325 -- proto: BlastDoorOpen - entities: - - uid: 190 - components: - - type: Transform - pos: 1.5,-5.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 331 - - type: DeviceLinkSink - links: - - 205 - - uid: 191 - components: - - type: Transform - pos: 1.5,-4.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 332 - - type: DeviceLinkSink - links: - - 205 - - uid: 192 - components: - - type: Transform - pos: -2.5,-5.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 333 - - type: DeviceLinkSink - links: - - 205 - - uid: 193 - components: - - type: Transform - pos: -2.5,-4.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 334 - - type: DeviceLinkSink - links: - - 205 - - uid: 196 - components: - - type: Transform - pos: 3.5,-1.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 337 - - type: DeviceLinkSink - links: - - 205 - - uid: 198 - components: - - type: Transform - pos: -1.5,1.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 339 - - type: DeviceLinkSink - links: - - 205 - - uid: 199 - components: - - type: Transform - pos: -1.5,2.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 340 - - type: DeviceLinkSink - links: - - 205 - - uid: 200 - components: - - type: Transform - pos: -0.5,2.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 341 - - type: DeviceLinkSink - links: - - 205 - - uid: 201 - components: - - type: Transform - pos: 0.5,2.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 342 - - type: DeviceLinkSink - links: - - 205 - - uid: 202 - components: - - type: Transform - pos: 0.5,1.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 343 - - type: DeviceLinkSink - links: - - 205 -- proto: BoxMRE - entities: - - uid: 320 - components: - - type: Transform - pos: 0.70504504,-7.29326 - parent: 325 -- proto: CableApcExtension - entities: - - uid: 120 - components: - - type: Transform - pos: 0.5,-6.5 - parent: 325 - - uid: 121 - components: - - type: Transform - pos: -0.5,-6.5 - parent: 325 - - uid: 122 - components: - - type: Transform - pos: -0.5,-7.5 - parent: 325 - - uid: 123 - components: - - type: Transform - pos: -0.5,-8.5 - parent: 325 - - uid: 124 - components: - - type: Transform - pos: -1.5,-8.5 - parent: 325 - - uid: 125 - components: - - type: Transform - pos: 0.5,-8.5 - parent: 325 - - uid: 126 - components: - - type: Transform - pos: 1.5,-8.5 - parent: 325 - - uid: 127 - components: - - type: Transform - pos: -2.5,-8.5 - parent: 325 - - uid: 128 - components: - - type: Transform - pos: -3.5,-8.5 - parent: 325 - - uid: 129 - components: - - type: Transform - pos: -3.5,-7.5 - parent: 325 - - uid: 130 - components: - - type: Transform - pos: 2.5,-8.5 - parent: 325 - - uid: 131 - components: - - type: Transform - pos: 2.5,-7.5 - parent: 325 - - uid: 132 - components: - - type: Transform - pos: -0.5,-5.5 - parent: 325 - - uid: 133 - components: - - type: Transform - pos: -0.5,-4.5 - parent: 325 - - uid: 134 - components: - - type: Transform - pos: -0.5,-3.5 - parent: 325 - - uid: 135 - components: - - type: Transform - pos: -0.5,-2.5 - parent: 325 - - uid: 136 - components: - - type: Transform - pos: -0.5,-1.5 - parent: 325 - - uid: 137 - components: - - type: Transform - pos: -0.5,-0.5 - parent: 325 - - uid: 138 - components: - - type: Transform - pos: -0.5,0.5 - parent: 325 - - uid: 139 - components: - - type: Transform - pos: -0.5,1.5 - parent: 325 - - uid: 140 - components: - - type: Transform - pos: -0.5,2.5 - parent: 325 - - uid: 141 - components: - - type: Transform - pos: -1.5,1.5 - parent: 325 - - uid: 143 - components: - - type: Transform - pos: 0.5,1.5 - parent: 325 - - uid: 145 - components: - - type: Transform - pos: -1.5,-1.5 - parent: 325 - - uid: 146 - components: - - type: Transform - pos: -2.5,-1.5 - parent: 325 - - uid: 147 - components: - - type: Transform - pos: -3.5,-1.5 - parent: 325 - - uid: 148 - components: - - type: Transform - pos: -4.5,-1.5 - parent: 325 - - uid: 149 - components: - - type: Transform - pos: 0.5,-1.5 - parent: 325 - - uid: 150 - components: - - type: Transform - pos: 1.5,-1.5 - parent: 325 - - uid: 151 - components: - - type: Transform - pos: 2.5,-1.5 - parent: 325 - - uid: 152 - components: - - type: Transform - pos: 3.5,-1.5 - parent: 325 - - uid: 153 - components: - - type: Transform - pos: 0.5,-4.5 - parent: 325 - - uid: 154 - components: - - type: Transform - pos: 1.5,-4.5 - parent: 325 - - uid: 155 - components: - - type: Transform - pos: 1.5,-5.5 - parent: 325 - - uid: 156 - components: - - type: Transform - pos: -1.5,-4.5 - parent: 325 - - uid: 157 - components: - - type: Transform - pos: -2.5,-4.5 - parent: 325 - - uid: 158 - components: - - type: Transform - pos: -2.5,-5.5 - parent: 325 -- proto: CableHV - entities: - - uid: 111 - components: - - type: Transform - pos: 1.5,-7.5 - parent: 325 - - uid: 112 - components: - - type: Transform - pos: 0.5,-7.5 - parent: 325 - - uid: 113 - components: - - type: Transform - pos: -0.5,-7.5 - parent: 325 - - uid: 114 - components: - - type: Transform - pos: -1.5,-7.5 - parent: 325 - - uid: 115 - components: - - type: Transform - pos: -2.5,-7.5 - parent: 325 - - uid: 116 - components: - - type: Transform - pos: -1.5,-6.5 - parent: 325 -- proto: CableHVStack1 - entities: - - uid: 235 - components: - - type: Transform - parent: 41 - - type: Stack - count: 10 - - type: Physics - canCollide: False - - uid: 239 - components: - - type: Transform - parent: 56 - - type: Stack - count: 10 - - type: Physics - canCollide: False -- proto: CableMV - entities: - - uid: 117 - components: - - type: Transform - pos: -1.5,-6.5 - parent: 325 - - uid: 118 - components: - - type: Transform - pos: -0.5,-6.5 - parent: 325 - - uid: 119 - components: - - type: Transform - pos: 0.5,-6.5 - parent: 325 -- proto: CapacitorStockPart - entities: - - uid: 233 - components: - - type: Transform - parent: 41 - - type: Physics - canCollide: False - - uid: 234 - components: - - type: Transform - parent: 41 - - type: Physics - canCollide: False - - uid: 237 - components: - - type: Transform - parent: 56 - - type: Physics - canCollide: False - - uid: 238 - components: - - type: Transform - parent: 56 - - type: Physics - canCollide: False - - uid: 241 - components: - - type: Transform - parent: 58 - - type: Physics - canCollide: False - - uid: 242 - components: - - type: Transform - parent: 58 - - type: Physics - canCollide: False - - uid: 243 - components: - - type: Transform - parent: 58 - - type: Physics - canCollide: False - - uid: 254 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 261 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 268 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 275 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 282 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 289 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 296 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 303 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False -- proto: Carpet - entities: - - uid: 74 - components: - - type: Transform - pos: -0.5,-4.5 - parent: 325 - - uid: 89 - components: - - type: Transform - pos: -0.5,-5.5 - parent: 325 -- proto: Catwalk - entities: - - uid: 159 - components: - - type: Transform - pos: -1.5,-7.5 - parent: 325 - - uid: 160 - components: - - type: Transform - pos: -0.5,-7.5 - parent: 325 - - uid: 161 - components: - - type: Transform - pos: 0.5,-7.5 - parent: 325 -- proto: ChairOfficeDark - entities: - - uid: 93 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -1.5,-2.5 - parent: 325 -- proto: ChairPilotSeat - entities: - - uid: 78 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,0.5 - parent: 325 -- proto: ComputerIFFSyndicate - entities: - - uid: 40 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,0.5 - parent: 325 -- proto: ComputerShuttleSyndie - entities: - - uid: 64 - components: - - type: Transform - pos: -0.5,1.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 245 -- proto: CyberPen - entities: - - uid: 77 - components: - - type: Transform - pos: -1.1813428,-5.15565 - parent: 325 -- proto: DoorElectronics - entities: - - uid: 331 - components: - - type: Transform - parent: 190 - - type: Physics - canCollide: False - - uid: 332 - components: - - type: Transform - parent: 191 - - type: Physics - canCollide: False - - uid: 333 - components: - - type: Transform - parent: 192 - - type: Physics - canCollide: False - - uid: 334 - components: - - type: Transform - parent: 193 - - type: Physics - canCollide: False - - uid: 337 - components: - - type: Transform - parent: 196 - - type: Physics - canCollide: False - - uid: 339 - components: - - type: Transform - parent: 198 - - type: Physics - canCollide: False - - uid: 340 - components: - - type: Transform - parent: 199 - - type: Physics - canCollide: False - - uid: 341 - components: - - type: Transform - parent: 200 - - type: Physics - canCollide: False - - uid: 342 - components: - - type: Transform - parent: 201 - - type: Physics - canCollide: False - - uid: 343 - components: - - type: Transform - parent: 202 - - type: Physics - canCollide: False - - uid: 346 - components: - - type: Transform - parent: 206 - - type: Physics - canCollide: False -- proto: DresserFilled - entities: - - uid: 85 - components: - - type: Transform - pos: 0.5,-4.5 - parent: 325 -- proto: DrinkNukieCan - entities: - - uid: 144 - components: - - type: Transform - pos: -2.6964839,-2.109029 - parent: 325 -- proto: FaxMachineSyndie - entities: - - uid: 46 - components: - - type: Transform - pos: -1.5,-5.5 - parent: 325 - - type: FaxMachine - name: Striker -- proto: filingCabinetRandom - entities: - - uid: 75 - components: - - type: Transform - pos: -1.5,-4.5 - parent: 325 -- proto: Firelock - entities: - - uid: 224 - components: - - type: Transform - pos: -0.5,-3.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 350 - - type: DeviceNetwork - address: 44a24659 - receiveFrequency: 1621 - - uid: 225 - components: - - type: Transform - pos: -0.5,-6.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 351 - - type: DeviceNetwork - address: 6fdb75cf - receiveFrequency: 1621 -- proto: FirelockElectronics - entities: - - uid: 350 - components: - - type: Transform - parent: 224 - - type: Physics - canCollide: False - - uid: 351 - components: - - type: Transform - parent: 225 - - type: Physics - canCollide: False -- proto: FoodBoxDonut - entities: - - uid: 87 - components: - - type: Transform - pos: -2.470145,-2.3953476 - parent: 325 -- proto: GasPipeFourway - entities: - - uid: 216 - components: - - type: Transform - pos: -0.5,-1.5 - parent: 325 -- proto: GasPipeStraight - entities: - - uid: 211 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-6.5 - parent: 325 - - uid: 213 - components: - - type: Transform - pos: -0.5,-4.5 - parent: 325 - - uid: 214 - components: - - type: Transform - pos: -0.5,-3.5 - parent: 325 - - uid: 215 - components: - - type: Transform - pos: -0.5,-2.5 - parent: 325 - - uid: 217 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-0.5 - parent: 325 -- proto: GasPipeTJunction - entities: - - uid: 210 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -0.5,-7.5 - parent: 325 - - uid: 212 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -0.5,-5.5 - parent: 325 -- proto: GasPort - entities: - - uid: 59 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-8.5 - parent: 325 - - type: AtmosDevice - joinedGrid: 325 -- proto: GasVentPump - entities: - - uid: 218 - components: - - type: Transform - pos: -0.5,0.5 - parent: 325 - - type: DeviceNetwork - address: Vnt-5f41a0ae - transmitFrequency: 1621 - receiveFrequency: 1621 - - type: AtmosDevice - joinedGrid: 325 - - uid: 219 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -1.5,-1.5 - parent: 325 - - type: DeviceNetwork - address: Vnt-129c27d2 - transmitFrequency: 1621 - receiveFrequency: 1621 - - type: AtmosDevice - joinedGrid: 325 - - uid: 220 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-1.5 - parent: 325 - - type: DeviceNetwork - address: Vnt-11c4609d - transmitFrequency: 1621 - receiveFrequency: 1621 - - type: AtmosDevice - joinedGrid: 325 - - uid: 221 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-5.5 - parent: 325 - - type: DeviceNetwork - address: Vnt-6859729f - transmitFrequency: 1621 - receiveFrequency: 1621 - - type: AtmosDevice - joinedGrid: 325 - - uid: 222 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -1.5,-7.5 - parent: 325 - - type: DeviceNetwork - address: Vnt-19d24c7f - transmitFrequency: 1621 - receiveFrequency: 1621 - - type: AtmosDevice - joinedGrid: 325 -- proto: GeneratorBasic15kW - entities: - - uid: 41 - components: - - type: Transform - pos: -2.5,-7.5 - parent: 325 - - type: PowerSupplier - supplyRampPosition: 7552.5303 - - type: ContainerContainer - containers: - machine_board: !type:Container - ents: - - 232 - machine_parts: !type:Container - ents: - - 233 - - 234 - - 235 - - uid: 56 - components: - - type: Transform - pos: 1.5,-7.5 - parent: 325 - - type: PowerSupplier - supplyRampPosition: 7552.5303 - - type: ContainerContainer - containers: - machine_board: !type:Container - ents: - - 236 - machine_parts: !type:Container - ents: - - 237 - - 238 - - 239 -- proto: GravityGeneratorMini - entities: - - uid: 57 - components: - - type: Transform - pos: -1.5,-8.5 - parent: 325 -- proto: Grille - entities: - - uid: 1 - components: - - type: Transform - pos: -0.5,2.5 - parent: 325 - - uid: 2 - components: - - type: Transform - pos: -1.5,2.5 - parent: 325 - - uid: 3 - components: - - type: Transform - pos: -1.5,1.5 - parent: 325 - - uid: 4 - components: - - type: Transform - pos: 0.5,2.5 - parent: 325 - - uid: 5 - components: - - type: Transform - pos: 0.5,1.5 - parent: 325 - - uid: 21 - components: - - type: Transform - pos: 3.5,-1.5 - parent: 325 - - uid: 50 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-5.5 - parent: 325 - - uid: 51 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-4.5 - parent: 325 - - uid: 52 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-5.5 - parent: 325 - - uid: 53 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-4.5 - parent: 325 -- proto: Gyroscope - entities: - - uid: 58 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,-8.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 240 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 241 - - 242 - - 243 - - 244 -- proto: GyroscopeMachineCircuitboard - entities: - - uid: 240 - components: - - type: Transform - parent: 58 - - type: Physics - canCollide: False -- proto: MedkitCombatFilled - entities: - - uid: 19 - components: - - type: Transform - pos: 1.48298,-0.3211529 - parent: 325 -- proto: MicroManipulatorStockPart - entities: - - uid: 250 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 251 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 252 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 253 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 257 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 258 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 259 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 260 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 264 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 265 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 266 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 267 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 271 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 272 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 273 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 274 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 278 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 279 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 280 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 281 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 285 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 286 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 287 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 288 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 292 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 293 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 294 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 295 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 299 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False - - uid: 300 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False - - uid: 301 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False - - uid: 302 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False -- proto: Mirror - entities: - - uid: 321 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-3.5 - parent: 325 -- proto: NitrogenTankFilled - entities: - - uid: 105 - components: - - type: Transform - pos: 1.373605,-0.2749618 - parent: 325 -- proto: NukeCodePaper - entities: - - uid: 323 - components: - - type: Transform - pos: 1.561105,-2.5567772 - parent: 325 -- proto: PinpointerNuclear - entities: - - uid: 162 - components: - - type: Transform - pos: 1.3790641,-2.3161128 - parent: 325 - - type: Physics - canCollide: False -- proto: PlasmaReinforcedWindowDirectional - entities: - - uid: 104 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-0.5 - parent: 325 - - uid: 109 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-0.5 - parent: 325 -- proto: PlushieNuke - entities: - - uid: 47 - components: - - type: Transform - pos: 0.5061571,-5.233775 - parent: 325 -- proto: PortableGeneratorSuperPacmanMachineCircuitboard - entities: - - uid: 232 - components: - - type: Transform - parent: 41 - - type: Physics - canCollide: False - - uid: 236 - components: - - type: Transform - parent: 56 - - type: Physics - canCollide: False -- proto: PosterContrabandC20r - entities: - - uid: 24 - components: - - type: Transform - pos: 2.5,-2.5 - parent: 325 -- proto: PosterContrabandEnergySwords - entities: - - uid: 227 - components: - - type: Transform - pos: -2.5,-6.5 - parent: 325 -- proto: PosterContrabandNuclearDeviceInformational - entities: - - uid: 228 - components: - - type: Transform - pos: -2.5,0.5 - parent: 325 -- proto: PosterContrabandSyndicateRecruitment - entities: - - uid: 229 - components: - - type: Transform - pos: 0.5,-3.5 - parent: 325 -- proto: Poweredlight - entities: - - uid: 94 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 0.5,0.5 - parent: 325 - - uid: 110 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -2.5,-2.5 - parent: 325 -- proto: PoweredlightLED - entities: - - uid: 182 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 2.5,-5.5 - parent: 325 - - type: ApcPowerReceiver - powerLoad: 0 - - uid: 183 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -3.5,-5.5 - parent: 325 - - type: ApcPowerReceiver - powerLoad: 0 - - uid: 184 - components: - - type: Transform - pos: -1.5,-7.5 - parent: 325 - - type: ApcPowerReceiver - powerLoad: 0 -- proto: PoweredSmallLight - entities: - - uid: 204 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-5.5 - parent: 325 - - type: ApcPowerReceiver - powerLoad: 0 -- proto: Rack - entities: - - uid: 83 - components: - - type: Transform - pos: 1.5,-0.5 - parent: 325 - - uid: 84 - components: - - type: Transform - pos: 1.5,-2.5 - parent: 325 -- proto: ReinforcedPlasmaWindow - entities: - - uid: 14 - components: - - type: Transform - pos: -1.5,1.5 - parent: 325 - - uid: 15 - components: - - type: Transform - pos: -1.5,2.5 - parent: 325 - - uid: 16 - components: - - type: Transform - pos: -0.5,2.5 - parent: 325 - - uid: 17 - components: - - type: Transform - pos: 0.5,2.5 - parent: 325 - - uid: 18 - components: - - type: Transform - pos: 0.5,1.5 - parent: 325 - - uid: 26 - components: - - type: Transform - pos: 3.5,-1.5 - parent: 325 - - uid: 42 - components: - - type: Transform - pos: 1.5,-4.5 - parent: 325 - - uid: 70 - components: - - type: Transform - pos: 1.5,-5.5 - parent: 325 - - uid: 71 - components: - - type: Transform - pos: -2.5,-4.5 - parent: 325 - - uid: 72 - components: - - type: Transform - pos: -2.5,-5.5 - parent: 325 -- proto: RemoteSignaller - entities: - - uid: 176 - components: - - type: Transform - pos: 1.3427892,-2.379079 - parent: 325 - - type: Physics - canCollide: False -- proto: SheetGlass1 - entities: - - uid: 244 - components: - - type: Transform - parent: 58 - - type: Stack - count: 2 - - type: Physics - canCollide: False -- proto: SheetSteel1 - entities: - - uid: 255 - components: - - type: Transform - parent: 95 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 262 - components: - - type: Transform - parent: 96 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 269 - components: - - type: Transform - parent: 97 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 276 - components: - - type: Transform - parent: 98 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 283 - components: - - type: Transform - parent: 99 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 290 - components: - - type: Transform - parent: 100 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 297 - components: - - type: Transform - parent: 101 - - type: Stack - count: 5 - - type: Physics - canCollide: False - - uid: 304 - components: - - type: Transform - parent: 102 - - type: Stack - count: 5 - - type: Physics - canCollide: False -- proto: SignalButton - entities: - - uid: 205 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-3.5 - parent: 325 - - type: DeviceLinkSource - linkedPorts: - 193: - - Pressed: Toggle - 192: - - Pressed: Toggle - 190: - - Pressed: Toggle - 191: - - Pressed: Toggle - 196: - - Pressed: Toggle - 202: - - Pressed: Toggle - 201: - - Pressed: Toggle - 200: - - Pressed: Toggle - 199: - - Pressed: Toggle - 198: - - Pressed: Toggle -- proto: SignSpace - entities: - - uid: 230 - components: - - type: Transform - pos: -3.5,-0.5 - parent: 325 -- proto: SoapSyndie - entities: - - uid: 90 - components: - - type: Transform - pos: 0.5436061,-7.5129323 - parent: 325 -- proto: SpawnPointNukies - entities: - - uid: 322 - components: - - type: Transform - pos: -0.5,-4.5 - parent: 325 -- proto: StealthBox - entities: - - uid: 106 - components: - - type: Transform - pos: 0.49860507,-2.4513345 - parent: 325 - - type: Stealth - enabled: False - - type: EntityStorage - open: True -- proto: SubstationWallBasic - entities: - - uid: 103 - components: - - type: Transform - pos: -1.5,-6.5 - parent: 325 - - type: PowerNetworkBattery - loadingNetworkDemand: 15106.935 - currentReceiving: 15105.06 - currentSupply: 15106.935 - supplyRampPosition: 1.875 -- proto: SuitStorageSyndie - entities: - - uid: 67 - components: - - type: Transform - pos: 2.5,-1.5 - parent: 325 -- proto: SyndicateCommsComputerCircuitboard - entities: - - uid: 246 - components: - - type: Transform - parent: 65 - - type: Physics - canCollide: False -- proto: SyndicateComputerComms - entities: - - uid: 65 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -1.5,0.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 246 -- proto: SyndicateIDCard - entities: - - uid: 324 - components: - - type: Transform - pos: 1.57673,-2.3849022 - parent: 325 -- proto: SyndicateShuttleConsoleCircuitboard - entities: - - uid: 245 - components: - - type: Transform - parent: 64 - - type: Physics - canCollide: False -- proto: Table - entities: - - uid: 165 - components: - - type: Transform - pos: -2.5,-2.5 - parent: 325 -- proto: TableWood - entities: - - uid: 45 - components: - - type: Transform - pos: -1.5,-5.5 - parent: 325 -- proto: Thruster - entities: - - uid: 95 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -3.5,-9.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 249 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 250 - - 251 - - 252 - - 253 - - 254 - - 255 - - uid: 96 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 2.5,-9.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 256 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 257 - - 258 - - 259 - - 260 - - 261 - - 262 - - uid: 97 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-4.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 263 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 264 - - 265 - - 266 - - 267 - - 268 - - 269 - - uid: 98 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-5.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 270 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 271 - - 272 - - 273 - - 274 - - 275 - - 276 - - uid: 99 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-4.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 277 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 278 - - 279 - - 280 - - 281 - - 282 - - 283 - - uid: 100 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-5.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 284 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 285 - - 286 - - 287 - - 288 - - 289 - - 290 - - uid: 101 - components: - - type: Transform - pos: -3.5,1.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 291 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 292 - - 293 - - 294 - - 295 - - 296 - - 297 - - uid: 102 - components: - - type: Transform - pos: 2.5,1.5 - parent: 325 - - type: ContainerContainer - containers: - machine_board: !type:Container - showEnts: False - occludes: True - ents: - - 298 - machine_parts: !type:Container - showEnts: False - occludes: True - ents: - - 299 - - 300 - - 301 - - 302 - - 303 - - 304 -- proto: ThrusterMachineCircuitboard - entities: - - uid: 249 - components: - - type: Transform - parent: 95 - - type: Physics - canCollide: False - - uid: 256 - components: - - type: Transform - parent: 96 - - type: Physics - canCollide: False - - uid: 263 - components: - - type: Transform - parent: 97 - - type: Physics - canCollide: False - - uid: 270 - components: - - type: Transform - parent: 98 - - type: Physics - canCollide: False - - uid: 277 - components: - - type: Transform - parent: 99 - - type: Physics - canCollide: False - - uid: 284 - components: - - type: Transform - parent: 100 - - type: Physics - canCollide: False - - uid: 291 - components: - - type: Transform - parent: 101 - - type: Physics - canCollide: False - - uid: 298 - components: - - type: Transform - parent: 102 - - type: Physics - canCollide: False -- proto: ToolboxSyndicateFilled - entities: - - uid: 177 - components: - - type: Transform - pos: 1.5699697,-0.44908836 - parent: 325 - - type: Physics - canCollide: False -- proto: ToyFigurineNukie - entities: - - uid: 10 - components: - - type: Transform - pos: -2.3371089,-2.140279 - parent: 325 -- proto: VendingMachineSyndieDrobe - entities: - - uid: 163 - components: - - type: Transform - pos: -2.5,-0.5 - parent: 325 -- proto: WallPlastitanium - entities: - - uid: 7 - components: - - type: Transform - pos: -2.5,0.5 - parent: 325 - - uid: 8 - components: - - type: Transform - pos: -3.5,0.5 - parent: 325 - - uid: 9 - components: - - type: Transform - pos: -3.5,-0.5 - parent: 325 - - uid: 11 - components: - - type: Transform - pos: 1.5,0.5 - parent: 325 - - uid: 12 - components: - - type: Transform - pos: 2.5,0.5 - parent: 325 - - uid: 13 - components: - - type: Transform - pos: -4.5,-0.5 - parent: 325 - - uid: 22 - components: - - type: Transform - pos: 3.5,-0.5 - parent: 325 - - uid: 25 - components: - - type: Transform - pos: 3.5,-2.5 - parent: 325 - - uid: 27 - components: - - type: Transform - pos: -3.5,-2.5 - parent: 325 - - uid: 28 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -3.5,-6.5 - parent: 325 - - uid: 29 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-6.5 - parent: 325 - - uid: 30 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-6.5 - parent: 325 - - uid: 31 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -3.5,-8.5 - parent: 325 - - uid: 32 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-8.5 - parent: 325 - - uid: 33 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-6.5 - parent: 325 - - uid: 34 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-3.5 - parent: 325 - - uid: 35 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-9.5 - parent: 325 - - uid: 36 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-8.5 - parent: 325 - - uid: 37 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,-9.5 - parent: 325 - - uid: 38 - components: - - type: Transform - pos: -4.5,-2.5 - parent: 325 - - uid: 39 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-3.5 - parent: 325 - - uid: 44 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 2.5,-8.5 - parent: 325 - - uid: 48 - components: - - type: Transform - pos: 2.5,-7.5 - parent: 325 - - uid: 49 - components: - - type: Transform - pos: -3.5,-7.5 - parent: 325 - - uid: 54 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -2.5,-3.5 - parent: 325 - - uid: 55 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -3.5,-3.5 - parent: 325 - - uid: 60 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-6.5 - parent: 325 - - uid: 61 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-6.5 - parent: 325 - - uid: 62 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -1.5,-3.5 - parent: 325 - - uid: 63 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 0.5,-3.5 - parent: 325 - - uid: 66 - components: - - type: Transform - pos: 0.5,-9.5 - parent: 325 - - uid: 69 - components: - - type: Transform - pos: -2.5,1.5 - parent: 325 - - uid: 73 - components: - - type: Transform - pos: 1.5,1.5 - parent: 325 - - uid: 80 - components: - - type: Transform - pos: 2.5,-2.5 - parent: 325 - - uid: 81 - components: - - type: Transform - pos: 2.5,-0.5 - parent: 325 - - uid: 92 - components: - - type: Transform - pos: -1.5,-9.5 - parent: 325 - - uid: 108 - components: - - type: Transform - pos: -0.5,-9.5 - parent: 325 -- proto: WallPlastitaniumDiagonal - entities: - - uid: 23 - components: - - type: Transform - pos: -4.5,0.5 - parent: 325 - - uid: 43 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 3.5,0.5 - parent: 325 - - uid: 68 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 1.5,2.5 - parent: 325 - - uid: 79 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -4.5,-3.5 - parent: 325 - - uid: 82 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 3.5,-3.5 - parent: 325 - - uid: 86 - components: - - type: Transform - pos: -2.5,2.5 - parent: 325 -- proto: WindoorSecure - entities: - - uid: 166 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -3.5,-1.5 - parent: 325 - - uid: 206 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: -0.5,-0.5 - parent: 325 - - type: ContainerContainer - containers: - board: !type:Container - showEnts: False - occludes: True - ents: - - 346 -- proto: YellowOxygenTankFilled - entities: - - uid: 167 - components: - - type: Transform - pos: 1.60798,-0.3062118 - parent: 325 -... +meta: + format: 6 + postmapinit: false +tilemap: + 0: Space + 29: FloorDark + 84: FloorShuttleRed + 104: FloorTechMaint + 105: FloorTechMaint2 + 118: FloorWood + 120: Lattice + 121: Plating +entities: +- proto: "" + entities: + - uid: 325 + components: + - type: MetaData + - type: Transform + pos: 0.5638949,0.47865233 + parent: invalid + - type: MapGrid + chunks: + -1,-1: + ind: -1,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAdgAAAAAAdgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAdgAAAAADdgAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAaQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAHQAAAAADHQAAAAADHQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaQAAAAAAaQAAAAAAHQAAAAABHQAAAAABHQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAHQAAAAABHQAAAAACHQAAAAAB + version: 6 + 0,-1: + ind: 0,-1 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAaAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAACeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAdgAAAAABeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAAAHQAAAAABeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAABHQAAAAABHQAAAAABeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHQAAAAADHQAAAAACeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + -1,0: + ind: -1,0 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAVAAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + 0,0: + ind: 0,0 + tiles: VAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + - type: Broadphase + - type: Physics + bodyStatus: InAir + angularDamping: 0.05 + linearDamping: 0.05 + fixedRotation: False + bodyType: Dynamic + - type: Fixtures + fixtures: {} + - type: OccluderTree + - type: Shuttle + - type: Gravity + gravityShakeSound: !type:SoundPathSpecifier + path: /Audio/Effects/alert.ogg + - type: DecalGrid + chunkCollection: + version: 2 + nodes: + - node: + color: '#FFFFFFFF' + id: BrickTileDarkCornerNe + decals: + 11: 1,-1 + - node: + color: '#FFFFFFFF' + id: BrickTileDarkCornerNw + decals: + 5: -3,-1 + - node: + color: '#FFFFFFFF' + id: BrickTileDarkCornerSe + decals: + 4: 1,-3 + - node: + color: '#FFFFFFFF' + id: BrickTileDarkCornerSw + decals: + 3: -3,-3 + - node: + color: '#FFFFFFFF' + id: BrickTileDarkLineS + decals: + 0: -1,-3 + 1: -2,-3 + 2: 0,-3 + - node: + color: '#7F1C1FFF' + id: BrickTileWhiteCornerNe + decals: + 13: 1,-1 + - node: + color: '#7F1C1FFF' + id: BrickTileWhiteCornerNw + decals: + 12: -3,-1 + - node: + color: '#7F1C1FFF' + id: BrickTileWhiteCornerSe + decals: + 9: 1,-3 + - node: + color: '#7F1C1FFF' + id: BrickTileWhiteCornerSw + decals: + 10: -3,-3 + - node: + color: '#7F1C1FFF' + id: BrickTileWhiteLineS + decals: + 6: -2,-3 + 7: -1,-3 + 8: 0,-3 + - node: + color: '#FFFFFFFF' + id: Delivery + decals: + 23: 2,-2 + 24: -4,-2 + - node: + color: '#FFFFFFFF' + id: WarnLineE + decals: + 14: 1,-2 + - node: + color: '#FFFFFFFF' + id: WarnLineS + decals: + 16: -3,-2 + - node: + color: '#FFFFFFFF' + id: WarnLineW + decals: + 15: -1,-1 + - node: + color: '#FFFFFFFF' + id: WoodTrimThinLineN + decals: + 17: -1,-5 + 18: 0,-5 + 19: -2,-5 + - node: + color: '#FFFFFFFF' + id: WoodTrimThinLineS + decals: + 20: -2,-6 + 21: -1,-6 + 22: 0,-6 + - type: GridAtmosphere + version: 2 + data: + tiles: + -1,-1: + 0: 65535 + 0,-1: + 0: 65535 + -2,-1: + 0: 52424 + -1,-3: + 0: 65280 + -1,-2: + 0: 65535 + 0,-3: + 0: 30464 + 0,-2: + 0: 30583 + -2,0: + 0: 8 + -1,0: + 0: 3839 + 0,0: + 0: 895 + uniqueMixes: + - volume: 2500 + temperature: 293.15 + moles: + - 21.824879 + - 82.10312 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + chunkSize: 4 + - type: GasTileOverlay + - type: RadiationGridResistance + - type: GravityShake + shakeTimes: 10 + - type: SpreaderGrid + - type: GridPathfinding +- proto: AirCanister + entities: + - uid: 91 + components: + - type: Transform + pos: -0.5,-8.5 + parent: 325 + - type: AtmosDevice + joinedGrid: 325 +- proto: AirlockExternalShuttleSyndicateLocked + entities: + - uid: 142 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,-1.5 + parent: 325 +- proto: AirlockSyndicateLocked + entities: + - uid: 20 + components: + - type: Transform + pos: -0.5,-3.5 + parent: 325 + - uid: 88 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 325 +- proto: APCBasic + entities: + - uid: 107 + components: + - type: Transform + pos: 0.5,-6.5 + parent: 325 + - type: PowerNetworkBattery + loadingNetworkDemand: 15107 + currentReceiving: 15106.935 + currentSupply: 15107 + supplyRampPosition: 0.064453125 +- proto: AtmosDeviceFanTiny + entities: + - uid: 6 + components: + - type: Transform + pos: -3.5,-1.5 + parent: 325 +- proto: Bed + entities: + - uid: 76 + components: + - type: Transform + pos: 0.5,-5.5 + parent: 325 +- proto: BedsheetSyndie + entities: + - uid: 164 + components: + - type: Transform + pos: 0.5,-5.5 + parent: 325 +- proto: BlastDoorOpen + entities: + - uid: 190 + components: + - type: Transform + pos: 1.5,-5.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 331 + - type: DeviceLinkSink + links: + - 205 + - uid: 191 + components: + - type: Transform + pos: 1.5,-4.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 332 + - type: DeviceLinkSink + links: + - 205 + - uid: 192 + components: + - type: Transform + pos: -2.5,-5.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 333 + - type: DeviceLinkSink + links: + - 205 + - uid: 193 + components: + - type: Transform + pos: -2.5,-4.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 334 + - type: DeviceLinkSink + links: + - 205 + - uid: 196 + components: + - type: Transform + pos: 3.5,-1.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 337 + - type: DeviceLinkSink + links: + - 205 + - uid: 198 + components: + - type: Transform + pos: -1.5,1.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 339 + - type: DeviceLinkSink + links: + - 205 + - uid: 199 + components: + - type: Transform + pos: -1.5,2.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 340 + - type: DeviceLinkSink + links: + - 205 + - uid: 200 + components: + - type: Transform + pos: -0.5,2.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 341 + - type: DeviceLinkSink + links: + - 205 + - uid: 201 + components: + - type: Transform + pos: 0.5,2.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 342 + - type: DeviceLinkSink + links: + - 205 + - uid: 202 + components: + - type: Transform + pos: 0.5,1.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 343 + - type: DeviceLinkSink + links: + - 205 +- proto: BoxMRE + entities: + - uid: 320 + components: + - type: Transform + pos: 0.70504504,-7.29326 + parent: 325 +- proto: CableApcExtension + entities: + - uid: 120 + components: + - type: Transform + pos: 0.5,-6.5 + parent: 325 + - uid: 121 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 325 + - uid: 122 + components: + - type: Transform + pos: -0.5,-7.5 + parent: 325 + - uid: 123 + components: + - type: Transform + pos: -0.5,-8.5 + parent: 325 + - uid: 124 + components: + - type: Transform + pos: -1.5,-8.5 + parent: 325 + - uid: 125 + components: + - type: Transform + pos: 0.5,-8.5 + parent: 325 + - uid: 126 + components: + - type: Transform + pos: 1.5,-8.5 + parent: 325 + - uid: 127 + components: + - type: Transform + pos: -2.5,-8.5 + parent: 325 + - uid: 128 + components: + - type: Transform + pos: -3.5,-8.5 + parent: 325 + - uid: 129 + components: + - type: Transform + pos: -3.5,-7.5 + parent: 325 + - uid: 130 + components: + - type: Transform + pos: 2.5,-8.5 + parent: 325 + - uid: 131 + components: + - type: Transform + pos: 2.5,-7.5 + parent: 325 + - uid: 132 + components: + - type: Transform + pos: -0.5,-5.5 + parent: 325 + - uid: 133 + components: + - type: Transform + pos: -0.5,-4.5 + parent: 325 + - uid: 134 + components: + - type: Transform + pos: -0.5,-3.5 + parent: 325 + - uid: 135 + components: + - type: Transform + pos: -0.5,-2.5 + parent: 325 + - uid: 136 + components: + - type: Transform + pos: -0.5,-1.5 + parent: 325 + - uid: 137 + components: + - type: Transform + pos: -0.5,-0.5 + parent: 325 + - uid: 138 + components: + - type: Transform + pos: -0.5,0.5 + parent: 325 + - uid: 139 + components: + - type: Transform + pos: -0.5,1.5 + parent: 325 + - uid: 140 + components: + - type: Transform + pos: -0.5,2.5 + parent: 325 + - uid: 141 + components: + - type: Transform + pos: -1.5,1.5 + parent: 325 + - uid: 143 + components: + - type: Transform + pos: 0.5,1.5 + parent: 325 + - uid: 145 + components: + - type: Transform + pos: -1.5,-1.5 + parent: 325 + - uid: 146 + components: + - type: Transform + pos: -2.5,-1.5 + parent: 325 + - uid: 147 + components: + - type: Transform + pos: -3.5,-1.5 + parent: 325 + - uid: 148 + components: + - type: Transform + pos: -4.5,-1.5 + parent: 325 + - uid: 149 + components: + - type: Transform + pos: 0.5,-1.5 + parent: 325 + - uid: 150 + components: + - type: Transform + pos: 1.5,-1.5 + parent: 325 + - uid: 151 + components: + - type: Transform + pos: 2.5,-1.5 + parent: 325 + - uid: 152 + components: + - type: Transform + pos: 3.5,-1.5 + parent: 325 + - uid: 153 + components: + - type: Transform + pos: 0.5,-4.5 + parent: 325 + - uid: 154 + components: + - type: Transform + pos: 1.5,-4.5 + parent: 325 + - uid: 155 + components: + - type: Transform + pos: 1.5,-5.5 + parent: 325 + - uid: 156 + components: + - type: Transform + pos: -1.5,-4.5 + parent: 325 + - uid: 157 + components: + - type: Transform + pos: -2.5,-4.5 + parent: 325 + - uid: 158 + components: + - type: Transform + pos: -2.5,-5.5 + parent: 325 +- proto: CableHV + entities: + - uid: 111 + components: + - type: Transform + pos: 1.5,-7.5 + parent: 325 + - uid: 112 + components: + - type: Transform + pos: 0.5,-7.5 + parent: 325 + - uid: 113 + components: + - type: Transform + pos: -0.5,-7.5 + parent: 325 + - uid: 114 + components: + - type: Transform + pos: -1.5,-7.5 + parent: 325 + - uid: 115 + components: + - type: Transform + pos: -2.5,-7.5 + parent: 325 + - uid: 116 + components: + - type: Transform + pos: -1.5,-6.5 + parent: 325 +- proto: CableHVStack1 + entities: + - uid: 235 + components: + - type: Transform + parent: 41 + - type: Stack + count: 10 + - type: Physics + canCollide: False + - uid: 239 + components: + - type: Transform + parent: 56 + - type: Stack + count: 10 + - type: Physics + canCollide: False +- proto: CableMV + entities: + - uid: 117 + components: + - type: Transform + pos: -1.5,-6.5 + parent: 325 + - uid: 118 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 325 + - uid: 119 + components: + - type: Transform + pos: 0.5,-6.5 + parent: 325 +- proto: CapacitorStockPart + entities: + - uid: 233 + components: + - type: Transform + parent: 41 + - type: Physics + canCollide: False + - uid: 234 + components: + - type: Transform + parent: 41 + - type: Physics + canCollide: False + - uid: 237 + components: + - type: Transform + parent: 56 + - type: Physics + canCollide: False + - uid: 238 + components: + - type: Transform + parent: 56 + - type: Physics + canCollide: False + - uid: 241 + components: + - type: Transform + parent: 58 + - type: Physics + canCollide: False + - uid: 242 + components: + - type: Transform + parent: 58 + - type: Physics + canCollide: False + - uid: 243 + components: + - type: Transform + parent: 58 + - type: Physics + canCollide: False + - uid: 254 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 261 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 268 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 275 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 282 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 289 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 296 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 303 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False +- proto: Carpet + entities: + - uid: 74 + components: + - type: Transform + pos: -0.5,-4.5 + parent: 325 + - uid: 89 + components: + - type: Transform + pos: -0.5,-5.5 + parent: 325 +- proto: Catwalk + entities: + - uid: 159 + components: + - type: Transform + pos: -1.5,-7.5 + parent: 325 + - uid: 160 + components: + - type: Transform + pos: -0.5,-7.5 + parent: 325 + - uid: 161 + components: + - type: Transform + pos: 0.5,-7.5 + parent: 325 +- proto: ChairOfficeDark + entities: + - uid: 93 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -1.5,-2.5 + parent: 325 +- proto: ChairPilotSeat + entities: + - uid: 78 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,0.5 + parent: 325 +- proto: ComputerIFFSyndicate + entities: + - uid: 40 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,0.5 + parent: 325 +- proto: ComputerShuttleSyndie + entities: + - uid: 64 + components: + - type: Transform + pos: -0.5,1.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 245 +- proto: CyberPen + entities: + - uid: 77 + components: + - type: Transform + pos: -1.1813428,-5.15565 + parent: 325 +- proto: DoorElectronics + entities: + - uid: 331 + components: + - type: Transform + parent: 190 + - type: Physics + canCollide: False + - uid: 332 + components: + - type: Transform + parent: 191 + - type: Physics + canCollide: False + - uid: 333 + components: + - type: Transform + parent: 192 + - type: Physics + canCollide: False + - uid: 334 + components: + - type: Transform + parent: 193 + - type: Physics + canCollide: False + - uid: 337 + components: + - type: Transform + parent: 196 + - type: Physics + canCollide: False + - uid: 339 + components: + - type: Transform + parent: 198 + - type: Physics + canCollide: False + - uid: 340 + components: + - type: Transform + parent: 199 + - type: Physics + canCollide: False + - uid: 341 + components: + - type: Transform + parent: 200 + - type: Physics + canCollide: False + - uid: 342 + components: + - type: Transform + parent: 201 + - type: Physics + canCollide: False + - uid: 343 + components: + - type: Transform + parent: 202 + - type: Physics + canCollide: False + - uid: 346 + components: + - type: Transform + parent: 206 + - type: Physics + canCollide: False +- proto: DresserFilled + entities: + - uid: 85 + components: + - type: Transform + pos: 0.5,-4.5 + parent: 325 +- proto: DrinkNukieCan + entities: + - uid: 144 + components: + - type: Transform + pos: -2.6964839,-2.109029 + parent: 325 +- proto: FaxMachineSyndie + entities: + - uid: 46 + components: + - type: Transform + pos: -1.5,-5.5 + parent: 325 + - type: FaxMachine + name: Striker +- proto: filingCabinetRandom + entities: + - uid: 75 + components: + - type: Transform + pos: -1.5,-4.5 + parent: 325 +- proto: Firelock + entities: + - uid: 224 + components: + - type: Transform + pos: -0.5,-3.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 350 + - type: DeviceNetwork + address: 44a24659 + receiveFrequency: 1621 + - uid: 225 + components: + - type: Transform + pos: -0.5,-6.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 351 + - type: DeviceNetwork + address: 6fdb75cf + receiveFrequency: 1621 +- proto: FirelockElectronics + entities: + - uid: 350 + components: + - type: Transform + parent: 224 + - type: Physics + canCollide: False + - uid: 351 + components: + - type: Transform + parent: 225 + - type: Physics + canCollide: False +- proto: FoodBoxDonut + entities: + - uid: 87 + components: + - type: Transform + pos: -2.470145,-2.3953476 + parent: 325 +- proto: GasPipeFourway + entities: + - uid: 216 + components: + - type: Transform + pos: -0.5,-1.5 + parent: 325 +- proto: GasPipeStraight + entities: + - uid: 211 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-6.5 + parent: 325 + - uid: 213 + components: + - type: Transform + pos: -0.5,-4.5 + parent: 325 + - uid: 214 + components: + - type: Transform + pos: -0.5,-3.5 + parent: 325 + - uid: 215 + components: + - type: Transform + pos: -0.5,-2.5 + parent: 325 + - uid: 217 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-0.5 + parent: 325 +- proto: GasPipeTJunction + entities: + - uid: 210 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -0.5,-7.5 + parent: 325 + - uid: 212 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -0.5,-5.5 + parent: 325 +- proto: GasPort + entities: + - uid: 59 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-8.5 + parent: 325 + - type: AtmosDevice + joinedGrid: 325 +- proto: GasVentPump + entities: + - uid: 218 + components: + - type: Transform + pos: -0.5,0.5 + parent: 325 + - type: DeviceNetwork + address: Vnt-5f41a0ae + transmitFrequency: 1621 + receiveFrequency: 1621 + - type: AtmosDevice + joinedGrid: 325 + - uid: 219 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-1.5 + parent: 325 + - type: DeviceNetwork + address: Vnt-129c27d2 + transmitFrequency: 1621 + receiveFrequency: 1621 + - type: AtmosDevice + joinedGrid: 325 + - uid: 220 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,-1.5 + parent: 325 + - type: DeviceNetwork + address: Vnt-11c4609d + transmitFrequency: 1621 + receiveFrequency: 1621 + - type: AtmosDevice + joinedGrid: 325 + - uid: 221 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,-5.5 + parent: 325 + - type: DeviceNetwork + address: Vnt-6859729f + transmitFrequency: 1621 + receiveFrequency: 1621 + - type: AtmosDevice + joinedGrid: 325 + - uid: 222 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,-7.5 + parent: 325 + - type: DeviceNetwork + address: Vnt-19d24c7f + transmitFrequency: 1621 + receiveFrequency: 1621 + - type: AtmosDevice + joinedGrid: 325 +- proto: GeneratorBasic15kW + entities: + - uid: 41 + components: + - type: Transform + pos: -2.5,-7.5 + parent: 325 + - type: PowerSupplier + supplyRampPosition: 7552.5303 + - type: ContainerContainer + containers: + machine_board: !type:Container + ents: + - 232 + machine_parts: !type:Container + ents: + - 233 + - 234 + - 235 + - uid: 56 + components: + - type: Transform + pos: 1.5,-7.5 + parent: 325 + - type: PowerSupplier + supplyRampPosition: 7552.5303 + - type: ContainerContainer + containers: + machine_board: !type:Container + ents: + - 236 + machine_parts: !type:Container + ents: + - 237 + - 238 + - 239 +- proto: GravityGeneratorMini + entities: + - uid: 57 + components: + - type: Transform + pos: -1.5,-8.5 + parent: 325 +- proto: Grille + entities: + - uid: 1 + components: + - type: Transform + pos: -0.5,2.5 + parent: 325 + - uid: 2 + components: + - type: Transform + pos: -1.5,2.5 + parent: 325 + - uid: 3 + components: + - type: Transform + pos: -1.5,1.5 + parent: 325 + - uid: 4 + components: + - type: Transform + pos: 0.5,2.5 + parent: 325 + - uid: 5 + components: + - type: Transform + pos: 0.5,1.5 + parent: 325 + - uid: 21 + components: + - type: Transform + pos: 3.5,-1.5 + parent: 325 + - uid: 50 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-5.5 + parent: 325 + - uid: 51 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-4.5 + parent: 325 + - uid: 52 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-5.5 + parent: 325 + - uid: 53 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-4.5 + parent: 325 +- proto: Gyroscope + entities: + - uid: 58 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,-8.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 240 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 241 + - 242 + - 243 + - 244 +- proto: GyroscopeMachineCircuitboard + entities: + - uid: 240 + components: + - type: Transform + parent: 58 + - type: Physics + canCollide: False +- proto: MedkitCombatFilled + entities: + - uid: 19 + components: + - type: Transform + pos: 1.48298,-0.3211529 + parent: 325 +- proto: MicroManipulatorStockPart + entities: + - uid: 250 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 251 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 252 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 253 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 257 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 258 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 259 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 260 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 264 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 265 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 266 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 267 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 271 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 272 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 273 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 274 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 278 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 279 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 280 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 281 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 285 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 286 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 287 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 288 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 292 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 293 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 294 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 295 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 299 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False + - uid: 300 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False + - uid: 301 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False + - uid: 302 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False +- proto: Mirror + entities: + - uid: 321 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -2.5,-3.5 + parent: 325 +- proto: NitrogenTankFilled + entities: + - uid: 105 + components: + - type: Transform + pos: 1.373605,-0.2749618 + parent: 325 +- proto: NukeCodePaper + entities: + - uid: 323 + components: + - type: Transform + pos: 1.561105,-2.5567772 + parent: 325 +- proto: PinpointerNuclear + entities: + - uid: 162 + components: + - type: Transform + pos: 1.3790641,-2.3161128 + parent: 325 + - type: Physics + canCollide: False +- proto: PlasmaReinforcedWindowDirectional + entities: + - uid: 104 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-0.5 + parent: 325 + - uid: 109 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-0.5 + parent: 325 +- proto: PlushieNuke + entities: + - uid: 47 + components: + - type: Transform + pos: 0.5061571,-5.233775 + parent: 325 +- proto: PortableGeneratorSuperPacmanMachineCircuitboard + entities: + - uid: 232 + components: + - type: Transform + parent: 41 + - type: Physics + canCollide: False + - uid: 236 + components: + - type: Transform + parent: 56 + - type: Physics + canCollide: False +- proto: PosterContrabandC20r + entities: + - uid: 24 + components: + - type: Transform + pos: 2.5,-2.5 + parent: 325 +- proto: PosterContrabandEnergySwords + entities: + - uid: 227 + components: + - type: Transform + pos: -2.5,-6.5 + parent: 325 +- proto: PosterContrabandNuclearDeviceInformational + entities: + - uid: 228 + components: + - type: Transform + pos: -2.5,0.5 + parent: 325 +- proto: PosterContrabandSyndicateRecruitment + entities: + - uid: 229 + components: + - type: Transform + pos: 0.5,-3.5 + parent: 325 +- proto: Poweredlight + entities: + - uid: 94 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 0.5,0.5 + parent: 325 + - uid: 110 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -2.5,-2.5 + parent: 325 +- proto: PoweredlightLED + entities: + - uid: 182 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 2.5,-5.5 + parent: 325 + - type: ApcPowerReceiver + powerLoad: 0 + - uid: 183 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-5.5 + parent: 325 + - type: ApcPowerReceiver + powerLoad: 0 + - uid: 184 + components: + - type: Transform + pos: -1.5,-7.5 + parent: 325 + - type: ApcPowerReceiver + powerLoad: 0 +- proto: PoweredSmallLight + entities: + - uid: 204 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-5.5 + parent: 325 + - type: ApcPowerReceiver + powerLoad: 0 +- proto: Rack + entities: + - uid: 83 + components: + - type: Transform + pos: 1.5,-0.5 + parent: 325 + - uid: 84 + components: + - type: Transform + pos: 1.5,-2.5 + parent: 325 +- proto: ReinforcedPlasmaWindow + entities: + - uid: 14 + components: + - type: Transform + pos: -1.5,1.5 + parent: 325 + - uid: 15 + components: + - type: Transform + pos: -1.5,2.5 + parent: 325 + - uid: 16 + components: + - type: Transform + pos: -0.5,2.5 + parent: 325 + - uid: 17 + components: + - type: Transform + pos: 0.5,2.5 + parent: 325 + - uid: 18 + components: + - type: Transform + pos: 0.5,1.5 + parent: 325 + - uid: 26 + components: + - type: Transform + pos: 3.5,-1.5 + parent: 325 + - uid: 42 + components: + - type: Transform + pos: 1.5,-4.5 + parent: 325 + - uid: 70 + components: + - type: Transform + pos: 1.5,-5.5 + parent: 325 + - uid: 71 + components: + - type: Transform + pos: -2.5,-4.5 + parent: 325 + - uid: 72 + components: + - type: Transform + pos: -2.5,-5.5 + parent: 325 +- proto: RemoteSignaller + entities: + - uid: 176 + components: + - type: Transform + pos: 1.3427892,-2.379079 + parent: 325 + - type: Physics + canCollide: False +- proto: SheetGlass1 + entities: + - uid: 244 + components: + - type: Transform + parent: 58 + - type: Stack + count: 2 + - type: Physics + canCollide: False +- proto: SheetSteel1 + entities: + - uid: 255 + components: + - type: Transform + parent: 95 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 262 + components: + - type: Transform + parent: 96 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 269 + components: + - type: Transform + parent: 97 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 276 + components: + - type: Transform + parent: 98 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 283 + components: + - type: Transform + parent: 99 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 290 + components: + - type: Transform + parent: 100 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 297 + components: + - type: Transform + parent: 101 + - type: Stack + count: 5 + - type: Physics + canCollide: False + - uid: 304 + components: + - type: Transform + parent: 102 + - type: Stack + count: 5 + - type: Physics + canCollide: False +- proto: SignalButton + entities: + - uid: 205 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-3.5 + parent: 325 + - type: DeviceLinkSource + linkedPorts: + 193: + - Pressed: Toggle + 192: + - Pressed: Toggle + 190: + - Pressed: Toggle + 191: + - Pressed: Toggle + 196: + - Pressed: Toggle + 202: + - Pressed: Toggle + 201: + - Pressed: Toggle + 200: + - Pressed: Toggle + 199: + - Pressed: Toggle + 198: + - Pressed: Toggle +- proto: SignSpace + entities: + - uid: 230 + components: + - type: Transform + pos: -3.5,-0.5 + parent: 325 +- proto: SoapSyndie + entities: + - uid: 90 + components: + - type: Transform + pos: 0.5436061,-7.5129323 + parent: 325 +- proto: SpawnPointLoneNukeOperative + entities: + - uid: 322 + components: + - type: Transform + pos: -0.5,-4.5 + parent: 325 +- proto: StealthBox + entities: + - uid: 106 + components: + - type: Transform + pos: 0.49860507,-2.4513345 + parent: 325 + - type: Stealth + enabled: False + - type: EntityStorage + open: True +- proto: SubstationWallBasic + entities: + - uid: 103 + components: + - type: Transform + pos: -1.5,-6.5 + parent: 325 + - type: PowerNetworkBattery + loadingNetworkDemand: 15106.935 + currentReceiving: 15105.06 + currentSupply: 15106.935 + supplyRampPosition: 1.875 +- proto: SuitStorageSyndie + entities: + - uid: 67 + components: + - type: Transform + pos: 2.5,-1.5 + parent: 325 +- proto: SyndicateCommsComputerCircuitboard + entities: + - uid: 246 + components: + - type: Transform + parent: 65 + - type: Physics + canCollide: False +- proto: SyndicateComputerComms + entities: + - uid: 65 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.5,0.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 246 +- proto: SyndicateIDCard + entities: + - uid: 324 + components: + - type: Transform + pos: 1.57673,-2.3849022 + parent: 325 +- proto: SyndicateShuttleConsoleCircuitboard + entities: + - uid: 245 + components: + - type: Transform + parent: 64 + - type: Physics + canCollide: False +- proto: Table + entities: + - uid: 165 + components: + - type: Transform + pos: -2.5,-2.5 + parent: 325 +- proto: TableWood + entities: + - uid: 45 + components: + - type: Transform + pos: -1.5,-5.5 + parent: 325 +- proto: Thruster + entities: + - uid: 95 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,-9.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 249 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 250 + - 251 + - 252 + - 253 + - 254 + - 255 + - uid: 96 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 2.5,-9.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 256 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 257 + - 258 + - 259 + - 260 + - 261 + - 262 + - uid: 97 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-4.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 263 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 264 + - 265 + - 266 + - 267 + - 268 + - 269 + - uid: 98 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-5.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 270 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 271 + - 272 + - 273 + - 274 + - 275 + - 276 + - uid: 99 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-4.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 277 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 278 + - 279 + - 280 + - 281 + - 282 + - 283 + - uid: 100 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-5.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 284 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 285 + - 286 + - 287 + - 288 + - 289 + - 290 + - uid: 101 + components: + - type: Transform + pos: -3.5,1.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 291 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 292 + - 293 + - 294 + - 295 + - 296 + - 297 + - uid: 102 + components: + - type: Transform + pos: 2.5,1.5 + parent: 325 + - type: ContainerContainer + containers: + machine_board: !type:Container + showEnts: False + occludes: True + ents: + - 298 + machine_parts: !type:Container + showEnts: False + occludes: True + ents: + - 299 + - 300 + - 301 + - 302 + - 303 + - 304 +- proto: ThrusterMachineCircuitboard + entities: + - uid: 249 + components: + - type: Transform + parent: 95 + - type: Physics + canCollide: False + - uid: 256 + components: + - type: Transform + parent: 96 + - type: Physics + canCollide: False + - uid: 263 + components: + - type: Transform + parent: 97 + - type: Physics + canCollide: False + - uid: 270 + components: + - type: Transform + parent: 98 + - type: Physics + canCollide: False + - uid: 277 + components: + - type: Transform + parent: 99 + - type: Physics + canCollide: False + - uid: 284 + components: + - type: Transform + parent: 100 + - type: Physics + canCollide: False + - uid: 291 + components: + - type: Transform + parent: 101 + - type: Physics + canCollide: False + - uid: 298 + components: + - type: Transform + parent: 102 + - type: Physics + canCollide: False +- proto: ToolboxSyndicateFilled + entities: + - uid: 177 + components: + - type: Transform + pos: 1.5699697,-0.44908836 + parent: 325 + - type: Physics + canCollide: False +- proto: ToyFigurineNukie + entities: + - uid: 10 + components: + - type: Transform + pos: -2.3371089,-2.140279 + parent: 325 +- proto: VendingMachineSyndieDrobe + entities: + - uid: 163 + components: + - type: Transform + pos: -2.5,-0.5 + parent: 325 +- proto: WallPlastitanium + entities: + - uid: 7 + components: + - type: Transform + pos: -2.5,0.5 + parent: 325 + - uid: 8 + components: + - type: Transform + pos: -3.5,0.5 + parent: 325 + - uid: 9 + components: + - type: Transform + pos: -3.5,-0.5 + parent: 325 + - uid: 11 + components: + - type: Transform + pos: 1.5,0.5 + parent: 325 + - uid: 12 + components: + - type: Transform + pos: 2.5,0.5 + parent: 325 + - uid: 13 + components: + - type: Transform + pos: -4.5,-0.5 + parent: 325 + - uid: 22 + components: + - type: Transform + pos: 3.5,-0.5 + parent: 325 + - uid: 25 + components: + - type: Transform + pos: 3.5,-2.5 + parent: 325 + - uid: 27 + components: + - type: Transform + pos: -3.5,-2.5 + parent: 325 + - uid: 28 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-6.5 + parent: 325 + - uid: 29 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-6.5 + parent: 325 + - uid: 30 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-6.5 + parent: 325 + - uid: 31 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-8.5 + parent: 325 + - uid: 32 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-8.5 + parent: 325 + - uid: 33 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-6.5 + parent: 325 + - uid: 34 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-3.5 + parent: 325 + - uid: 35 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-9.5 + parent: 325 + - uid: 36 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-8.5 + parent: 325 + - uid: 37 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,-9.5 + parent: 325 + - uid: 38 + components: + - type: Transform + pos: -4.5,-2.5 + parent: 325 + - uid: 39 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-3.5 + parent: 325 + - uid: 44 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 2.5,-8.5 + parent: 325 + - uid: 48 + components: + - type: Transform + pos: 2.5,-7.5 + parent: 325 + - uid: 49 + components: + - type: Transform + pos: -3.5,-7.5 + parent: 325 + - uid: 54 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,-3.5 + parent: 325 + - uid: 55 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,-3.5 + parent: 325 + - uid: 60 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-6.5 + parent: 325 + - uid: 61 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-6.5 + parent: 325 + - uid: 62 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -1.5,-3.5 + parent: 325 + - uid: 63 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 0.5,-3.5 + parent: 325 + - uid: 66 + components: + - type: Transform + pos: 0.5,-9.5 + parent: 325 + - uid: 69 + components: + - type: Transform + pos: -2.5,1.5 + parent: 325 + - uid: 73 + components: + - type: Transform + pos: 1.5,1.5 + parent: 325 + - uid: 80 + components: + - type: Transform + pos: 2.5,-2.5 + parent: 325 + - uid: 81 + components: + - type: Transform + pos: 2.5,-0.5 + parent: 325 + - uid: 92 + components: + - type: Transform + pos: -1.5,-9.5 + parent: 325 + - uid: 108 + components: + - type: Transform + pos: -0.5,-9.5 + parent: 325 +- proto: WallPlastitaniumDiagonal + entities: + - uid: 23 + components: + - type: Transform + pos: -4.5,0.5 + parent: 325 + - uid: 43 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 3.5,0.5 + parent: 325 + - uid: 68 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 1.5,2.5 + parent: 325 + - uid: 79 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,-3.5 + parent: 325 + - uid: 82 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 3.5,-3.5 + parent: 325 + - uid: 86 + components: + - type: Transform + pos: -2.5,2.5 + parent: 325 +- proto: WindoorSecure + entities: + - uid: 166 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,-1.5 + parent: 325 + - uid: 206 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -0.5,-0.5 + parent: 325 + - type: ContainerContainer + containers: + board: !type:Container + showEnts: False + occludes: True + ents: + - 346 +- proto: YellowOxygenTankFilled + entities: + - uid: 167 + components: + - type: Transform + pos: 1.60798,-0.3062118 + parent: 325 +... diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index d699229b02..00b799670f 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -4,10 +4,8 @@ # If item is not in list it will go at the bottom (ties broken by alert type enum value) id: BaseAlertOrder order: - - alertType: ChangelingBiomass # goobstation - changelings - category: Health - category: Stamina - - alertType: ChangelingChemicals # goobstation - changelings - alertType: SuitPower - category: Internals - alertType: Fire diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml index 474dd8200a..0bcd71fbad 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml @@ -5,7 +5,6 @@ id: BaseMobVulpkanin abstract: true components: - - type: Absorbable # Goobstation - changelings - type: HumanoidAppearance species: Vulpkanin - type: Hunger diff --git a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml index f39c6a0f69..712dfcf3a0 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml @@ -90,7 +90,8 @@ - !type:DepartmentTimeRequirement # DeltaV - Security dept time requirement department: Security time: 36000 # DeltaV - 10 hours - - type: GhostRoleAntagSpawner + - type: GhostRoleMobSpawner + prototype: MobHumanLoneNuclearOperative - type: Sprite sprite: Markers/jobs.rsi layers: @@ -98,33 +99,6 @@ - sprite: Structures/Wallmounts/signs.rsi state: radiation -- type: entity - noSpawn: true - parent: SpawnPointLoneNukeOperative - id: SpawnPointNukeopsCommander - components: - - type: GhostRole - name: roles-antag-nuclear-operative-commander-name - description: roles-antag-nuclear-operative-commander-objective - -- type: entity - noSpawn: true - parent: SpawnPointLoneNukeOperative - id: SpawnPointNukeopsMedic - components: - - type: GhostRole - name: roles-antag-nuclear-operative-agent-name - description: roles-antag-nuclear-operative-agent-objective - -- type: entity - noSpawn: true - parent: SpawnPointLoneNukeOperative - id: SpawnPointNukeopsOperative - components: - - type: GhostRole - name: roles-antag-nuclear-operative-name - description: roles-antag-nuclear-operative-objective - - type: entity parent: MarkerBase id: SpawnPointGhostDragon diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml index 38da683652..c135ac2b82 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml @@ -5,7 +5,6 @@ id: BaseMobArachnid abstract: true components: - - type: Absorbable # Goobstation - changelings - type: Body prototype: Arachnid requiredLegs: 2 # It would be funny if arachnids could use their little back limbs to move around once they lose their legs, but just something to consider post-woundmed diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index a807119df0..9fb241c40d 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -195,8 +195,6 @@ type: HumanoidMarkingModifierBoundUserInterface - key: enum.StrippingUiKey.Key type: StrippableBoundUserInterface - - key: enum.StoreUiKey.Key - type: StoreBoundUserInterface - type: Puller - type: Speech speechSounds: Alto @@ -376,7 +374,6 @@ - type: Body prototype: Human requiredLegs: 2 - - type: Absorbable # Goobstation - changelings - type: UserInterface interfaces: - key: enum.HumanoidMarkingModifierKey.Key # sure, this can go here too diff --git a/Resources/Prototypes/Entities/Mobs/Species/diona.yml b/Resources/Prototypes/Entities/Mobs/Species/diona.yml index 7a8dc8160c..42383d9a42 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/diona.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/diona.yml @@ -5,7 +5,6 @@ id: BaseMobDiona abstract: true components: - - type: Absorbable # Goobstation - changelings - type: HumanoidAppearance species: Diona - type: Hunger diff --git a/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml b/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml index 045eca86bc..055c6522dd 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml @@ -5,7 +5,6 @@ id: BaseMobDwarf abstract: true components: - - type: Absorbable # Goobstation - changelings - type: Hunger - type: Thirst - type: Carriable # Carrying system from nyanotrasen. diff --git a/Resources/Prototypes/Entities/Mobs/Species/gingerbread.yml b/Resources/Prototypes/Entities/Mobs/Species/gingerbread.yml index 0660ea53d0..c514a6f1a0 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/gingerbread.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/gingerbread.yml @@ -5,7 +5,6 @@ id: BaseMobGingerbread abstract: true components: - - type: Absorbable # Goobstation - changelings - type: HumanoidAppearance species: Gingerbread - type: Icon diff --git a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml index 5295e8c4fe..60ccc31d79 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml @@ -31,7 +31,6 @@ type: HumanoidMarkingModifierBoundUserInterface - key: enum.StrippingUiKey.Key type: StrippableBoundUserInterface - - type: Absorbable - type: Sprite scale: 0.9, 0.9 layers: diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 6b187c52d3..ac373725ce 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -5,7 +5,6 @@ name: Urist McHands abstract: true components: - - type: Absorbable # Goobstation - changelings - type: Hunger - type: Icon # It will not have an icon in the adminspawn menu without this. Body parts seem fine for whatever reason. sprite: Mobs/Species/Human/parts.rsi diff --git a/Resources/Prototypes/Entities/Mobs/Species/moth.yml b/Resources/Prototypes/Entities/Mobs/Species/moth.yml index c99b4b3a1a..1c55dcf0df 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/moth.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/moth.yml @@ -5,7 +5,6 @@ id: BaseMobMoth abstract: true components: - - type: Absorbable # Goobstation - changelings - type: HumanoidAppearance species: Moth - type: Hunger diff --git a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml index ff91d4c788..35f9e9fa39 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml @@ -5,7 +5,6 @@ id: BaseMobReptilian abstract: true components: - - type: Absorbable # Goobstation - changelings - type: HumanoidAppearance species: Reptilian - type: Hunger diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index af575159cd..0e05b0c827 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -4,7 +4,6 @@ id: BaseMobSlimePerson abstract: true components: - - type: Absorbable # Goobstation - changelings - type: Hunger - type: Thirst - type: Carriable # Carrying system from nyanotrasen. diff --git a/Resources/Prototypes/Entities/Mobs/Species/vox.yml b/Resources/Prototypes/Entities/Mobs/Species/vox.yml index 71f484c132..a271e9d084 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/vox.yml @@ -3,7 +3,6 @@ id: BaseMobVox abstract: true components: - - type: Absorbable # Goobstation - changelings - type: Hunger - type: Thirst - type: Icon diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 284ed00652..ca88511744 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -441,28 +441,11 @@ weight: 2 duration: 1 - type: ZombieRule - - type: AntagSelection - definitions: - - prefRoles: [ InitialInfected ] - max: 3 - playerRatio: 10 - blacklist: - components: - - ZombieImmune - - InitialInfectedExempt - briefing: - text: zombie-patientzero-role-greeting - color: Plum - sound: "/Audio/Ambience/Antag/zombie_start.ogg" - components: - - type: PendingZombie #less time to prepare than normal - minInitialInfectedGrace: 300 - maxInitialInfectedGrace: 450 - - type: ZombifyOnDeath - - type: IncurableZombie - mindComponents: - - type: InitialInfectedRole - prototype: InitialInfected + minStartDelay: 0 #let them know immediately + maxStartDelay: 10 + maxInitialInfected: 2 + minInitialInfectedGrace: 300 + maxInitialInfectedGrace: 450 - type: entity id: LoneOpsSpawn @@ -475,29 +458,7 @@ minimumPlayers: 15 reoccurrenceDelay: 45 duration: 1 - - type: LoadMapRule - mapPath: /Maps/Shuttles/striker.yml - - type: NukeopsRule - roundEndBehavior: Nothing - - type: AntagSelection - definitions: - - spawnerPrototype: SpawnPointLoneNukeOperative - min: 1 - max: 1 - pickPlayer: false - startingGear: SyndicateLoneOperativeGearFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - SyndicateNamesPrefix - - SyndicateNamesNormal - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: Nukeops + - type: LoneOpsSpawnRule - type: entity id: MassHallucinations diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index bb870f6007..37fc4b44cd 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -34,23 +34,6 @@ id: Thief components: - type: ThiefRule - - type: AntagSelection - definitions: - - prefRoles: [ Thief ] - maxRange: - min: 1 - max: 3 - playerRatio: 1 - allowNonHumans: true - multiAntagSetting: All - startingGear: ThiefGear - components: - - type: Pacified - mindComponents: - - type: ThiefRole - prototype: Thief - briefing: - sound: "/Audio/Misc/thief_greeting.ogg" - type: entity noSpawn: true diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index f3d0704ca5..0af55a7f9d 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -70,116 +70,31 @@ components: - type: GameRule minPlayers: 35 - - type: RandomMetadata #this generates the random operation name cuz it's cool. - nameSegments: - - operationPrefix - - operationSuffix - type: NukeopsRule - - type: LoadMapRule - gameMap: NukieOutpost - - type: AntagSelection - selectionTime: PrePlayerSpawn - definitions: - - prefRoles: [ NukeopsCommander ] - fallbackRoles: [ Nukeops, NukeopsMedic ] - spawnerPrototype: SpawnPointNukeopsCommander - startingGear: SyndicateCommanderGearFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-commander - - SyndicateNamesElite - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: NukeopsCommander - - prefRoles: [ NukeopsMedic ] - fallbackRoles: [ Nukeops, NukeopsCommander ] - spawnerPrototype: SpawnPointNukeopsMedic - startingGear: SyndicateOperativeMedicFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-agent - - SyndicateNamesNormal - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: NukeopsMedic - - prefRoles: [ Nukeops ] - fallbackRoles: [ NukeopsCommander, NukeopsMedic ] - spawnerPrototype: SpawnPointNukeopsOperative - max: 3 - playerRatio: 10 - startingGear: SyndicateOperativeGearFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-operator - - SyndicateNamesNormal - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: Nukeops + faction: Syndicate + +- type: entity + id: Pirates + parent: BaseGameRule + noSpawn: true + components: + - type: PiratesRule - type: entity id: Traitor parent: BaseGameRule noSpawn: true components: - - type: GameRule - minPlayers: 5 - delay: - min: 240 - max: 420 - type: TraitorRule - - type: AntagSelection - definitions: - - prefRoles: [ Traitor ] - max: 12 - playerRatio: 10 - blacklist: - components: - - AntagImmune - - Changeling # Goobstation - lateJoinAdditional: true - mindComponents: - - type: TraitorRole - prototype: Traitor - type: entity id: Revolutionary parent: BaseGameRule noSpawn: true components: - - type: GameRule - minPlayers: 15 - type: RevolutionaryRule - - type: AntagSelection - definitions: - - prefRoles: [ HeadRev ] - max: 2 - playerRatio: 20 # WD - briefing: - text: head-rev-role-greeting - color: CornflowerBlue - sound: "/Audio/Ambience/Antag/headrev_start.ogg" - startingGear: HeadRevGear - components: - - type: Revolutionary - - type: HeadRevolutionary - mindComponents: - - type: RevolutionaryRole - prototype: HeadRev + maxHeadRevs: 2 # DeltaV + playersPerHeadRev: 30 # DeltaV - need highpop readied up for multiple headrevs - type: entity id: Sandbox @@ -200,32 +115,7 @@ parent: BaseGameRule noSpawn: true components: - - type: GameRule - minPlayers: 20 - delay: - min: 600 - max: 900 - type: ZombieRule - - type: AntagSelection - definitions: - - prefRoles: [ InitialInfected ] - max: 6 - playerRatio: 10 - blacklist: - components: - - ZombieImmune - - InitialInfectedExempt - briefing: - text: zombie-patientzero-role-greeting - color: Plum - sound: "/Audio/Ambience/Antag/zombie_start.ogg" - components: - - type: PendingZombie - - type: ZombifyOnDeath - - type: IncurableZombie - mindComponents: - - type: InitialInfectedRole - prototype: InitialInfected # event schedulers - type: entity @@ -264,6 +154,7 @@ - id: BasicTrashVariationPass - id: SolidWallRustingVariationPass - id: ReinforcedWallRustingVariationPass + - id: CutWireVariationPass - id: BasicPuddleMessVariationPass prob: 0.99 orGroup: puddleMess diff --git a/Resources/Prototypes/Goobstation/Alerts/alerts.yml b/Resources/Prototypes/Goobstation/Alerts/alerts.yml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Resources/Prototypes/Goobstation/Changeling/Actions/changeling.yml b/Resources/Prototypes/Goobstation/Changeling/Actions/changeling.yml deleted file mode 100644 index 3ae08abd7d..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Actions/changeling.yml +++ /dev/null @@ -1,537 +0,0 @@ -# abilities - -# starting -- type: entity - id: ActionEvolutionMenu - name: Open evolution menu - description: Opens the evolution menu. - noSpawn: true - components: - - type: InstantAction - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: evolution_menu - event: !type:OpenEvolutionMenuEvent {} - - type: ChangelingAction - requireBiomass: false - useInLesserForm: true - -- type: entity - id: ActionAbsorbDNA - name: Absorb DNA - description: Absorb the target's DNA, husking them in the process. Costs 25 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - useDelay: 5 - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: absorb_dna - event: !type:AbsorbDNAEvent {} - - type: ChangelingAction - chemicalCost: 25 - requireBiomass: false - useInLesserForm: true - -- type: entity - id: ActionStingExtractDNA - name: Extract DNA sting - description: Steal your target's genetic information. Costs 25 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_extractdna - event: !type:StingExtractDNAEvent {} - - type: ChangelingAction - chemicalCost: 25 - useInLesserForm: true - -- type: entity - id: ActionChangelingTransformCycle - name: Cycle DNA - description: Cycle your available DNA. - noSpawn: true - components: - - type: InstantAction - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: transform_cycle - event: !type:ChangelingTransformCycleEvent {} - - type: ChangelingAction - requireBiomass: false - useInLesserForm: true - -- type: entity - id: ActionChangelingTransform - name: Transform - description: Transform into another humanoid. Doesn't come with clothes. Costs 5 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 5 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: transform - event: !type:ChangelingTransformEvent {} - - type: ChangelingAction - chemicalCost: 5 - useInLesserForm: true - -- type: entity - id: ActionEnterStasis - name: Enter regenerative stasis - description: Fake your death and start regenerating. Drains all your chemicals. Consumes biomass. - noSpawn: true - components: - - type: InstantAction - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: stasis_enter - event: !type:EnterStasisEvent {} - - type: ChangelingAction - biomassCost: 1 - useInLesserForm: true - -- type: entity - id: ActionExitStasis - name: Exit stasis - description: Rise from the dead with full health. Costs 60 chemicals. - noSpawn: true - components: - - type: InstantAction - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: stasis_exit - event: !type:ExitStasisEvent {} - - type: ChangelingAction - chemicalCost: 60 - useInLesserForm: true - -# combat -- type: entity - id: ActionToggleArmblade - name: Toggle Arm Blade - description: Reform one of your arms into a strong blade, composed of bone and flesh. Retract on secondary use. Costs 15 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 2 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: armblade - event: !type:ToggleArmbladeEvent {} - - type: ChangelingAction - chemicalCost: 15 - -- type: entity - id: ActionCreateBoneShard - name: Form Bone Shard - description: Break off shards of your bone and shape them into a throwing star. Costs 15 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 1 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: bone_shard - event: !type:CreateBoneShardEvent {} - - type: ChangelingAction - chemicalCost: 15 - -- type: entity - id: ActionToggleChitinousArmor - name: Toggle Armor - description: Inflate your body into an all-consuming chitinous mass of armor. Costs 25 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 2 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: chitinous_armor - event: !type:ToggleChitinousArmorEvent {} - - type: ChangelingAction - chemicalCost: 25 - requireAbsorbed: 2 - -- type: entity - id: ActionToggleOrganicShield - name: Form Shield - description: Reform one of your arms into a large, fleshy shield. Costs 20 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 2 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: organic_shield - event: !type:ToggleOrganicShieldEvent {} - - type: ChangelingAction - chemicalCost: 20 - requireAbsorbed: 1 - -- type: entity - id: ActionShriekDissonant - name: Dissonant Shriek - description: Emit an EMP blast, with just your voice. Costs 30 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 10 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: shriek_dissonant - event: !type:ShriekDissonantEvent {} - - type: ChangelingAction - chemicalCost: 30 - useInLesserForm: true - requireAbsorbed: 1 - -- type: entity - id: ActionShriekResonant - name: Resonant Shriek - description: Disorient people and break lights, with just your voice. Costs 30 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 10 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: shriek_resonant - event: !type:ShriekResonantEvent {} - - type: ChangelingAction - chemicalCost: 30 - useInLesserForm: true - requireAbsorbed: 1 - -- type: entity - id: ActionToggleStrainedMuscles - name: Strain Muscles - description: Move at extremely fast speeds. Deals stamina damage. - noSpawn: true - components: - - type: InstantAction - useDelay: 1 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: strained_muscles - event: !type:ToggleStrainedMusclesEvent {} - - type: ChangelingAction - chemicalCost: 0 - useInLesserForm: true - -# stings -- type: entity - id: ActionStingBlind - name: Blind Sting - description: Silently sting your target, blinding them for a short time and rendering them near sighted. Costs 35 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_blind - event: !type:StingBlindEvent {} - - type: ChangelingAction - chemicalCost: 35 - useInLesserForm: true - -- type: entity - id: ActionStingCryo - name: Cryogenic Sting - description: Silently sting your target, constantly slowing and freezing them. Costs 35 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_cryo - event: !type:StingCryoEvent {} - - type: ChangelingAction - chemicalCost: 35 - useInLesserForm: true - -- type: entity - id: ActionStingLethargic - name: Lethargic Sting - description: Silently inject a cocktail of anesthetics into the target. Costs 35 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_lethargic - event: !type:StingLethargicEvent {} - - type: ChangelingAction - chemicalCost: 35 - useInLesserForm: true - -- type: entity - id: ActionStingMute - name: Mute Sting - description: Silently sting your target, completely silencing them for a short time. Costs 35 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_mute - event: !type:StingMuteEvent {} - - type: ChangelingAction - chemicalCost: 35 - useInLesserForm: true - -- type: entity - id: ActionStingFakeArmblade - name: Fake Armblade Sting - description: Silently sting your target, making them grow a dull armblade for a short time. Costs 50 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_armblade - event: !type:StingFakeArmbladeEvent {} - - type: ChangelingAction - chemicalCost: 50 - useInLesserForm: true - -- type: entity - id: ActionStingTransform - name: Transformation Sting - description: Silently sting your target, transforming them into a person of your choosing. Costs 75 chemicals. - noSpawn: true - components: - - type: EntityTargetAction - whitelist: - components: - - Body - canTargetSelf: false - interactOnMiss: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: sting_transform - event: !type:StingTransformEvent {} - - type: ChangelingAction - chemicalCost: 75 - useInLesserForm: true - -# utility -- type: entity - id: ActionAnatomicPanacea - name: Anatomic Panacea - description: Cure yourself of diseases, disabilities, radiation, toxins, drunkedness and brain damage. Costs 30 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 30 - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: anatomic_panacea - event: !type:ActionAnatomicPanaceaEvent {} - - type: ChangelingAction - chemicalCost: 30 - useInLesserForm: true - -- type: entity - id: ActionAugmentedEyesight - name: Augmented Eyesight - description: Toggle flash protection. - noSpawn: true - components: - - type: InstantAction - checkCanInteract: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: augmented_eyesight - event: !type:ActionAugmentedEyesightEvent {} - - type: ChangelingAction - chemicalCost: 0 - -- type: entity - id: ActionBiodegrade - name: Biodegrade - description: Vomit a caustic substance onto any restraints, or someone's face. Costs 30 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 5 - checkCanInteract: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: biodegrade - event: !type:ActionBiodegradeEvent {} - - type: ChangelingAction - chemicalCost: 30 - -- type: entity - id: ActionChameleonSkin - name: Chameleon Skin - description: Slowly blend in with the environment. Costs 25 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 1 - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: chameleon_skin - event: !type:ActionChameleonSkinEvent {} - - type: ChangelingAction - chemicalCost: 20 - -- type: entity - id: ActionEphedrineOverdose - name: Ephedrine Overdose - description: Inject some stimulants into yourself. Costs 30 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 10 - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: epinephrine_overdose - event: !type:ActionEphedrineOverdoseEvent {} - - type: ChangelingAction - chemicalCost: 30 - useInLesserForm: true - -- type: entity - id: ActionFleshmend - name: Fleshmend - description: Rapidly heal yourself. Costs 35 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 30 - checkCanInteract: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: fleshmend - event: !type:ActionFleshmendEvent {} - - type: ChangelingAction - chemicalCost: 35 - useInLesserForm: true - -- type: entity - id: ActionToggleLesserForm - name: Lesser Form - description: Abandon your current form and transform into a monkey. Costs 20 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 5 - checkCanInteract: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: lesser_form - event: !type:ActionLesserFormEvent {} - - type: ChangelingAction - chemicalCost: 20 - -- type: entity - id: ActionToggleSpacesuit - name: Toggle Space Suit - description: Make your body space proof. Costs 20 chemicals. - noSpawn: true - components: - - type: InstantAction - useDelay: 2 - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: space_adaptation - event: !type:ActionSpacesuitEvent {} - - type: ChangelingAction - chemicalCost: 20 - -- type: entity - id: ActionHivemindAccess - name: Hivemind Access - description: Tune your chemical receptors for hivemind communication. - noSpawn: true - components: - - type: InstantAction - checkCanInteract: false - checkConsciousness: false - itemIconStyle: NoItem - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: hivemind_access - event: !type:ActionHivemindAccessEvent {} - - type: ChangelingAction - chemicalCost: 0 - useInLesserForm: true diff --git a/Resources/Prototypes/Goobstation/Changeling/Alerts/changeling.yml b/Resources/Prototypes/Goobstation/Changeling/Alerts/changeling.yml deleted file mode 100644 index 6d14d49c06..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Alerts/changeling.yml +++ /dev/null @@ -1,35 +0,0 @@ -- type: alert - id: ChangelingChemicals - icons: - - sprite: /Textures/Goobstation/Changeling/changeling_chemicals.rsi - state: 0 - alertViewEntity: AlertChangelingChemicalsSpriteView - name: alerts-changeling-chemicals-name - description: alerts-changeling-chemicals-desc - -- type: alert - id: ChangelingBiomass - icons: - - sprite: /Textures/Goobstation/Changeling/changeling_biomass.rsi - state: 0 - alertViewEntity: AlertChangelingBiomassSpriteView - name: alerts-changeling-biomass-name - description: alerts-changeling-biomass-desc - -- type: entity - id: AlertChangelingChemicalsSpriteView - categories: [ HideSpawnMenu ] - components: - - type: Sprite - sprite: /Textures/Goobstation/Changeling/changeling_chemicals.rsi - layers: - - map: [ "enum.AlertVisualLayers.Base" ] - -- type: entity - id: AlertChangelingBiomassSpriteView - categories: [ HideSpawnMenu ] - components: - - type: Sprite - sprite: /Textures/Goobstation/Changeling/changeling_biomass.rsi - layers: - - map: [ "enum.AlertVisualLayers.Base" ] diff --git a/Resources/Prototypes/Goobstation/Changeling/Catalog/changeling_catalog.yml b/Resources/Prototypes/Goobstation/Changeling/Catalog/changeling_catalog.yml deleted file mode 100644 index 00eba5e962..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Catalog/changeling_catalog.yml +++ /dev/null @@ -1,341 +0,0 @@ -# combat - -- type: listing - id: EvolutionMenuCombatArmblade - name: evolutionmenu-combat-armblade-name - description: evolutionmenu-combat-armblade-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: armblade } - productAction: ActionToggleArmblade - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatBoneShard - name: evolutionmenu-combat-boneshard-name - description: evolutionmenu-combat-boneshard-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: bone_shard } - productAction: ActionCreateBoneShard - cost: - EvolutionPoint: 3 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatChitinousArmor - name: evolutionmenu-combat-armor-name - description: evolutionmenu-combat-armor-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: chitinous_armor } - productAction: ActionToggleChitinousArmor - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatOrganicShield - name: evolutionmenu-combat-shield-name - description: evolutionmenu-combat-shield-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: organic_shield } - productAction: ActionToggleOrganicShield - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatShriekDissonant - name: evolutionmenu-combat-shriek-dissonant-name - description: evolutionmenu-combat-shriek-dissonant-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: shriek_dissonant } - productAction: ActionShriekDissonant - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatShriekResonant - name: evolutionmenu-combat-shriek-resonant-name - description: evolutionmenu-combat-shriek-resonant-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: shriek_resonant } - productAction: ActionShriekResonant - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuCombatMuscles - name: evolutionmenu-combat-strainedmuscles-name - description: evolutionmenu-combat-strainedmuscles-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: strained_muscles } - productAction: ActionToggleStrainedMuscles - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityCombat - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -#- type: listing -# id: EvolutionMenuCombatSpiders -# name: evolutionmenu-combat-spiders-name -# description: evolutionmenu-combat-spiders-desc -# icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: spiders_spawn } -# productAction: ActionSpawnChangelingSpider -# cost: -# EvolutionPoint: 6 -# categories: -# - ChangelingAbilityCombat -# consitions: -# - !type:ListingLimitedStockCondition -# stock: 1 - -# sting - -- type: listing - id: EvolutionMenuStingBlind - name: evolutionmenu-sting-blind-name - description: evolutionmenu-sting-blind-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_blind } - productAction: ActionStingBlind - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuStingCryo - name: evolutionmenu-sting-cryo-name - description: evolutionmenu-sting-cryo-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_cryo } - productAction: ActionStingCryo - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuStingLethargic - name: evolutionmenu-sting-lethargic-name - description: evolutionmenu-sting-lethargic-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_lethargic } - productAction: ActionStingLethargic - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuStingMute - name: evolutionmenu-sting-mute-name - description: evolutionmenu-sting-mute-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_mute } - productAction: ActionStingMute - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuStingFakeArmblade - name: evolutionmenu-sting-armblade-name - description: evolutionmenu-sting-armblade-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_armblade } - productAction: ActionStingFakeArmblade - cost: - EvolutionPoint: 5 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuStingTransform - name: evolutionmenu-sting-transform-name - description: evolutionmenu-sting-transform-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: sting_transform } - productAction: ActionStingTransform - cost: - EvolutionPoint: 6 - categories: - - ChangelingAbilitySting - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -# utility - -- type: listing - id: EvolutionMenuUtilityPanacea - name: evolutionmenu-utility-panacea-name - description: evolutionmenu-utility-panacea-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: anatomic_panacea } - productAction: ActionAnatomicPanacea - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityEyesight - name: evolutionmenu-utility-eyesight-name - description: evolutionmenu-utility-eyesight-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: augmented_eyesight } - productAction: ActionAugmentedEyesight - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityBiodegrade - name: evolutionmenu-utility-biodegrade-name - description: evolutionmenu-utility-biodegrade-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: biodegrade } - productAction: ActionBiodegrade - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityChameleon - name: evolutionmenu-utility-chameleon-name - description: evolutionmenu-utility-chameleon-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: chameleon_skin } - productAction: ActionChameleonSkin - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityOverdose - name: evolutionmenu-utility-stims-name - description: evolutionmenu-utility-stims-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: epinephrine_overdose } - productAction: ActionEphedrineOverdose - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityFleshmend - name: evolutionmenu-utility-fleshmend-name - description: evolutionmenu-utility-fleshmend-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: fleshmend } - productAction: ActionFleshmend - cost: - EvolutionPoint: 5 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -#- type: listing -# id: EvolutionMenuUtilityLastResort -# name: evolutionmenu-utility-lastresort-name -# description: evolutionmenu-utility-lastresort-desc -# icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: last_resort } -# productAction: ActionLastResort -# cost: -# EvolutionPoint: 2 -# categories: -# - ChangelingAbilityUtility -# conditions: -# - !type:ListingLimitedStockCondition -# stock: 1 - -- type: listing - id: EvolutionMenuUtilityLesserForm - name: evolutionmenu-utility-lesserform-name - description: evolutionmenu-utility-lesserform-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: lesser_form } - productAction: ActionToggleLesserForm - cost: - EvolutionPoint: 2 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilitySpacesuit - name: evolutionmenu-utility-spacesuit-name - description: evolutionmenu-utility-spacesuit-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: space_adaptation } - productAction: ActionToggleSpacesuit - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 - -- type: listing - id: EvolutionMenuUtilityHivemindAccess - name: evolutionmenu-utility-hivemindaccess-name - description: evolutionmenu-utility-hivemindaccess-desc - icon: { sprite: Goobstation/Changeling/changeling_abilities.rsi, state: hivemind_access } - productAction: ActionHivemindAccess - cost: - EvolutionPoint: 4 - categories: - - ChangelingAbilityUtility - conditions: - - !type:ListingLimitedStockCondition - stock: 1 diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/hardsuit-helmets.yml deleted file mode 100644 index de00f10e38..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/hardsuit-helmets.yml +++ /dev/null @@ -1,16 +0,0 @@ -- type: entity - parent: ClothingHeadHardsuitBase - id: ChangelingClothingHeadHelmetHardsuit - name: organic space helmet - description: A spaceworthy biomass of pressure and temperature resistant tissue. - suffix: Unremoveable - components: - - type: Item - - type: Sprite - sprite: Goobstation/Changeling/ling_spacesuit_helmet.rsi - - type: Clothing - sprite: Goobstation/Changeling/ling_spacesuit_helmet.rsi - - type: PressureProtection - highPressureMultiplier: 0.225 - lowPressureMultiplier: 1000 - - type: Unremoveable \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/helmets.yml deleted file mode 100644 index 11d3e23b9f..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/Head/helmets.yml +++ /dev/null @@ -1,19 +0,0 @@ -- type: entity - parent: ClothingHeadBase - id: ChangelingClothingHeadHelmet - name: chitinous helmet - description: An all-consuming chitinous mass of armor. - suffix: Unremoveable - components: - - type: Sprite - sprite: Goobstation/Changeling/ling_armor_helmet.rsi - - type: Clothing - sprite: Goobstation/Changeling/ling_armor_helmet.rsi - - type: Armor - modifiers: - coefficients: - Blunt: 0.9 - Slash: 0.9 - Piercing: 0.9 - Heat: 0.9 - - type: Unremoveable \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/armor.yml deleted file mode 100644 index 9627c226e1..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/armor.yml +++ /dev/null @@ -1,28 +0,0 @@ -- type: entity - parent: ClothingOuterBaseLarge - id: ChangelingClothingOuterArmor - name: chitinous armor - description: An all-consuming chitinous mass of armor. - suffix: Unremoveable - components: - - type: Sprite - sprite: Goobstation/Changeling/ling_armor.rsi - - type: Clothing - sprite: Goobstation/Changeling/ling_armor.rsi - - type: Armor - modifiers: - coefficients: - Blunt: 0.5 - Slash: 0.5 - Piercing: 0.5 - Heat: 0.5 - Radiation: 0.5 - Caustic: 0.5 - - type: ClothingSpeedModifier - walkModifier: 0.7 - sprintModifier: 0.65 - - type: HeldSpeedModifier - - type: ExplosionResistance - damageCoefficient: 0.5 - - type: GroupExamine - - type: Unremoveable \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/hardsuits.yml deleted file mode 100644 index dda0aef11c..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Clothing/OuterClothing/hardsuits.yml +++ /dev/null @@ -1,30 +0,0 @@ -- type: entity - parent: ClothingOuterBaseLarge - id: ChangelingClothingOuterHardsuit - name: organic space suit - description: A spaceworthy biomass of pressure and temperature resistant tissue. - suffix: Unremoveable - components: - - type: Sprite - sprite: Goobstation/Changeling/ling_spacesuit.rsi - - type: Clothing - sprite: Goobstation/Changeling/ling_spacesuit.rsi - - type: PressureProtection - highPressureMultiplier: 0.225 - lowPressureMultiplier: 1000 - - type: TemperatureProtection - coefficient: 0.01 - - type: ExplosionResistance - damageCoefficient: 0.2 - - type: Armor - modifiers: - coefficients: - Blunt: 0.95 - Slash: 0.95 - Piercing: 1 - Heat: 0.5 - - type: ClothingSpeedModifier - walkModifier: 0.9 - sprintModifier: 0.9 - - type: HeldSpeedModifier - - type: Unremoveable \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Guidebook/changeling.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Guidebook/changeling.yml deleted file mode 100644 index b1c64f0bcb..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Guidebook/changeling.yml +++ /dev/null @@ -1,9 +0,0 @@ -- type: entity - id: GuidebookChangelingFluff - name: guidebook changeling - description: you shouldn't be seeing this normally. - noSpawn: true - components: - - type: Sprite - sprite: Goobstation/Changeling/Guidebook/guidebook_changeling.rsi - state: icon \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Shields/shields.yml deleted file mode 100644 index b6f409f432..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Shields/shields.yml +++ /dev/null @@ -1,31 +0,0 @@ -- type: entity - parent: BaseShield - id: ChangelingShield - name: oraganic shield - description: A large, fleshy shield. - suffix: Unremoveable - components: - - type: Unremoveable - - type: Sprite - sprite: Goobstation/Changeling/shields.rsi - state: ling-icon - - type: Item - sprite: Goobstation/Changeling/shields.rsi - heldPrefix: ling - - type: Destructible - thresholds: - - trigger: - !type:DamageTrigger - damage: 60 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - trigger: - !type:DamageTrigger - damage: 50 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] - - !type:PlaySoundBehavior - sound: - collection: MetalBreak \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Melee/changeling_armblade.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Melee/changeling_armblade.yml deleted file mode 100644 index 6caa150e3f..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Melee/changeling_armblade.yml +++ /dev/null @@ -1,48 +0,0 @@ -- type: entity - parent: ArmBlade - id: ArmBladeChangeling - suffix: Unremoveable - components: - - type: Sharp - - type: Sprite - sprite: Goobstation/Changeling/arm_blade.rsi - state: icon - - type: MeleeWeapon - wideAnimationRotation: 90 - attackRate: 0.75 - damage: - types: - Blunt: 5 - Slash: 12.5 - Piercing: 10 - Structural: 10 - soundHit: - path: /Audio/Weapons/bladeslice.ogg - - type: Item - size: Ginormous - sprite: Goobstation/Changeling/arm_blade.rsi - - type: Prying - pryPowered: true - - type: Unremoveable - - type: Tool - qualities: - - Slicing - - Prying - - type: DisarmMalus - malus: 0 - -- type: entity - parent: ArmBladeChangeling - id: FakeArmBladeChangeling - components: - - type: MeleeWeapon - wideAnimationRotation: 90 - attackRate: 0.75 - damage: - types: - Blunt: 1 - Slash: 1 - Piercing: 1 - Structural: 1 - - type: TimedDespawn - lifetime: 60 \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Throwable/throwing_stars.yml b/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Throwable/throwing_stars.yml deleted file mode 100644 index 20d076903d..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Entities/Objects/Weapons/Throwable/throwing_stars.yml +++ /dev/null @@ -1,12 +0,0 @@ -- type: entity - parent: ThrowingStar - id: ThrowingStarChangeling - name: bone shard - components: - - type: Sprite - sprite: Goobstation/Changeling/bone_shard.rsi - layers: - - state: icon - map: ["base"] - - type: TimedDespawn - lifetime: 30 \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Guidebook/antagonist.yml b/Resources/Prototypes/Goobstation/Changeling/Guidebook/antagonist.yml deleted file mode 100644 index e437e355fb..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Guidebook/antagonist.yml +++ /dev/null @@ -1,4 +0,0 @@ -- type: guideEntry - id: Changelings - name: guide-entry-changelings - text: "/ServerInfo/Goobstation/Changeling/Guidebook/Antagonist/Changelings.xml" \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Objectives/changeling.yml b/Resources/Prototypes/Goobstation/Changeling/Objectives/changeling.yml deleted file mode 100644 index fbbc93c84b..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Objectives/changeling.yml +++ /dev/null @@ -1,67 +0,0 @@ -- type: entity - abstract: true - parent: BaseObjective - id: BaseChangelingObjective - components: - - type: Objective - difficulty: 1.5 # unused but necessary i guess - issuer: hivemind - - type: RoleRequirement - roles: - components: - - ChangelingRole - -- type: entity - parent: [BaseChangelingObjective, BaseSurviveObjective] - id: ChangelingSurviveObjective - name: Survive - description: I must survive no matter what. - components: - - type: Objective - icon: - sprite: Goobstation/Changeling/changeling_abilities.rsi - state: stasis_exit - -- type: entity - parent: BaseChangelingObjective - id: ChangelingAbsorbObjective - components: - - type: Objective - icon: - sprite: Mobs/Demons/abomination.rsi - state: dead - - type: NumberObjective - min: 2 - max: 4 - title: objective-condition-absorb-title - description: objective-condition-absorb-description - - type: AbsorbCondition - -- type: entity - parent: BaseChangelingObjective - id: ChangelingStealDNAObjective - components: - - type: Objective - icon: - sprite: Mobs/Species/Human/organs.rsi - state: brain - - type: NumberObjective - min: 6 - max: 9 - title: objective-condition-stealdna-title - description: objective-condition-stealdna-description - - type: StealDNACondition - -- type: entity - parent: BaseChangelingObjective - id: EscapeIdentityObjective - description: I need to escape on the evacuation shuttle. Undercover. - components: - - type: Objective - icon: - sprite: Objects/Magic/magicactions.rsi - state: blink - - type: ImpersonateCondition - - type: TargetObjective - title: objective-condition-escape-identity-title - - type: PickRandomPerson \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/Reagents/biological.yml b/Resources/Prototypes/Goobstation/Changeling/Reagents/biological.yml deleted file mode 100644 index fa8bbb9644..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Reagents/biological.yml +++ /dev/null @@ -1,27 +0,0 @@ -- type: reagent - parent: Blood - id: BloodChangeling - -- type: reaction - id: ChangelingBloodBreakdown - source: true - requiredMixerCategories: - - Centrifuge - reactants: - BloodChangeling: - amount: 5 - products: - Water: 11 - Iron: 0.5 - Sugar: 2 - CarbonDioxide: 3 - Protein: 4 - effects: - - !type:CreateEntityReactionEffect - entity: FleshKudzu - - !type:ExplosionReactionEffect - explosionType: Default - maxIntensity: 2 - intensityPerUnit: 0.5 - intensitySlope: 4 - maxTotalIntensity: 1 diff --git a/Resources/Prototypes/Goobstation/Changeling/Roles/Antags/changeling.yml b/Resources/Prototypes/Goobstation/Changeling/Roles/Antags/changeling.yml deleted file mode 100644 index 0bfe8dc560..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Roles/Antags/changeling.yml +++ /dev/null @@ -1,7 +0,0 @@ -- type: antag - id: Changeling - name: roles-antag-changeling-name - antagonist: true - setPreference: true - objective: roles-antag-changeling-description - # guides: [ Changelings ] #Temporarily commented \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/StatusIcon/antag.yml b/Resources/Prototypes/Goobstation/Changeling/StatusIcon/antag.yml deleted file mode 100644 index db243ce62d..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/StatusIcon/antag.yml +++ /dev/null @@ -1,6 +0,0 @@ -- type: statusIcon - id: HivemindFaction - priority: 11 - icon: - sprite: /Textures/Interface/Misc/job_icons.rsi - state: Changeling diff --git a/Resources/Prototypes/Goobstation/Changeling/Store/categories.yml b/Resources/Prototypes/Goobstation/Changeling/Store/categories.yml deleted file mode 100644 index 8444dea1bf..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Store/categories.yml +++ /dev/null @@ -1,16 +0,0 @@ -# changeling - -- type: storeCategory - id: ChangelingAbilityCombat - name: store-ling-category-combat - priority: 0 - -- type: storeCategory - id: ChangelingAbilitySting - name: store-ling-category-sting - priority: 1 - -- type: storeCategory - id: ChangelingAbilityUtility - name: store-ling-category-utility - priority: 2 diff --git a/Resources/Prototypes/Goobstation/Changeling/Store/currency.yml b/Resources/Prototypes/Goobstation/Changeling/Store/currency.yml deleted file mode 100644 index 3ad315d05d..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/Store/currency.yml +++ /dev/null @@ -1,4 +0,0 @@ -- type: currency - id: EvolutionPoint - displayName: store-currency-display-evolutionpoints - canWithdraw: false diff --git a/Resources/Prototypes/Goobstation/Changeling/ai_factions.yml b/Resources/Prototypes/Goobstation/Changeling/ai_factions.yml deleted file mode 100644 index 9a6e1745af..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/ai_factions.yml +++ /dev/null @@ -1,7 +0,0 @@ -- type: npcFaction - id: Changeling - hostile: - - NanoTrasen - - Syndicate - - Zombie - - Revolutionary \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/Changeling/radio_channels.yml b/Resources/Prototypes/Goobstation/Changeling/radio_channels.yml deleted file mode 100644 index efb3b8da0f..0000000000 --- a/Resources/Prototypes/Goobstation/Changeling/radio_channels.yml +++ /dev/null @@ -1,7 +0,0 @@ -- type: radioChannel - id: Hivemind - name: chat-radio-hivemind - keycode: 'g' - frequency: 19840 - color: "#e36b00" - longRange: true \ No newline at end of file diff --git a/Resources/Prototypes/Goobstation/GameRules/roundstart.yml b/Resources/Prototypes/Goobstation/GameRules/roundstart.yml deleted file mode 100644 index 791b2dd41b..0000000000 --- a/Resources/Prototypes/Goobstation/GameRules/roundstart.yml +++ /dev/null @@ -1,149 +0,0 @@ -- type: entity - parent: BaseGameRule - id: Changeling - components: - - type: ChangelingRule - - type: GameRule - minPlayers: 15 - delay: - min: 240 - max: 420 - - type: AntagSelection - agentName: changeling-roundend-name - definitions: - - prefRoles: [ Changeling ] - max: 5 - playerRatio: 15 - lateJoinAdditional: true - mindComponents: - - type: ChangelingRole - prototype: Changeling - -- type: entity - parent: Traitor - id: CalmTraitor # For Dual Antag Gamemodes - components: - - type: GameRule - minPlayers: 30 - delay: - min: 240 - max: 420 - - type: AntagSelection - definitions: - - prefRoles: [ Traitor ] - max: 5 - playerRatio: 15 - blacklist: - components: - - AntagImmune - - Changeling - lateJoinAdditional: true - mindComponents: - - type: TraitorRole - prototype: Traitor - -- type: entity - parent: Changeling - id: CalmLing # For Dual Antag Gamemodes - components: - - type: GameRule - minPlayers: 30 - delay: - min: 240 - max: 420 - - type: AntagSelection - agentName: changeling-roundend-name - definitions: - - prefRoles: [ Changeling ] - max: 2 - playerRatio: 20 - lateJoinAdditional: true - mindComponents: - - type: ChangelingRole - prototype: Changeling - -- type: entity - parent: Nukeops - id: Calmops # For Dual Antag Gamemodes - components: - - type: GameRule - minPlayers: 30 - - type: LoadMapRule - gameMap: NukieOutpost - - type: AntagSelection - selectionTime: PrePlayerSpawn - definitions: - - prefRoles: [ NukeopsCommander ] - fallbackRoles: [ Nukeops, NukeopsMedic ] - spawnerPrototype: SpawnPointNukeopsCommander - startingGear: SyndicateCommanderGearFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-commander - - SyndicateNamesElite - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: NukeopsCommander - - prefRoles: [ NukeopsMedic ] - fallbackRoles: [ Nukeops, NukeopsCommander ] - spawnerPrototype: SpawnPointNukeopsMedic - startingGear: SyndicateOperativeMedicFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-agent - - SyndicateNamesNormal - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: NukeopsMedic - - prefRoles: [ Nukeops ] - fallbackRoles: [ NukeopsCommander, NukeopsMedic ] - spawnerPrototype: SpawnPointNukeopsOperative - max: 1 - playerRatio: 15 - startingGear: SyndicateOperativeGearFull - components: - - type: NukeOperative - - type: RandomMetadata - nameSegments: - - nukeops-role-operator - - SyndicateNamesNormal - - type: NpcFactionMember - factions: - - Syndicate - mindComponents: - - type: NukeopsRole - prototype: Nukeops - -- type: entity - id: CalmRevs # For Dual Antag Gamemodes - parent: BaseGameRule - components: - - type: GameRule - minPlayers: 30 - - type: RevolutionaryRule - - type: AntagSelection - definitions: - - prefRoles: [ HeadRev ] - max: 1 - playerRatio: 25 - briefing: - text: head-rev-role-greeting - color: CornflowerBlue - sound: "/Audio/Ambience/Antag/headrev_start.ogg" - startingGear: HeadRevGear - components: - - type: Revolutionary - - type: HeadRevolutionary - mindComponents: - - type: RevolutionaryRole - prototype: HeadRev diff --git a/Resources/Prototypes/Goobstation/game_presets.yml b/Resources/Prototypes/Goobstation/game_presets.yml deleted file mode 100644 index 36edaa285b..0000000000 --- a/Resources/Prototypes/Goobstation/game_presets.yml +++ /dev/null @@ -1,86 +0,0 @@ -- type: gamePreset - id: Changeling - alias: - - ling - - lings - - changeling - name: changeling-gamemode-title - description: changeling-gamemode-description - showInVote: false - rules: - - Changeling - - SubGamemodesRule - - BasicStationEventScheduler - - BasicRoundstartVariation - -- type: gamePreset - id: Traitorling - alias: - - lingtraitor - - traitorling - name: traitorling-title - description: traitorling-description - showInVote: false - rules: - - CalmLing - - CalmTraitor - - BasicStationEventScheduler - - BasicRoundstartVariation - -- type: gamePreset - id: NukeTraitor - alias: - - nuketot - - optraitor - - optot - name: nukeops-title - description: nukeops-description - showInVote: false - rules: - - Calmops - - CalmTraitor - - BasicStationEventScheduler - - BasicRoundstartVariation - -- type: gamePreset - id: NukeLing - alias: - - nukeling - - opling - name: nukeops-title - description: nukeops-description - showInVote: false - rules: - - Calmops - - CalmLing - - BasicStationEventScheduler - - BasicRoundstartVariation - -- type: gamePreset - id: RevTraitor - alias: - - revtraitor - - revtot - - totrevs - name: revtraitor-title - description: revtraitor-description - showInVote: false - rules: - - CalmRevs - - CalmTraitor - - BasicStationEventScheduler - -- type: gamePreset - id: RevLing - alias: - - revling - - lingrevs - name: revling-title - description: revling-description - showInVote: false - rules: - - CalmRevs - - CalmLing - - BasicStationEventScheduler - - BasicRoundstartVariation - - BasicRoundstartVariation diff --git a/Resources/Prototypes/Guidebook/antagonist.yml b/Resources/Prototypes/Guidebook/antagonist.yml index 985e590c4d..081ff7ef0a 100644 --- a/Resources/Prototypes/Guidebook/antagonist.yml +++ b/Resources/Prototypes/Guidebook/antagonist.yml @@ -4,7 +4,6 @@ text: "/ServerInfo/Guidebook/Antagonist/Antagonists.xml" children: - Traitors - - Changelings # goobstation - changelings - NuclearOperatives - Zombies - Revolutionaries diff --git a/Resources/Prototypes/Roles/Antags/nukeops.yml b/Resources/Prototypes/Roles/Antags/nukeops.yml index 1f803dd681..6e58b1c9ba 100644 --- a/Resources/Prototypes/Roles/Antags/nukeops.yml +++ b/Resources/Prototypes/Roles/Antags/nukeops.yml @@ -30,23 +30,13 @@ antagonist: true setPreference: true objective: roles-antag-nuclear-operative-commander-objective - -#Lone Operative Gear -- type: startingGear - id: SyndicateLoneOperativeGearFull - equipment: - jumpsuit: ClothingUniformJumpsuitOperative - back: ClothingBackpackDuffelSyndicateOperative - mask: ClothingMaskGasSyndicate - eyes: ClothingEyesHudSyndicate - ears: ClothingHeadsetAltSyndicate - gloves: ClothingHandsGlovesCombat - outerClothing: ClothingOuterHardsuitSyndie - shoes: ClothingShoesBootsCombatFilled - id: SyndiPDA - pocket1: DoubleEmergencyOxygenTankFilled - pocket2: BaseUplinkRadio40TC - belt: ClothingBeltMilitaryWebbing - innerClothingSkirt: ClothingUniformJumpskirtOperative - satchel: ClothingBackpackDuffelSyndicateOperative - duffelbag: ClothingBackpackDuffelSyndicateOperative + # requirements: + # - !type:OverallPlaytimeRequirement + # time: 216000 # DeltaV - 60 hours + # - !type:DepartmentTimeRequirement # DeltaV - Security dept time requirement + # department: Security + # time: 36000 # DeltaV - 10 hours + # - !type:DepartmentTimeRequirement # DeltaV - Command dept time requirement + # department: Command + # time: 36000 # DeltaV - 10 hours + # - !type:WhitelistRequirement # DeltaV - Whitelist requirement diff --git a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml index b2bcd8bcb4..434a7c1083 100644 --- a/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml +++ b/Resources/Prototypes/Roles/Jobs/Fun/misc_startinggear.yml @@ -293,18 +293,8 @@ #Head Rev Gear - type: startingGear id: HeadRevGear - storage: - back: - - Flash - - ClothingEyesGlassesSunglasses - -#Thief Gear -- type: startingGear - id: ThiefGear - storage: - back: - - ToolboxThief - - ClothingHandsChameleonThief + equipment: + pocket2: Flash #Gladiator with spear - type: startingGear diff --git a/Resources/Prototypes/game_presets.yml b/Resources/Prototypes/game_presets.yml index e71dee0433..92d879b066 100644 --- a/Resources/Prototypes/game_presets.yml +++ b/Resources/Prototypes/game_presets.yml @@ -31,7 +31,6 @@ - Revolutionary - Zombie - RampingStationEventScheduler - - Changeling - type: gamePreset id: Extended @@ -165,3 +164,15 @@ - Zombie - BasicStationEventScheduler - BasicRoundstartVariation + +- type: gamePreset + id: Pirates + alias: + - pirates + name: pirates-title + description: pirates-description + showInVote: false + rules: + - Pirates + - BasicStationEventScheduler + - BasicRoundstartVariation diff --git a/Resources/Prototypes/secret_weights.yml b/Resources/Prototypes/secret_weights.yml index 8a9f9a766a..4ad31cd194 100644 --- a/Resources/Prototypes/secret_weights.yml +++ b/Resources/Prototypes/secret_weights.yml @@ -1,16 +1,9 @@ - type: weightedRandom id: Secret weights: - Traitor: 0.40 - Changeling: 0.10 - Traitorling: 0.05 - Nukeops: 0.20 - NukeTraitor: 0.01 - NukeLing: 0.01 - Revolutionary: 0.10 - RevTraitor: 0.02 - RevLing: 0.01 - Zombie: 0.05 - Survival: 0.05 - # Wizard: 0.05 - # Cult: 0.05 + Survival: 0.44 + Nukeops: 0.14 + Zombie: 0.03 + Traitor: 0.39 + #Pirates: 0.15 #ahoy me bucko + diff --git a/Resources/ServerInfo/Goobstation/Changeling/Guidebook/Antagonist/Changelings.xml b/Resources/ServerInfo/Goobstation/Changeling/Guidebook/Antagonist/Changelings.xml deleted file mode 100644 index 789982ad9e..0000000000 --- a/Resources/ServerInfo/Goobstation/Changeling/Guidebook/Antagonist/Changelings.xml +++ /dev/null @@ -1,61 +0,0 @@ - - # Changelings - - - - A Changeling is an alien creature that is intelligent and able to morph into humans. Changelings are aliens in a form of a headslug, but before a shift starts, they either transform as someone while they're off-station or rather they somehow killed someone and made into a Changeling through unknown ways. - The main weapons of the Changeling are its ability to internally synthesize dangerous chemicals, morph into other creatures that it has absorbed, and blend in with humans. - - The changeling can be anyone it's absorbed, it can switch identities instantaneously, only absorbing takes time and peace. Unlike the Traitor, the Changeling's only objective is to survive until the shuttle arrives and escape on it while transformed into somebody else. - - Remember that Changelings are not obliged to work eachother as a team, and some may go solo/rogue depending on how they like it. - - ## I've turned into myself, what do? - - ### Chemicals - Chemicals are your source of abilities. Without them you won't be able to use your powers. - They regenerate slowly over time, and absorption will increase their max capacity. - - ### Biomass - Your biomass is your health. In the beginning you have 30 biomass to begin. You spend 1 biomass each minute, and absorption fully recovers it. - Once your biomass levels get low enough, the effects of your decay will be seen by crew, such as: - - Vomiting blood - - Violently shaking - - Death. - You cannot die normally, as in being gibbed by blunt trauma, but your Biomass is slowly draining away, and if you don't get to absorb someone before it runs out, your game will be over. - - ### DNA Absorption - Your main weapon is deception. Transform into other humanoid creatures to confuse the crew. - To do this, it must take ANY human, living or dead (even thrown away bodies from cloning), and absorb them using either the Absorb abliity, or the DNA Extraction Sting. - You can only have a maximum of 5 DNA strands at a time, and must transform to obtain more. - - Acquiring DNA via absorption requires the victim's incapacitation, critical condition or death. Simply, handcuff, put him into crit or kill him if you need to absorb him. - - - Absorbing someone takes a lot of time, so prepare a safe spot or do it somewhere with the least amount of ears possible. - - Absorbing a victim will recover all of your biomass, increase your maximum chemical capacity and give you bonus evolution points to buy new abilities. - - Absorbing another changeling will, on top of that, increase your maximum biomass capacity, allowing you to stay alive for more time and give you even more chemicals and evolution points. - - [color=red]Absorbed victims cannot be cloned.[/color] On the other hand, they can still be turned into cyborgs. - - ### You exclaim, "I am the only one here!" - Changelings are limited, however, to how much DNA they can absorb at once! If a changeling has 5 DNAs stored and attempts to gain another, they must purge the older DNA by transforming. Eventually, any changeling will have to be a twin of someone else on the station, living or dead. - The changeling can shift its appearance, making them look and sound exactly like a victim of which they have absorbed. This can be massive compromise in security, especially if command staff are absorbed and the changeling is able to imitate them. - Changelings can also, via their lesser form ability, transform into monkeys and do monkey things. - - - ### Regeneration - Also known as Regenerative Stasis, changelings have the ability to 'kill' themselves, and appear dead. After an uncertain amount of time, the changeling can revive at will, fully healed of all injuries and illness. - Entering stasis drains all of the changeling's chemicals, and leaving costs 60. Chemicals will still regenerate while a changeling is dead, meaning it can always enter stasis unless it's biomass levels are critical. - - Changelings can also choose to revive by defibrillator, making it even harder to tell if they are one or not. - - This makes them nigh-unkillable, as they can fully regenerate themselves even from death if they have enough chemicals. Spaced changelings may also be able to make it back on station given enough time. The best way to permanently deal with a changeling is to starve it to death, putting them on mining colonies or solitary confinement. - - ### Going Solo or Teaming Up - Like traitors, changelings operate individually and are in no way obligated to assist each other. It is not required for changelings to even reveal their identity to each other, as it's not uncommon for changelings to backstab each other to remove competition. - Even so, [colopr=red]a coordinated group of changelings is truly a terror to behold[/color]. - - ## Identifying a changeling - Changelings have a different blood type. Even if a changeling is pretending to be a diona, vox or a moth person, they have one blood type. - Also, when put in a centrifuge, the changeling's blood reacts violently. - You cannot identify changelings in any other possible way, unless they're dead obvious and start going loud. - diff --git a/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/icon.png deleted file mode 100644 index b0a671bf4a..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/icon.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/meta.json deleted file mode 100644 index 23850a83e4..0000000000 --- a/Resources/Textures/Goobstation/Changeling/Guidebook/guidebook_changeling.rsi/meta.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/tgstation/tgstation/blob/8024397cc81c5f47f74cf4279e35728487d0a1a7/icons/mob/human_parts_greyscale.dmi , modified by DrSmugleaf and whateverusername0", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon", - "delays": [ [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1 ] ] - } - ] -} diff --git a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/icon.png deleted file mode 100644 index 94bfbcc365..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/icon.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/inhand-left.png b/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/inhand-left.png deleted file mode 100644 index 7ee864de99..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/inhand-left.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/inhand-right.png b/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/inhand-right.png deleted file mode 100644 index 10e69a7ef1..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/inhand-right.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/meta.json deleted file mode 100644 index 9ef5a5f7d0..0000000000 --- a/Resources/Textures/Goobstation/Changeling/arm_blade.rsi/meta.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "taken from tg at https://github.com/tgstation/tgstation/blob/master/icons/obj/changeling_items.dmi and edited by https://github.com/RealFakeSoof", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "inhand-left", - "directions" : 4, - "delays": [ - [ 0.5, 0.5 ], - [ 0.5, 0.5 ], - [ 0.5, 0.5 ], - [ 0.5, 0.5 ] - ] - }, - { - "name": "inhand-right", - "directions" : 4, - "delays": [ - [ 0.5, 0.5 ], - [ 0.5, 0.5 ], - [ 0.5, 0.5 ], - [ 0.5, 0.5 ] - ] - } - ] -} diff --git a/Resources/Textures/Goobstation/Changeling/bone_shard.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/bone_shard.rsi/icon.png deleted file mode 100644 index 7661444b00..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/bone_shard.rsi/icon.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/bone_shard.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/bone_shard.rsi/meta.json deleted file mode 100644 index a4cc335ab8..0000000000 --- a/Resources/Textures/Goobstation/Changeling/bone_shard.rsi/meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 1, - "license": "CC0-1.0", - "copyright": "https://github.com/RealFakeSoof with references from deltanedas (github)", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - } - ] -} diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/absorb_dna.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/absorb_dna.png deleted file mode 100644 index 391abb092f..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/absorb_dna.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/anatomic_panacea.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/anatomic_panacea.png deleted file mode 100644 index 348e6bc318..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/anatomic_panacea.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/armblade.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/armblade.png deleted file mode 100644 index 5964de5538..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/armblade.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/augmented_eyesight.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/augmented_eyesight.png deleted file mode 100644 index f80ae10c88..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/augmented_eyesight.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/biodegrade.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/biodegrade.png deleted file mode 100644 index 902205d203..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/biodegrade.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/bone_shard.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/bone_shard.png deleted file mode 100644 index de7b3ba28a..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/bone_shard.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/chameleon_skin.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/chameleon_skin.png deleted file mode 100644 index f2e5fb28e6..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/chameleon_skin.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/chitinous_armor.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/chitinous_armor.png deleted file mode 100644 index 1c8e18ecc8..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/chitinous_armor.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/epinephrine_overdose.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/epinephrine_overdose.png deleted file mode 100644 index aec8e4fcf9..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/epinephrine_overdose.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/evolution_menu.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/evolution_menu.png deleted file mode 100644 index a68fa0544d..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/evolution_menu.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/fleshmend.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/fleshmend.png deleted file mode 100644 index 0f0b3f0ed4..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/fleshmend.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/hivemind_access.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/hivemind_access.png deleted file mode 100644 index a0405d5f6c..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/hivemind_access.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/lesser_form.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/lesser_form.png deleted file mode 100644 index 5b287d37b7..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/lesser_form.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/meta.json deleted file mode 100644 index e85f2a54fa..0000000000 --- a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/meta.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken and edited from Paradise Station wiki https://paradisestation.org/wiki/index.php/Changeling , sting_transform and sting_armblade retextures made by whateverusername0", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "absorb_dna" - }, - { - "name": "anatomic_panacea" - }, - { - "name": "armblade" - }, - { - "name": "augmented_eyesight" - }, - { - "name": "biodegrade" - }, - { - "name": "bone_shard" - }, - { - "name": "chameleon_skin", - "delays": [ [ 0.1, 0.1, 0.1, 0.1 ] ] - }, - { - "name": "chitinous_armor" - }, - { - "name": "epinephrine_overdose" - }, - { - "name": "evolution_menu" - }, - { - "name": "fleshmend" - }, - { - "name": "hivemind_access" - }, - { - "name": "lesser_form" - }, - { - "name": "organic_shield" - }, - { - "name": "shriek_dissonant" - }, - { - "name": "shriek_resonant" - }, - { - "name": "space_adaptation" - }, - { - "name": "stasis_enter" - }, - { - "name": "stasis_exit", - "delays": [ [ 0.5, 0.5 ] ] - }, - { - "name": "sting_armblade" - }, - { - "name": "sting_blind" - }, - { - "name": "sting_cryo" - }, - { - "name": "sting_extractdna" - }, - { - "name": "sting_lethargic" - }, - { - "name": "sting_mute" - }, - { - "name": "sting_transform" - }, - { - "name": "strained_muscles" - }, - { - "name": "tentacle" - }, - { - "name": "transform" - }, - { - "name": "transform_cycle" - } - ] -} diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/organic_shield.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/organic_shield.png deleted file mode 100644 index 1fc5524e2d..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/organic_shield.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_dissonant.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_dissonant.png deleted file mode 100644 index 88e6918624..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_dissonant.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_resonant.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_resonant.png deleted file mode 100644 index 1f75903bb4..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/shriek_resonant.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/space_adaptation.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/space_adaptation.png deleted file mode 100644 index 762e6a79a7..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/space_adaptation.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/stasis_enter.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/stasis_enter.png deleted file mode 100644 index 95b6448748..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/stasis_enter.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/stasis_exit.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/stasis_exit.png deleted file mode 100644 index ad5a30b7c3..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/stasis_exit.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_armblade.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_armblade.png deleted file mode 100644 index 5eca795087..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_armblade.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_blind.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_blind.png deleted file mode 100644 index 4d3e48c7bc..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_blind.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_cryo.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_cryo.png deleted file mode 100644 index b935074af0..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_cryo.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_extractdna.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_extractdna.png deleted file mode 100644 index 390660021d..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_extractdna.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_lethargic.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_lethargic.png deleted file mode 100644 index 53283dcac9..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_lethargic.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_mute.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_mute.png deleted file mode 100644 index f4f1c41726..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_mute.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_transform.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_transform.png deleted file mode 100644 index b06ec7fa88..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/sting_transform.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/strained_muscles.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/strained_muscles.png deleted file mode 100644 index 0bc4ca3177..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/strained_muscles.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/tentacle.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/tentacle.png deleted file mode 100644 index f09b036552..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/tentacle.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/transform.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/transform.png deleted file mode 100644 index daa38687e3..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/transform.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/transform_cycle.png b/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/transform_cycle.png deleted file mode 100644 index fab4a3dcb0..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_abilities.rsi/transform_cycle.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/0.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/0.png deleted file mode 100644 index e6dea8ec56..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/0.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/1.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/1.png deleted file mode 100644 index 626313e674..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/1.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/10.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/10.png deleted file mode 100644 index 17316fd6c3..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/10.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/11.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/11.png deleted file mode 100644 index d5060df98b..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/11.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/12.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/12.png deleted file mode 100644 index 093cbf1033..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/12.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/13.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/13.png deleted file mode 100644 index 2df6df47dd..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/13.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/14.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/14.png deleted file mode 100644 index 2b4dc8e0cb..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/14.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/15.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/15.png deleted file mode 100644 index 36ca36b9fa..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/15.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/16.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/16.png deleted file mode 100644 index a58d33d1e7..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/16.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/2.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/2.png deleted file mode 100644 index a7f6210c9e..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/2.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/3.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/3.png deleted file mode 100644 index ae0e391e1c..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/3.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/4.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/4.png deleted file mode 100644 index 36d867d3b8..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/4.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/5.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/5.png deleted file mode 100644 index a49d8830b6..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/5.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/6.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/6.png deleted file mode 100644 index 994c2fb5bd..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/6.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/7.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/7.png deleted file mode 100644 index deede1c64c..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/7.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/8.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/8.png deleted file mode 100644 index 8740017517..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/8.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/9.png b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/9.png deleted file mode 100644 index eda5f02551..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/9.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/meta.json deleted file mode 100644 index 12f5ad4cdb..0000000000 --- a/Resources/Textures/Goobstation/Changeling/changeling_biomass.rsi/meta.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from cmss13 at https://github.com/cmss13-devs/cmss13/blob/09a5191fb11aab8ddffe3f9be94292b53e4d96f6/icons/mob/hud/alien_standard.dmi", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "16" - }, - { - "name": "15" - }, - { - "name": "14" - }, - { - "name": "13" - }, - { - "name": "12" - }, - { - "name": "11" - }, - { - "name": "10" - }, - { - "name": "9" - }, - { - "name": "8" - }, - { - "name": "7" - }, - { - "name": "6" - }, - { - "name": "5" - }, - { - "name": "4" - }, - { - "name": "3" - }, - { - "name": "2" - }, - { - "name": "1" - }, - { - "name": "0", - "delays": [ [ 0.1, 0.1 ] ] - } - ] -} diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/0.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/0.png deleted file mode 100644 index bb1a8a818a..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/0.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/1.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/1.png deleted file mode 100644 index ab9936e2a6..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/1.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/10.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/10.png deleted file mode 100644 index 9ef25d0043..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/10.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/11.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/11.png deleted file mode 100644 index af2190ad8e..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/11.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/12.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/12.png deleted file mode 100644 index 061ffac41a..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/12.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/13.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/13.png deleted file mode 100644 index 26c1870e72..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/13.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/14.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/14.png deleted file mode 100644 index 99c7f452f1..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/14.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/15.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/15.png deleted file mode 100644 index 61536f9e5c..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/15.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/16.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/16.png deleted file mode 100644 index 9c054691a6..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/16.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/17.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/17.png deleted file mode 100644 index aafe104caa..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/17.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/18.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/18.png deleted file mode 100644 index 6a7b5a75d8..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/18.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/2.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/2.png deleted file mode 100644 index f6b5880efe..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/2.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/3.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/3.png deleted file mode 100644 index 7028b90245..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/3.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/4.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/4.png deleted file mode 100644 index a1175788b8..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/4.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/5.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/5.png deleted file mode 100644 index b1cd396c1e..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/5.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/6.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/6.png deleted file mode 100644 index a7733d59ee..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/6.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/7.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/7.png deleted file mode 100644 index a0f02d6a37..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/7.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/8.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/8.png deleted file mode 100644 index e701948378..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/8.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/9.png b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/9.png deleted file mode 100644 index 8ac5c24934..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/9.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/meta.json deleted file mode 100644 index 8d646c4f49..0000000000 --- a/Resources/Textures/Goobstation/Changeling/changeling_chemicals.rsi/meta.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from cmss13 at https://github.com/cmss13-devs/cmss13/blob/09a5191fb11aab8ddffe3f9be94292b53e4d96f6/icons/mob/hud/alien_standard.dmi", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "0" - }, - { - "name": "1", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "2", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "3", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "4", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "5", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "6", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "7", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "8", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "9", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "10", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "11", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "12", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "13", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "14", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "15", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "16", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "17", - "delays": [ [ 0.3, 0.1 ] ] - }, - { - "name": "18", - "delays": [ [ 0.3, 0.1 ] ] - } - ] -} \ No newline at end of file diff --git a/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/equipped-OUTERCLOTHING.png deleted file mode 100644 index cee98c3e60..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/equipped-OUTERCLOTHING.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/icon.png deleted file mode 100644 index 18f0de148d..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/icon.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/meta.json deleted file mode 100644 index b8872a3c05..0000000000 --- a/Resources/Textures/Goobstation/Changeling/ling_armor.rsi/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/master/icons/mob/clothing/suit.dmi", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "equipped-OUTERCLOTHING", - "directions": 4 - } - ] -} \ No newline at end of file diff --git a/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/equipped-HELMET.png b/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/equipped-HELMET.png deleted file mode 100644 index cb4bdb9ebc..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/equipped-HELMET.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/icon.png deleted file mode 100644 index 822c8e519b..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/icon.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/meta.json deleted file mode 100644 index f564695227..0000000000 --- a/Resources/Textures/Goobstation/Changeling/ling_armor_helmet.rsi/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tg at https://github.com/tgstation/tgstation/blob/master/icons/mob/clothing/head.dmi", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "equipped-HELMET", - "directions": 4 - } - ] -} \ No newline at end of file diff --git a/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/equipped-OUTERCLOTHING.png deleted file mode 100644 index c3b4280843..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/equipped-OUTERCLOTHING.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/icon.png deleted file mode 100644 index 27bcc7fe79..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/icon.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/meta.json deleted file mode 100644 index ffa6adf456..0000000000 --- a/Resources/Textures/Goobstation/Changeling/ling_spacesuit.rsi/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "whateverusername0", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "equipped-OUTERCLOTHING", - "directions": 4 - } - ] -} \ No newline at end of file diff --git a/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/equipped-HELMET.png b/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/equipped-HELMET.png deleted file mode 100644 index 27a959da55..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/equipped-HELMET.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/icon.png b/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/icon.png deleted file mode 100644 index acb1c73720..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/icon.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/meta.json deleted file mode 100644 index a40d761820..0000000000 --- a/Resources/Textures/Goobstation/Changeling/ling_spacesuit_helmet.rsi/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "whateverusername0", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "icon" - }, - { - "name": "equipped-HELMET", - "directions": 4 - } - ] -} \ No newline at end of file diff --git a/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-icon.png b/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-icon.png deleted file mode 100644 index 81da793fff..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-icon.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-left.png b/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-left.png deleted file mode 100644 index 373bbc8d43..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-left.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-right.png b/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-right.png deleted file mode 100644 index 9f879f0840..0000000000 Binary files a/Resources/Textures/Goobstation/Changeling/shields.rsi/ling-inhand-right.png and /dev/null differ diff --git a/Resources/Textures/Goobstation/Changeling/shields.rsi/meta.json b/Resources/Textures/Goobstation/Changeling/shields.rsi/meta.json deleted file mode 100644 index a7ffc69df7..0000000000 --- a/Resources/Textures/Goobstation/Changeling/shields.rsi/meta.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from https://github.com/ParadiseSS13/Paradise/blob/master/icons/obj/weapons/shield.dmi", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "ling-icon" - }, - { - "name": "ling-inhand-left", - "directions": 4 - }, - { - "name": "ling-inhand-right", - "directions": 4 - } - ] -} diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/Changeling.png b/Resources/Textures/Interface/Misc/job_icons.rsi/Changeling.png deleted file mode 100644 index 95be439460..0000000000 Binary files a/Resources/Textures/Interface/Misc/job_icons.rsi/Changeling.png and /dev/null differ diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json b/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json index e845b98728..745cc43b84 100644 --- a/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json +++ b/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json @@ -185,9 +185,6 @@ }, { "name": "InitialInfected" - }, - { - "name": "Changeling" } ] }