diff --git a/Content.Client/IconSmoothing/IconSmoothComponent.cs b/Content.Client/IconSmoothing/IconSmoothComponent.cs index 040198529c751..7898e9dc1ebc6 100644 --- a/Content.Client/IconSmoothing/IconSmoothComponent.cs +++ b/Content.Client/IconSmoothing/IconSmoothComponent.cs @@ -26,6 +26,12 @@ public sealed partial class IconSmoothComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField("key")] public string? SmoothKey { get; private set; } + /// + /// Additional keys to smooth with. + /// + [DataField] + public List AdditionalKeys = new(); + /// /// Prepended to the RSI state. /// diff --git a/Content.Client/IconSmoothing/IconSmoothSystem.cs b/Content.Client/IconSmoothing/IconSmoothSystem.cs index 2654f1ab574f1..45aafb528875f 100644 --- a/Content.Client/IconSmoothing/IconSmoothSystem.cs +++ b/Content.Client/IconSmoothing/IconSmoothSystem.cs @@ -376,7 +376,8 @@ private bool MatchingEntity(IconSmoothComponent smooth, AnchoredEntitiesEnumerat while (candidates.MoveNext(out var entity)) { if (smoothQuery.TryGetComponent(entity, out var other) && - other.SmoothKey == smooth.SmoothKey && + other.SmoothKey != null && + (other.SmoothKey == smooth.SmoothKey || smooth.AdditionalKeys.Contains(other.SmoothKey)) && other.Enabled) { return true; diff --git a/Content.Server/Antag/AntagSelectionSystem.API.cs b/Content.Server/Antag/AntagSelectionSystem.API.cs index a4cb6fd0e5975..93b5fa6136516 100644 --- a/Content.Server/Antag/AntagSelectionSystem.API.cs +++ b/Content.Server/Antag/AntagSelectionSystem.API.cs @@ -3,7 +3,9 @@ using Content.Server.Antag.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Objectives; +using Content.Shared.Antag; using Content.Shared.Chat; +using Content.Shared.GameTicking.Components; using Content.Shared.Mind; using Content.Shared.Preferences; using JetBrains.Annotations; @@ -25,7 +27,7 @@ public bool TryGetNextAvailableDefinition(Entity ent, definition = null; var totalTargetCount = GetTargetAntagCount(ent, players); - var mindCount = ent.Comp.SelectedMinds.Count; + var mindCount = ent.Comp.AssignedMinds.Count; if (mindCount >= totalTargetCount) return false; @@ -95,7 +97,7 @@ public int GetTargetAntagCount(Entity ent, int? playerC var countOffset = 0; foreach (var otherDef in ent.Comp.Definitions) { - countOffset += Math.Clamp((poolSize - countOffset) / otherDef.PlayerRatio, otherDef.Min, otherDef.Max) * otherDef.PlayerRatio; + countOffset += Math.Clamp((poolSize - countOffset) / otherDef.PlayerRatio, otherDef.Min, otherDef.Max) * otherDef.PlayerRatio; // Note: Is the PlayerRatio necessary here? Seems like it can cause issues for defs with varied PlayerRatio. } // make sure we don't double-count the current selection countOffset -= Math.Clamp(poolSize / def.PlayerRatio, def.Min, def.Max) * def.PlayerRatio; @@ -115,7 +117,7 @@ public int GetTargetAntagCount(Entity ent, int? playerC return new List<(EntityUid, SessionData, string)>(); var output = new List<(EntityUid, SessionData, string)>(); - foreach (var (mind, name) in ent.Comp.SelectedMinds) + foreach (var (mind, name) in ent.Comp.AssignedMinds) { if (!TryComp(mind, out var mindComp) || mindComp.OriginalOwnerUserId == null) continue; @@ -137,7 +139,7 @@ public List> GetAntagMinds(Entity>(); - foreach (var (mind, _) in ent.Comp.SelectedMinds) + foreach (var (mind, _) in ent.Comp.AssignedMinds) { if (!TryComp(mind, out var mindComp) || mindComp.OriginalOwnerUserId == null) continue; @@ -155,7 +157,7 @@ public List GetAntagMindEntityUids(Entity e if (!Resolve(ent, ref ent.Comp, false)) return new(); - return ent.Comp.SelectedMinds.Select(p => p.Item1).ToList(); + return ent.Comp.AssignedMinds.Select(p => p.Item1).ToList(); } /// @@ -247,7 +249,7 @@ public bool AllAntagsAlive(Entity ent) if (!Resolve(ent, ref ent.Comp, false)) return false; - return GetAliveAntagCount(ent) == ent.Comp.SelectedMinds.Count; + return GetAliveAntagCount(ent) == ent.Comp.AssignedMinds.Count; } /// @@ -352,8 +354,66 @@ public Entity ForceGetGameRuleEnt(string id) where T var ruleEnt = GameTicker.AddGameRule(id); RemComp(ruleEnt); var antag = Comp(ruleEnt); - antag.SelectionsComplete = true; // don't do normal selection. + antag.AssignmentComplete = true; // don't do normal selection. GameTicker.StartGameRule(ruleEnt); return (ruleEnt, antag); } + + /// + /// Get all sessions that have been preselected for antag. + /// + /// A specific definition to be excluded from the check. + public HashSet GetPreSelectedAntagSessions(AntagSelectionDefinition? except = null) + { + var result = new HashSet(); + var query = QueryAllRules(); + while (query.MoveNext(out var uid, out var comp, out _)) + { + if (HasComp(uid)) + continue; + + foreach (var def in comp.Definitions) + { + if (def.Equals(except)) + continue; + + if (comp.PreSelectedSessions.TryGetValue(def, out var set)) + result.UnionWith(set); + } + } + + return result; + } + + /// + /// Get all sessions that have been preselected for antag and are exclusive, i.e. should not be paired with other antags. + /// + /// A specific definition to be excluded from the check. + // Note: This is a bit iffy since technically this exclusive definition is defined via the MultiAntagSetting, while there's a separately tracked antagExclusive variable in the mindrole. + // We can't query that however since there's no guarantee the mindrole has been given out yet when checking pre-selected antags. + // I don't think there's any instance where they differ, but it's something to be aware of for a potential future refactor. + public HashSet GetPreSelectedExclusiveAntagSessions(AntagSelectionDefinition? except = null) + { + var result = new HashSet(); + var query = QueryAllRules(); + while (query.MoveNext(out var uid, out var comp, out _)) + { + if (HasComp(uid)) + continue; + + foreach (var def in comp.Definitions) + { + if (def.Equals(except)) + continue; + + if (def.MultiAntagSetting == AntagAcceptability.None && comp.PreSelectedSessions.TryGetValue(def, out var set)) + { + result.UnionWith(set); + break; + } + } + } + + return result; + } } diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index ce7f3fec4c628..c8b2a7d4bb6c8 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -11,8 +11,11 @@ using Content.Server.Roles; using Content.Server.Roles.Jobs; using Content.Server.Shuttles.Components; +using Content.Server.Station.Events; +using Content.Shared.Administration.Logs; using Content.Shared.Antag; using Content.Shared.Clothing; +using Content.Shared.Database; using Content.Shared.GameTicking; using Content.Shared.GameTicking.Components; using Content.Shared.Ghost; @@ -46,6 +49,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem(); - while (query.MoveNext(out var uid, out _, out var antag, out _)) + while (query.MoveNext(out var uid, out var antag, out _)) { - rules.Add((uid, antag)); + if (HasComp(uid) || + (HasComp(uid) && antag.SelectionTime == AntagSelectionTime.IntraPlayerSpawn)) //IntraPlayerSpawn selects antags before spawning, but doesn't activate until after. + rules.Add((uid, antag)); } RobustRandom.Shuffle(rules); @@ -142,7 +163,7 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent args) if (!antag.Definitions.Any(p => p.LateJoinAdditional)) continue; - DebugTools.AssertEqual(antag.SelectionTime, AntagSelectionTime.PostPlayerSpawn); + DebugTools.AssertNotEqual(antag.SelectionTime, AntagSelectionTime.PrePlayerSpawn); // do not count players in the lobby for the antag ratio var players = _playerManager.NetworkedSessions.Count(x => x.AttachedEntity != null); @@ -150,7 +171,9 @@ private void OnSpawnComplete(PlayerSpawnCompleteEvent args) if (!TryGetNextAvailableDefinition((uid, antag), out var def, players)) continue; - if (TryMakeAntag((uid, antag), args.Player, def.Value)) + var onlyPreSelect = (antag.SelectionTime == AntagSelectionTime.IntraPlayerSpawn && !antag.AssignmentComplete); // Don't wanna give them antag status if the rule hasn't assigned its existing ones yet + + if (TryMakeAntag((uid, antag), args.Player, def.Value, onlyPreSelect: onlyPreSelect)) break; } } @@ -183,14 +206,20 @@ protected override void Started(EntityUid uid, AntagSelectionComponent component if (GameTicker.RunLevel != GameRunLevel.InRound) return; - if (component.SelectionsComplete) + if (component.AssignmentComplete) return; - var players = _playerManager.Sessions - .Where(x => GameTicker.PlayerGameStatuses.TryGetValue(x.UserId, out var status) && status == PlayerGameStatus.JoinedGame) - .ToList(); + if (!component.PreSelectionsComplete) + { + var players = _playerManager.Sessions + .Where(x => GameTicker.PlayerGameStatuses.TryGetValue(x.UserId, out var status) && + status == PlayerGameStatus.JoinedGame) + .ToList(); + + ChooseAntags((uid, component), players, midround: true); + } - ChooseAntags((uid, component), players, midround: true); + AssignPreSelectedSessions((uid, component)); } /// @@ -201,7 +230,7 @@ protected override void Started(EntityUid uid, AntagSelectionComponent component /// Disable picking players for pre-spawn antags in the middle of a round public void ChooseAntags(Entity ent, IList pool, bool midround = false) { - if (ent.Comp.SelectionsComplete) + if (ent.Comp.PreSelectionsComplete) return; foreach (var def in ent.Comp.Definitions) @@ -209,7 +238,7 @@ public void ChooseAntags(Entity ent, IList @@ -250,21 +279,53 @@ public void ChooseAntags(Entity ent, break; } - if (session != null && ent.Comp.SelectedSessions.Contains(session)) + if (session != null && ent.Comp.PreSelectedSessions.Values.Any(x => x.Contains(session))) { Log.Warning($"Somehow picked {session} for an antag when this rule already selected them previously"); continue; } } - MakeAntag(ent, session, def); + if (session == null) + MakeAntag(ent, null, def); // This is for spawner antags + else + { + if (!ent.Comp.PreSelectedSessions.TryGetValue(def, out var set)) + ent.Comp.PreSelectedSessions.Add(def, set = new HashSet()); + set.Add(session); // Selection done! + Log.Debug($"Pre-selected {session.Name} as antagonist: {ToPrettyString(ent)}"); + _adminLogger.Add(LogType.AntagSelection, $"Pre-selected {session.Name} as antagonist: {ToPrettyString(ent)}"); + } } } + /// + /// Assigns antag roles to sessions selected for it. + /// + public void AssignPreSelectedSessions(Entity ent) + { + // Only assign if there's been a pre-selection, and the selection hasn't already been made + if (!ent.Comp.PreSelectionsComplete || ent.Comp.AssignmentComplete) + return; + + foreach (var def in ent.Comp.Definitions) + { + if (!ent.Comp.PreSelectedSessions.TryGetValue(def, out var set)) + continue; + + foreach (var session in set) + { + TryMakeAntag(ent, session, def); + } + } + + ent.Comp.AssignmentComplete = true; + } + /// /// Tries to makes a given player into the specified antagonist. /// - public bool TryMakeAntag(Entity ent, ICommonSession? session, AntagSelectionDefinition def, bool ignoreSpawner = false, bool checkPref = true) + public bool TryMakeAntag(Entity ent, ICommonSession? session, AntagSelectionDefinition def, bool ignoreSpawner = false, bool checkPref = true, bool onlyPreSelect = false) { if (checkPref && !HasPrimaryAntagPreference(session, def)) return false; @@ -272,7 +333,19 @@ public bool TryMakeAntag(Entity ent, ICommonSession? se if (!IsSessionValid(ent, session, def) || !IsEntityValid(session?.AttachedEntity, def)) return false; - MakeAntag(ent, session, def, ignoreSpawner); + if (onlyPreSelect && session != null) + { + if (!ent.Comp.PreSelectedSessions.TryGetValue(def, out var set)) + ent.Comp.PreSelectedSessions.Add(def, set = new HashSet()); + set.Add(session); + Log.Debug($"Pre-selected {session!.Name} as antagonist: {ToPrettyString(ent)}"); + _adminLogger.Add(LogType.AntagSelection, $"Pre-selected {session.Name} as antagonist: {ToPrettyString(ent)}"); + } + else + { + MakeAntag(ent, session, def, ignoreSpawner); + } + return true; } @@ -286,7 +359,10 @@ public void MakeAntag(Entity ent, ICommonSession? sessi if (session != null) { - ent.Comp.SelectedSessions.Add(session); + if (!ent.Comp.PreSelectedSessions.TryGetValue(def, out var set)) + ent.Comp.PreSelectedSessions.Add(def, set = new HashSet()); + set.Add(session); + ent.Comp.AssignedSessions.Add(session); // we shouldn't be blocking the entity if they're just a ghost or smth. if (!HasComp(session.AttachedEntity)) @@ -309,7 +385,11 @@ public void MakeAntag(Entity ent, ICommonSession? sessi { Log.Error($"Attempted to make {session} antagonist in gamerule {ToPrettyString(ent)} but there was no valid entity for player."); if (session != null) - ent.Comp.SelectedSessions.Remove(session); + { + ent.Comp.AssignedSessions.Remove(session); + ent.Comp.PreSelectedSessions[def].Remove(session); + } + return; } @@ -330,7 +410,11 @@ public void MakeAntag(Entity ent, ICommonSession? sessi { Log.Error($"Antag spawner {player} does not have a GhostRoleAntagSpawnerComponent."); if (session != null) - ent.Comp.SelectedSessions.Remove(session); + { + ent.Comp.AssignedSessions.Remove(session); + ent.Comp.PreSelectedSessions[def].Remove(session); + } + return; } @@ -363,10 +447,11 @@ public void MakeAntag(Entity ent, ICommonSession? sessi _mind.TransferTo(curMind.Value, antagEnt, ghostCheckOverride: true); _role.MindAddRoles(curMind.Value, def.MindRoles, null, true); - ent.Comp.SelectedMinds.Add((curMind.Value, Name(player))); + ent.Comp.AssignedMinds.Add((curMind.Value, Name(player))); SendBriefing(session, def.Briefing); - Log.Debug($"Selected {ToPrettyString(curMind)} as antagonist: {ToPrettyString(ent)}"); + Log.Debug($"Assigned {ToPrettyString(curMind)} as antagonist: {ToPrettyString(ent)}"); + _adminLogger.Add(LogType.AntagSelection, $"Assigned {ToPrettyString(curMind)} as antagonist: {ToPrettyString(ent)}"); } var afterEv = new AfterAntagEntitySelectedEvent(session, player, ent, def); @@ -412,15 +497,11 @@ public bool IsSessionValid(Entity ent, ICommonSession? if (session.Status is SessionStatus.Disconnected or SessionStatus.Zombie) return false; - if (ent.Comp.SelectedSessions.Contains(session)) + if (ent.Comp.AssignedSessions.Contains(session)) return false; mind ??= session.GetMind(); - // If the player has not spawned in as any entity (e.g., in the lobby), they can be given an antag role/entity. - if (mind == null) - return true; - //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) @@ -429,12 +510,16 @@ public bool IsSessionValid(Entity ent, ICommonSession? { if (_role.MindIsAntagonist(mind)) return false; + if (GetPreSelectedAntagSessions(def).Contains(session)) // Used for rules where the antag has been selected, but not started yet + return false; break; } case AntagAcceptability.NotExclusive: { if (_role.MindIsExclusiveAntagonist(mind)) return false; + if (GetPreSelectedExclusiveAntagSessions(def).Contains(session)) + return false; break; } } @@ -481,7 +566,7 @@ private void OnObjectivesTextGetInfo(Entity ent, ref Ob if (ent.Comp.AgentName is not { } name) return; - args.Minds = ent.Comp.SelectedMinds; + args.Minds = ent.Comp.AssignedMinds; args.AgentName = Loc.GetString(name); } } diff --git a/Content.Server/Antag/Components/AntagSelectionComponent.cs b/Content.Server/Antag/Components/AntagSelectionComponent.cs index 35c6f8bc3a409..5c752ddff79fd 100644 --- a/Content.Server/Antag/Components/AntagSelectionComponent.cs +++ b/Content.Server/Antag/Components/AntagSelectionComponent.cs @@ -14,10 +14,16 @@ namespace Content.Server.Antag.Components; public sealed partial class AntagSelectionComponent : Component { /// - /// Has the primary selection of antagonists finished yet? + /// Has the primary assignment of antagonists finished yet? /// [DataField] - public bool SelectionsComplete; + public bool AssignmentComplete; + + /// + /// Has the antagonists been preselected but yet to be fully assigned? + /// + [DataField] + public bool PreSelectionsComplete; /// /// The definitions for the antagonists @@ -26,10 +32,10 @@ public sealed partial class AntagSelectionComponent : Component public List Definitions = new(); /// - /// The minds and original names of the players selected to be antagonists. + /// The minds and original names of the players assigned to be antagonists. /// [DataField] - public List<(EntityUid, string)> SelectedMinds = new(); + public List<(EntityUid, string)> AssignedMinds = new(); /// /// When the antag selection will occur. @@ -37,11 +43,17 @@ public sealed partial class AntagSelectionComponent : Component [DataField] public AntagSelectionTime SelectionTime = AntagSelectionTime.PostPlayerSpawn; + /// + /// Cached sessions of antag definitions and selected players. Players in this dict are not guaranteed to have been assigned the role yet. + /// + [DataField] + public Dictionary>PreSelectedSessions = new(); + /// /// 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(); + public HashSet AssignedSessions = new(); /// /// Locale id for the name of the antag. diff --git a/Content.Server/Forensics/Systems/ForensicsSystem.cs b/Content.Server/Forensics/Systems/ForensicsSystem.cs index a52b06039135b..27df086ae1b91 100644 --- a/Content.Server/Forensics/Systems/ForensicsSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicsSystem.cs @@ -73,7 +73,6 @@ private void OnFingerprintInit(Entity ent, ref MapInitEven private void OnDNAInit(Entity ent, ref MapInitEvent args) { - Log.Debug($"Init DNA {Name(ent.Owner)} {ent.Comp.DNA}"); if (ent.Comp.DNA == null) RandomizeDNA((ent.Owner, ent.Comp)); else @@ -327,7 +326,6 @@ public void RandomizeDNA(Entity ent) ent.Comp.DNA = GenerateDNA(); Dirty(ent); - Log.Debug($"Randomize DNA {Name(ent.Owner)} {ent.Comp.DNA}"); var ev = new GenerateDnaEvent { Owner = ent.Owner, DNA = ent.Comp.DNA }; RaiseLocalEvent(ent.Owner, ref ev); } diff --git a/Content.Server/GameTicking/Rules/Components/ParadoxCloneRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/ParadoxCloneRuleComponent.cs new file mode 100644 index 0000000000000..a6ffb4e66993b --- /dev/null +++ b/Content.Server/GameTicking/Rules/Components/ParadoxCloneRuleComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Cloning; +using Robust.Shared.Prototypes; + +namespace Content.Server.GameTicking.Rules.Components; + +/// +/// Gamerule component for spawning a paradox clone antagonist. +/// +[RegisterComponent] +public sealed partial class ParadoxCloneRuleComponent : Component +{ + /// + /// Cloning settings to be used. + /// + [DataField] + public ProtoId Settings = "BaseClone"; + + /// + /// Visual effect spawned when gibbing at round end. + /// + [DataField] + public EntProtoId GibProto = "MobParadoxTimed"; +} diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs index 5a5eb720cfb70..33ee91f8a5a5b 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs @@ -19,6 +19,11 @@ protected EntityQueryEnumerator Q return EntityQueryEnumerator(); } + protected EntityQueryEnumerator QueryDelayedRules() + { + return EntityQueryEnumerator(); + } + /// /// Queries all gamerules, regardless of if they're active or not. /// diff --git a/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs b/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs new file mode 100644 index 0000000000000..80fad7d2ef379 --- /dev/null +++ b/Content.Server/GameTicking/Rules/ParadoxCloneRuleSystem.cs @@ -0,0 +1,83 @@ +using Content.Server.Antag; +using Content.Server.Cloning; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Objectives.Components; +using Content.Shared.GameTicking.Components; +using Content.Shared.Gibbing.Components; +using Content.Shared.Mind; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server.GameTicking.Rules; + +public sealed class ParadoxCloneRuleSystem : GameRuleSystem +{ + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly CloningSystem _cloning = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAntagSelectEntity); + } + + protected override void Started(EntityUid uid, ParadoxCloneRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + // check if we got enough potential cloning targets, otherwise cancel the gamerule so that the ghost role does not show up + var allHumans = _mind.GetAliveHumans(); + + if (allHumans.Count == 0) + { + Log.Info("Could not find any alive players to create a paradox clone from! Ending gamerule."); + ForceEndSelf(uid, gameRule); + } + } + + // we have to do the spawning here so we can transfer the mind to the correct entity and can assign the objectives correctly + private void OnAntagSelectEntity(Entity ent, ref AntagSelectEntityEvent args) + { + if (args.Session?.AttachedEntity is not { } spawner) + return; + + if (!_prototypeManager.TryIndex(ent.Comp.Settings, out var settings)) + { + Log.Error($"Used invalid cloning settings {ent.Comp.Settings} for ParadoxCloneRule"); + return; + } + + // get possible targets + var allHumans = _mind.GetAliveHumans(); + + // we already checked when starting the gamerule, but someone might have died since then. + if (allHumans.Count == 0) + { + Log.Warning("Could not find any alive players to create a paradox clone from!"); + return; + } + + // pick a random player + var playerToClone = _random.Pick(allHumans); + var bodyToClone = playerToClone.Comp.OwnedEntity; + + if (bodyToClone == null || !_cloning.TryCloning(bodyToClone.Value, _transform.GetMapCoordinates(spawner), settings, out var clone)) + { + Log.Error($"Unable to make a paradox clone of entity {ToPrettyString(bodyToClone)}"); + return; + } + + var targetComp = EnsureComp(clone.Value); + targetComp.Target = playerToClone.Owner; // set the kill target + + var gibComp = EnsureComp(clone.Value); + gibComp.SpawnProto = ent.Comp.GibProto; + gibComp.PreventGibbingObjectives = new() { "ParadoxCloneKillObjective" }; // don't gib them if they killed the original. + + args.Entity = clone; + } +} diff --git a/Content.Server/Gibbing/Systems/GibOnRoundEndSystem.cs b/Content.Server/Gibbing/Systems/GibOnRoundEndSystem.cs new file mode 100644 index 0000000000000..d23832984f806 --- /dev/null +++ b/Content.Server/Gibbing/Systems/GibOnRoundEndSystem.cs @@ -0,0 +1,55 @@ +using Content.Shared.GameTicking; +using Content.Shared.Gibbing.Components; +using Content.Shared.Mind; +using Content.Shared.Objectives.Systems; +using Content.Server.Body.Systems; + +namespace Content.Server.Gibbing.Systems; +public sealed class GibOnRoundEndSystem : EntitySystem +{ + [Dependency] private readonly BodySystem _body = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedObjectivesSystem _objectives = default!; + + public override void Initialize() + { + base.Initialize(); + + // this is raised after RoundEndTextAppendEvent, so they can successfully greentext before we gib them + SubscribeLocalEvent(OnRoundEnd); + } + + private void OnRoundEnd(RoundEndMessageEvent args) + { + var gibQuery = EntityQueryEnumerator(); + + // gib everyone with the component + while (gibQuery.MoveNext(out var uid, out var gibComp)) + { + var gib = false; + // if they fulfill all objectives given in the component they are not gibbed + if (_mind.TryGetMind(uid, out var mindId, out var mindComp)) + { + foreach (var objectiveId in gibComp.PreventGibbingObjectives) + { + if (!_mind.TryFindObjective((mindId, mindComp), objectiveId, out var objective) + || !_objectives.IsCompleted(objective.Value, (mindId, mindComp))) + { + gib = true; + break; + } + } + } + else + gib = true; + + if (!gib) + continue; + + if (gibComp.SpawnProto != null) + SpawnAtPosition(gibComp.SpawnProto, Transform(uid).Coordinates); + + _body.GibBody(uid, splatModifier: 5f); + } + } +} diff --git a/Content.Server/Objectives/Components/PickRandomHeadComponent.cs b/Content.Server/Objectives/Components/PickRandomHeadComponent.cs index c2f82fb6c5945..38ed2522646f5 100644 --- a/Content.Server/Objectives/Components/PickRandomHeadComponent.cs +++ b/Content.Server/Objectives/Components/PickRandomHeadComponent.cs @@ -1,12 +1,8 @@ -using Content.Server.Objectives.Systems; - namespace Content.Server.Objectives.Components; /// /// Sets the target for to a random head. /// If there are no heads it will fallback to any person. /// -[RegisterComponent, Access(typeof(KillPersonConditionSystem))] -public sealed partial class PickRandomHeadComponent : Component -{ -} +[RegisterComponent] +public sealed partial class PickRandomHeadComponent : Component; diff --git a/Content.Server/Objectives/Components/PickRandomPersonComponent.cs b/Content.Server/Objectives/Components/PickRandomPersonComponent.cs index 4188b1da3d250..bf4135e2a9b34 100644 --- a/Content.Server/Objectives/Components/PickRandomPersonComponent.cs +++ b/Content.Server/Objectives/Components/PickRandomPersonComponent.cs @@ -1,11 +1,7 @@ -using Content.Server.Objectives.Systems; - namespace Content.Server.Objectives.Components; /// /// Sets the target for to a random person. /// -[RegisterComponent, Access(typeof(KillPersonConditionSystem))] -public sealed partial class PickRandomPersonComponent : Component -{ -} +[RegisterComponent] +public sealed partial class PickRandomPersonComponent : Component; diff --git a/Content.Server/Objectives/Components/PickSpecificPersonComponent.cs b/Content.Server/Objectives/Components/PickSpecificPersonComponent.cs new file mode 100644 index 0000000000000..03a482fc7b1e5 --- /dev/null +++ b/Content.Server/Objectives/Components/PickSpecificPersonComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Server.Objectives.Components; + +/// +/// Sets this objective's target to the one given in , if the entity has it. +/// This component needs to be added to objective entity itself. +/// +[RegisterComponent] +public sealed partial class PickSpecificPersonComponent : Component; diff --git a/Content.Server/Objectives/Components/RandomTraitorAliveComponent.cs b/Content.Server/Objectives/Components/RandomTraitorAliveComponent.cs index fd37d0d2c849a..1c45cb45d5abb 100644 --- a/Content.Server/Objectives/Components/RandomTraitorAliveComponent.cs +++ b/Content.Server/Objectives/Components/RandomTraitorAliveComponent.cs @@ -1,11 +1,7 @@ -using Content.Server.Objectives.Systems; - namespace Content.Server.Objectives.Components; /// /// Sets the target for to a random traitor. /// -[RegisterComponent, Access(typeof(KeepAliveConditionSystem))] -public sealed partial class RandomTraitorAliveComponent : Component -{ -} +[RegisterComponent] +public sealed partial class RandomTraitorAliveComponent : Component; diff --git a/Content.Server/Objectives/Components/RandomTraitorProgressComponent.cs b/Content.Server/Objectives/Components/RandomTraitorProgressComponent.cs index c05ac0d3efee2..f2da9778eb74b 100644 --- a/Content.Server/Objectives/Components/RandomTraitorProgressComponent.cs +++ b/Content.Server/Objectives/Components/RandomTraitorProgressComponent.cs @@ -1,11 +1,7 @@ -using Content.Server.Objectives.Systems; - namespace Content.Server.Objectives.Components; /// /// Sets the target for to a random traitor. /// -[RegisterComponent, Access(typeof(HelpProgressConditionSystem))] -public sealed partial class RandomTraitorProgressComponent : Component -{ -} +[RegisterComponent] +public sealed partial class RandomTraitorProgressComponent : Component; diff --git a/Content.Server/Objectives/Components/TargetOverrideComponent.cs b/Content.Server/Objectives/Components/TargetOverrideComponent.cs new file mode 100644 index 0000000000000..faa402012c20b --- /dev/null +++ b/Content.Server/Objectives/Components/TargetOverrideComponent.cs @@ -0,0 +1,16 @@ +namespace Content.Server.Objectives.Components; + +/// +/// Sets a target objective to a specific target when receiving it. +/// The objective entity needs to have . +/// This component needs to be added to entity receiving the objective. +/// +[RegisterComponent] +public sealed partial class TargetOverrideComponent : Component +{ + /// + /// The entity that should be targeted. + /// + [DataField] + public EntityUid? Target; +} diff --git a/Content.Server/Objectives/Systems/HelpProgressConditionSystem.cs b/Content.Server/Objectives/Systems/HelpProgressConditionSystem.cs index 7639e69bfdaa3..1816896ecc9b5 100644 --- a/Content.Server/Objectives/Systems/HelpProgressConditionSystem.cs +++ b/Content.Server/Objectives/Systems/HelpProgressConditionSystem.cs @@ -1,31 +1,23 @@ -using Content.Server.GameTicking.Rules; using Content.Server.Objectives.Components; using Content.Shared.Mind; using Content.Shared.Objectives.Components; using Content.Shared.Objectives.Systems; -using Content.Shared.Roles.Jobs; -using Robust.Shared.Random; -using System.Linq; namespace Content.Server.Objectives.Systems; /// -/// Handles help progress condition logic and picking random help targets. +/// Handles help progress condition logic. /// public sealed class HelpProgressConditionSystem : EntitySystem { - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedObjectivesSystem _objectives = default!; [Dependency] private readonly TargetObjectiveSystem _target = default!; - [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnGetProgress); - - SubscribeLocalEvent(OnTraitorAssigned); } private void OnGetProgress(EntityUid uid, HelpProgressConditionComponent comp, ref ObjectiveGetProgressEvent args) @@ -36,55 +28,6 @@ private void OnGetProgress(EntityUid uid, HelpProgressConditionComponent comp, r args.Progress = GetProgress(target.Value); } - private void OnTraitorAssigned(EntityUid uid, RandomTraitorProgressComponent comp, ref ObjectiveAssignedEvent args) - { - // invalid prototype - if (!TryComp(uid, out var target)) - { - args.Cancelled = true; - return; - } - - var traitors = _traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind).ToHashSet(); - - // cant help anyone who is tasked with helping: - // 1. thats boring - // 2. no cyclic progress dependencies!!! - foreach (var traitor in traitors) - { - // TODO: replace this with TryComp(traitor) or something when objectives are moved out of mind - if (!TryComp(traitor.Id, out var mind)) - continue; - - foreach (var objective in mind.Objectives) - { - if (HasComp(objective)) - traitors.RemoveWhere(x => x.Mind == mind); - } - } - - // Can't have multiple objectives to help/save the same person - foreach (var objective in args.Mind.Objectives) - { - if (HasComp(objective) || HasComp(objective)) - { - if (TryComp(objective, out var help)) - { - traitors.RemoveWhere(x => x.Id == help.Target); - } - } - } - - // no more helpable traitors - if (traitors.Count == 0) - { - args.Cancelled = true; - return; - } - - _target.SetTarget(uid, _random.Pick(traitors).Id, target); - } - private float GetProgress(EntityUid target) { var total = 0f; // how much progress they have diff --git a/Content.Server/Objectives/Systems/KeepAliveCondition.cs b/Content.Server/Objectives/Systems/KeepAliveCondition.cs index fad8aa6d18e15..f68227e861a76 100644 --- a/Content.Server/Objectives/Systems/KeepAliveCondition.cs +++ b/Content.Server/Objectives/Systems/KeepAliveCondition.cs @@ -1,30 +1,22 @@ using Content.Server.Objectives.Components; -using Content.Server.GameTicking.Rules; using Content.Shared.Mind; using Content.Shared.Objectives.Components; -using Content.Shared.Roles.Jobs; -using Robust.Shared.Random; -using System.Linq; namespace Content.Server.Objectives.Systems; /// -/// Handles keep alive condition logic and picking random traitors to keep alive. +/// Handles keep alive condition logic. /// public sealed class KeepAliveConditionSystem : EntitySystem { - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly TargetObjectiveSystem _target = default!; - [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnGetProgress); - - SubscribeLocalEvent(OnAssigned); } private void OnGetProgress(EntityUid uid, KeepAliveConditionComponent comp, ref ObjectiveGetProgressEvent args) @@ -35,39 +27,6 @@ private void OnGetProgress(EntityUid uid, KeepAliveConditionComponent comp, ref args.Progress = GetProgress(target.Value); } - private void OnAssigned(EntityUid uid, RandomTraitorAliveComponent comp, ref ObjectiveAssignedEvent args) - { - // invalid prototype - if (!TryComp(uid, out var target)) - { - args.Cancelled = true; - return; - } - - var traitors = _traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind).ToHashSet(); - - // Can't have multiple objectives to help/save the same person - foreach (var objective in args.Mind.Objectives) - { - if (HasComp(objective) || HasComp(objective)) - { - if (TryComp(objective, out var help)) - { - traitors.RemoveWhere(x => x.Id == help.Target); - } - } - } - - // You are the first/only traitor. - if (traitors.Count == 0) - { - args.Cancelled = true; - return; - } - - _target.SetTarget(uid, _random.Pick(traitors).Id, target); - } - private float GetProgress(EntityUid target) { if (!TryComp(target, out var mind)) diff --git a/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs b/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs index 3aa4d606fc3bd..45ad68e28b6c1 100644 --- a/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs +++ b/Content.Server/Objectives/Systems/KillPersonConditionSystem.cs @@ -1,12 +1,9 @@ using Content.Server.Objectives.Components; -using Content.Server.Revolutionary.Components; using Content.Server.Shuttles.Systems; using Content.Shared.CCVar; using Content.Shared.Mind; using Content.Shared.Objectives.Components; using Robust.Shared.Configuration; -using Robust.Shared.Random; -using System.Linq; namespace Content.Server.Objectives.Systems; @@ -17,7 +14,6 @@ public sealed class KillPersonConditionSystem : EntitySystem { [Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!; [Dependency] private readonly IConfigurationManager _config = default!; - [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly TargetObjectiveSystem _target = default!; @@ -26,10 +22,6 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnGetProgress); - - SubscribeLocalEvent(OnPersonAssigned); - - SubscribeLocalEvent(OnHeadAssigned); } private void OnGetProgress(EntityUid uid, KillPersonConditionComponent comp, ref ObjectiveGetProgressEvent args) @@ -40,74 +32,6 @@ private void OnGetProgress(EntityUid uid, KillPersonConditionComponent comp, ref args.Progress = GetProgress(target.Value, comp.RequireDead); } - private void OnPersonAssigned(EntityUid uid, PickRandomPersonComponent comp, ref ObjectiveAssignedEvent args) - { - // invalid objective prototype - if (!TryComp(uid, out var target)) - { - args.Cancelled = true; - return; - } - - // target already assigned - if (target.Target != null) - return; - - var allHumans = _mind.GetAliveHumans(args.MindId); - - // Can't have multiple objectives to kill the same person - foreach (var objective in args.Mind.Objectives) - { - if (HasComp(objective) && TryComp(objective, out var kill)) - { - allHumans.RemoveWhere(x => x.Owner == kill.Target); - } - } - - // no other humans to kill - if (allHumans.Count == 0) - { - args.Cancelled = true; - return; - } - - _target.SetTarget(uid, _random.Pick(allHumans), target); - } - - private void OnHeadAssigned(EntityUid uid, PickRandomHeadComponent comp, ref ObjectiveAssignedEvent args) - { - // invalid prototype - if (!TryComp(uid, out var target)) - { - args.Cancelled = true; - return; - } - - // target already assigned - if (target.Target != null) - return; - - // no other humans to kill - var allHumans = _mind.GetAliveHumans(args.MindId); - if (allHumans.Count == 0) - { - args.Cancelled = true; - return; - } - - var allHeads = new HashSet>(); - foreach (var person in allHumans) - { - if (TryComp(person, out var mind) && mind.OwnedEntity is { } ent && HasComp(ent)) - allHeads.Add(person); - } - - if (allHeads.Count == 0) - allHeads = allHumans; // fallback to non-head target - - _target.SetTarget(uid, _random.Pick(allHeads), target); - } - private float GetProgress(EntityUid target, bool requireDead) { // deleted or gibbed or something, counts as dead diff --git a/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs b/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs new file mode 100644 index 0000000000000..2977e10569d31 --- /dev/null +++ b/Content.Server/Objectives/Systems/PickObjectiveTargetSystem.cs @@ -0,0 +1,212 @@ +using Content.Server.Objectives.Components; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; +using Content.Server.GameTicking.Rules; +using Content.Server.Revolutionary.Components; +using Robust.Shared.Random; +using System.Linq; + +namespace Content.Server.Objectives.Systems; + +/// +/// Handles assinging a target to an objective entity with using different components. +/// These can be combined with condition components for objective completions in order to create a variety of objectives. +/// +public sealed class PickObjectiveTargetSystem : EntitySystem +{ + [Dependency] private readonly TargetObjectiveSystem _target = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly TraitorRuleSystem _traitorRule = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnSpecificPersonAssigned); + SubscribeLocalEvent(OnRandomPersonAssigned); + SubscribeLocalEvent(OnRandomHeadAssigned); + + SubscribeLocalEvent(OnRandomTraitorProgressAssigned); + SubscribeLocalEvent(OnRandomTraitorAliveAssigned); + } + + private void OnSpecificPersonAssigned(Entity ent, ref ObjectiveAssignedEvent args) + { + // invalid objective prototype + if (!TryComp(ent.Owner, out var target)) + { + args.Cancelled = true; + return; + } + + // target already assigned + if (target.Target != null) + return; + + if (args.Mind.OwnedEntity == null) + { + args.Cancelled = true; + return; + } + + var user = args.Mind.OwnedEntity.Value; + if (!TryComp(user, out var targetComp) || targetComp.Target == null) + { + args.Cancelled = true; + return; + } + + _target.SetTarget(ent.Owner, targetComp.Target.Value); + } + + private void OnRandomPersonAssigned(Entity ent, ref ObjectiveAssignedEvent args) + { + // invalid objective prototype + if (!TryComp(ent.Owner, out var target)) + { + args.Cancelled = true; + return; + } + + // target already assigned + if (target.Target != null) + return; + + var allHumans = _mind.GetAliveHumans(args.MindId); + + // Can't have multiple objectives to kill the same person + foreach (var objective in args.Mind.Objectives) + { + if (HasComp(objective) && TryComp(objective, out var kill)) + { + allHumans.RemoveWhere(x => x.Owner == kill.Target); + } + } + + // no other humans to kill + if (allHumans.Count == 0) + { + args.Cancelled = true; + return; + } + + _target.SetTarget(ent.Owner, _random.Pick(allHumans), target); + } + + private void OnRandomHeadAssigned(Entity ent, ref ObjectiveAssignedEvent args) + { + // invalid prototype + if (!TryComp(ent.Owner, out var target)) + { + args.Cancelled = true; + return; + } + + // target already assigned + if (target.Target != null) + return; + + // no other humans to kill + var allHumans = _mind.GetAliveHumans(args.MindId); + if (allHumans.Count == 0) + { + args.Cancelled = true; + return; + } + + var allHeads = new HashSet>(); + foreach (var person in allHumans) + { + if (TryComp(person, out var mind) && mind.OwnedEntity is { } owned && HasComp(owned)) + allHeads.Add(person); + } + + if (allHeads.Count == 0) + allHeads = allHumans; // fallback to non-head target + + _target.SetTarget(ent.Owner, _random.Pick(allHeads), target); + } + + private void OnRandomTraitorProgressAssigned(Entity ent, ref ObjectiveAssignedEvent args) + { + // invalid prototype + if (!TryComp(ent.Owner, out var target)) + { + args.Cancelled = true; + return; + } + + var traitors = _traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind).ToHashSet(); + + // cant help anyone who is tasked with helping: + // 1. thats boring + // 2. no cyclic progress dependencies!!! + foreach (var traitor in traitors) + { + // TODO: replace this with TryComp(traitor) or something when objectives are moved out of mind + if (!TryComp(traitor.Id, out var mind)) + continue; + + foreach (var objective in mind.Objectives) + { + if (HasComp(objective)) + traitors.RemoveWhere(x => x.Mind == mind); + } + } + + // Can't have multiple objectives to help/save the same person + foreach (var objective in args.Mind.Objectives) + { + if (HasComp(objective) || HasComp(objective)) + { + if (TryComp(objective, out var help)) + { + traitors.RemoveWhere(x => x.Id == help.Target); + } + } + } + + // no more helpable traitors + if (traitors.Count == 0) + { + args.Cancelled = true; + return; + } + + _target.SetTarget(ent.Owner, _random.Pick(traitors).Id, target); + } + + private void OnRandomTraitorAliveAssigned(Entity ent, ref ObjectiveAssignedEvent args) + { + // invalid prototype + if (!TryComp(ent.Owner, out var target)) + { + args.Cancelled = true; + return; + } + + var traitors = _traitorRule.GetOtherTraitorMindsAliveAndConnected(args.Mind).ToHashSet(); + + // Can't have multiple objectives to help/save the same person + foreach (var objective in args.Mind.Objectives) + { + if (HasComp(objective) || HasComp(objective)) + { + if (TryComp(objective, out var help)) + { + traitors.RemoveWhere(x => x.Id == help.Target); + } + } + } + + // You are the first/only traitor. + if (traitors.Count == 0) + { + args.Cancelled = true; + return; + } + + _target.SetTarget(ent.Owner, _random.Pick(traitors).Id, target); + } +} diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs index 8adab1e00db41..e305109ad54e2 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs @@ -204,6 +204,12 @@ private void OnDestruction(Entity ent, ref Destructi var child = Spawn(configuration.Entity, _transform.GetMapCoordinates(uid, targetTransformComp), rotation: _transform.GetWorldRotation(uid)); + if (configuration.PolymorphPopup != null) + _popup.PopupEntity(Loc.GetString(configuration.PolymorphPopup, + ("parent", Identity.Entity(uid, EntityManager)), + ("child", Identity.Entity(child, EntityManager))), + child); + MakeSentientCommand.MakeSentient(child, EntityManager); var polymorphedComp = _compFact.GetComponent(); @@ -347,10 +353,11 @@ private void OnDestruction(Entity ent, ref Destructi var ev = new PolymorphedEvent(uid, parent, true); RaiseLocalEvent(uid, ref ev); - _popup.PopupEntity(Loc.GetString("polymorph-revert-popup-generic", + if (component.Configuration.ExitPolymorphPopup != null) + _popup.PopupEntity(Loc.GetString(component.Configuration.ExitPolymorphPopup, ("parent", Identity.Entity(uid, EntityManager)), ("child", Identity.Entity(parent, EntityManager))), - parent); + parent); QueueDel(uid); return parent; diff --git a/Content.Server/Roles/ParadoxCloneRoleComponent.cs b/Content.Server/Roles/ParadoxCloneRoleComponent.cs new file mode 100644 index 0000000000000..d55cd34da4e35 --- /dev/null +++ b/Content.Server/Roles/ParadoxCloneRoleComponent.cs @@ -0,0 +1,9 @@ +using Content.Shared.Roles; + +namespace Content.Server.Roles; + +/// +/// Added to mind role entities to tag that they are a paradox clone. +/// +[RegisterComponent] +public sealed partial class ParadoxCloneRoleComponent : BaseMindRoleComponent; diff --git a/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs b/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs index 8a918bd2fd045..8045cfed462aa 100644 --- a/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs +++ b/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs @@ -1,10 +1,12 @@ using System.Linq; using Content.Server.Administration.Managers; +using Content.Server.Antag; using Content.Server.Players.PlayTimeTracking; using Content.Server.Station.Components; using Content.Server.Station.Events; using Content.Shared.Preferences; using Content.Shared.Roles; +using Robust.Server.Player; using Robust.Shared.Network; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -17,6 +19,8 @@ public sealed partial class StationJobsSystem { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IBanManager _banManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly AntagSelectionSystem _antag = default!; private Dictionary> _jobsByWeight = default!; private List _orderedWeights = default!; @@ -345,6 +349,7 @@ private Dictionary> GetPlayersJobCandidates(int? weight, foreach (var (player, profile) in profiles) { var roleBans = _banManager.GetJobBans(player); + var antagBlocked = _antag.GetPreSelectedAntagSessions(); var profileJobs = profile.JobPriorities.Keys.Select(k => new ProtoId(k)).ToList(); var ev = new StationJobsGetCandidatesEvent(player, profileJobs); RaiseLocalEvent(ref ev); @@ -361,6 +366,9 @@ private Dictionary> GetPlayersJobCandidates(int? weight, if (!_prototypeManager.TryIndex(jobId, out var job)) continue; + if (!job.CanBeAntag && (!_playerManager.TryGetSessionById(player, out var session) || antagBlocked.Contains(session))) + continue; + if (weight is not null && job.Weight != weight.Value) continue; diff --git a/Content.Shared.Database/LogType.cs b/Content.Shared.Database/LogType.cs index 5ebb100dafd24..a868d0e384269 100644 --- a/Content.Shared.Database/LogType.cs +++ b/Content.Shared.Database/LogType.cs @@ -449,9 +449,14 @@ public enum LogType /// An atmos networked device (such as a vent or pump) has had its settings changed, usually through an air alarm /// AtmosDeviceSetting = 97, - + /// /// Commands related to admemes. Stuff like config changes, etc. /// AdminCommands = 98, + + /// + /// A player was selected or assigned antag status + /// + AntagSelection = 99, } diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs index 3f8da9e3c8bec..0d7c926f76397 100644 --- a/Content.Shared/Anomaly/SharedAnomalySystem.cs +++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs @@ -163,7 +163,7 @@ public void DoAnomalySupercriticalEvent(EntityUid uid, AnomalyComponent? compone var ev = new AnomalySupercriticalEvent(uid, powerMod); RaiseLocalEvent(uid, ref ev, true); - EndAnomaly(uid, component, true); + EndAnomaly(uid, component, true, logged: true); } /// @@ -173,13 +173,17 @@ public void DoAnomalySupercriticalEvent(EntityUid uid, AnomalyComponent? compone /// /// Whether or not the anomaly ended via supercritical event /// Create anomaly cores based on the result of completing an anomaly? - public void EndAnomaly(EntityUid uid, AnomalyComponent? component = null, bool supercritical = false, bool spawnCore = true) + /// Whether or not the anomaly decaying/going supercritical is logged + public void EndAnomaly(EntityUid uid, AnomalyComponent? component = null, bool supercritical = false, bool spawnCore = true, bool logged = false) { - // Logging before resolve, in case the anomaly has deleted itself. - if (_net.IsServer) - Log.Info($"Ending anomaly. Entity: {ToPrettyString(uid)}"); - AdminLog.Add(LogType.Anomaly, supercritical ? LogImpact.High : LogImpact.Low, - $"Anomaly {ToPrettyString(uid)} {(supercritical ? "went supercritical" : "decayed")}."); + if (logged) + { + // Logging before resolve, in case the anomaly has deleted itself. + if (_net.IsServer) + Log.Info($"Ending anomaly. Entity: {ToPrettyString(uid)}"); + AdminLog.Add(LogType.Anomaly, supercritical ? LogImpact.High : LogImpact.Low, + $"Anomaly {ToPrettyString(uid)} {(supercritical ? "went supercritical" : "decayed")}."); + } if (!Resolve(uid, ref component)) return; @@ -260,7 +264,7 @@ public void ChangeAnomalyHealth(EntityUid uid, float change, AnomalyComponent? c if (newVal < 0) { - EndAnomaly(uid, component); + EndAnomaly(uid, component, logged: true); return; } diff --git a/Content.Shared/Antag/AntagAcceptability.cs b/Content.Shared/Antag/AntagAcceptability.cs index f56be97503353..33323aacf37c2 100644 --- a/Content.Shared/Antag/AntagAcceptability.cs +++ b/Content.Shared/Antag/AntagAcceptability.cs @@ -17,7 +17,7 @@ public enum AntagAcceptability /// /// Choose anyone /// - All + All, } public enum AntagSelectionTime : byte @@ -28,8 +28,14 @@ public enum AntagSelectionTime : byte /// PrePlayerSpawn, + /// + /// Antag roles are selected to the player session before job assignment and spawning. + /// Unlike PrePlayerSpawn, this does not remove you from the job spawn pool. + /// + IntraPlayerSpawn, + /// /// Antag roles get assigned after players have been assigned jobs and have spawned in. /// - PostPlayerSpawn + PostPlayerSpawn, } diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index 1745730647d0f..3d2d263652a4b 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -416,7 +416,7 @@ public bool TryUnbuckle(EntityUid buckleUid, public bool TryUnbuckle(Entity buckle, EntityUid? user, bool popup) { - if (!Resolve(buckle.Owner, ref buckle.Comp)) + if (!Resolve(buckle.Owner, ref buckle.Comp, false)) return false; if (!CanUnbuckle(buckle, user, popup, out var strap)) diff --git a/Content.Shared/Gibbing/Components/GibOnRoundEndComponent.cs b/Content.Shared/Gibbing/Components/GibOnRoundEndComponent.cs new file mode 100644 index 0000000000000..5ce8edbd25dc6 --- /dev/null +++ b/Content.Shared/Gibbing/Components/GibOnRoundEndComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Gibbing.Components; + +/// +/// Gibs an entity on round end. +/// +[RegisterComponent] +public sealed partial class GibOnRoundEndComponent : Component +{ + /// + /// If the entity has all these objectives fulfilled they won't be gibbed. + /// + [DataField] + public HashSet PreventGibbingObjectives = new(); + + /// + /// Entity to spawn when gibbed. Can be used for effects. + /// + [DataField] + public EntProtoId? SpawnProto; +} diff --git a/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs b/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs index a736a63cb227d..43244a5f0df33 100644 --- a/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs +++ b/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs @@ -1,4 +1,4 @@ -using Robust.Shared.Containers; +using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; @@ -16,7 +16,7 @@ public sealed partial class CryoPodComponent : Component public string PortName { get; set; } = "port"; /// - /// Specifies the name of the atmospherics port to draw gas from. + /// Specifies the name of the slot that holds beaker with medicine. /// [ViewVariables(VVAccess.ReadWrite)] [DataField("solutionContainerName")] diff --git a/Content.Shared/Polymorph/PolymorphPrototype.cs b/Content.Shared/Polymorph/PolymorphPrototype.cs index c05766940840a..07901b1857f0a 100644 --- a/Content.Shared/Polymorph/PolymorphPrototype.cs +++ b/Content.Shared/Polymorph/PolymorphPrototype.cs @@ -128,6 +128,18 @@ public sealed partial record PolymorphConfiguration /// [DataField] public SoundSpecifier? ExitPolymorphSound; + + /// + /// If not null, this popup will be displayed when being polymorphed into something. + /// + [DataField] + public LocId? PolymorphPopup = "polymorph-popup-generic"; + + /// + /// If not null, this popup will be displayed when when being reverted from a polymorph. + /// + [DataField] + public LocId? ExitPolymorphPopup = "polymorph-revert-popup-generic"; } public enum PolymorphInventoryChange : byte diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 231bac8302fe3..ac6f27f7e9a5f 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -350,10 +350,7 @@ private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) // If they're firing an existing clip then don't play anything. if (shots > 0) { - if (ev.Reason != null && Timing.IsFirstTimePredicted) - { - PopupSystem.PopupCursor(ev.Reason); - } + PopupSystem.PopupCursor(ev.Reason ?? Loc.GetString("gun-magazine-fired-empty")); // Don't spam safety sounds at gun fire rate, play it at a reduced rate. // May cause prediction issues? Needs more tweaking diff --git a/Resources/Audio/Lobby/attributions.yml b/Resources/Audio/Lobby/attributions.yml index 722f70731afb3..ab96eb41a1b2c 100644 --- a/Resources/Audio/Lobby/attributions.yml +++ b/Resources/Audio/Lobby/attributions.yml @@ -12,7 +12,7 @@ license: "Custom" copyright: "Space Asshole by Chris Remo is used with special permission from the author, under the condition that the project remains non-commercial and open source. The author also requested that a link to his bandcamp be included: https://chrisremo.bandcamp.com/" source: "https://idlethumbs.bandcamp.com/track/space-asshole" - # The source is a direct link the the track, but not the "main" bandcamp of the author. Hence the link is also included separately in the copyright. + # The source is a direct link to the track, but not the "main" bandcamp of the author. Hence the link is also included separately in the copyright. - files: ["the_wizard.ogg"] license: "Custom" @@ -49,7 +49,7 @@ - files: ["comet_haley.ogg"] license: "CC-BY-NC-SA-3.0" copyright: "Comet Haley by Stellardrone. Converted from MP3 to OGG." - source: "https://freemusicarchive.org/music/Stellardrone/Light_Years_1227/07_Comet_Halley" + source: "https://stellardrone.bandcamp.com/track/comet-halley" - files: ["mod.flip-flap.ogg"] license: "Custom" @@ -63,15 +63,15 @@ - files: ["pwmur.ogg"] license: "CC-BY-NC-SA-3.0" - copyright: "phoron will make us rich by Alexander Divine." - source: "https://soundcloud.com/alexanderdivine/phoron-will-make-us-rich" + copyright: "phoron will make us rich by Sunbeamstress/Lauren Loveless." + source: "https://soundcloud.com/sunbeamstress/phoron-will-make-us-rich" - files: ["lasers_rip_apart_the_bulkhead.ogg"] license: "CC-BY-NC-SA-3.0" - copyright: "lasers rip apart the bulkhead by Alexander Divine." - source: "https://soundcloud.com/alexanderdivine/lasers-rip-apart-the-bulkhead" + copyright: "lasers rip apart the bulkhead by Sunbeamstress/Lauren Loveless." + source: "https://soundcloud.com/sunbeamstress/lasers-rip-apart-the-bulkhead" - files: ["every_light_is_blinking_at_once.ogg"] license: "CC-BY-NC-SA-3.0" - copyright: "every light is blinking at once by Alexander Divine." - source: "https://soundcloud.com/alexanderdivine/every-light-is-blinking-at-once" + copyright: "every light is blinking at once by Sunbeamstress/Lauren Loveless." + source: "https://soundcloud.com/sunbeamstress/every-light-is-blinking-at-once" diff --git a/Resources/Audio/Misc/attributions.yml b/Resources/Audio/Misc/attributions.yml index a7349a445be63..67daaff404203 100644 --- a/Resources/Audio/Misc/attributions.yml +++ b/Resources/Audio/Misc/attributions.yml @@ -8,6 +8,11 @@ copyright: "Created by Chillyconmor" source: "https://github.com/space-wizards/space-station-14/blob/master/Resources/Audio/Misc/ninja_greeting.ogg" +- files: ["nparadox_clone_greeting.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Created by ps3moira" + source: "https://github.com/space-wizards/space-station-14/blob/master/Resources/Audio/Misc/ninja_greeting.ogg" + - files: ["thief_greeting.ogg"] license: "CC-BY-NC-4.0" copyright: "Taken from SergeQuadrado via freesound.org, edit and mono by TheShuEd" diff --git a/Resources/Audio/Misc/paradox_clone_greeting.ogg b/Resources/Audio/Misc/paradox_clone_greeting.ogg new file mode 100644 index 0000000000000..deffca12e6d09 Binary files /dev/null and b/Resources/Audio/Misc/paradox_clone_greeting.ogg differ diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c4c465cdabc10..8e88fefc1209b 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,62 +1,4 @@ Entries: -- author: BramvanZijp - changes: - - message: Fixed the Lone Nuclear Operative mid-round antagonist being extremely - rare. - type: Fix - id: 7553 - time: '2024-10-26T02:16:45.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32942 -- author: Moomoobeef - changes: - - message: Bowls no longer make an eating sound when drinking from them. - type: Fix - id: 7554 - time: '2024-10-26T04:00:49.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32819 -- author: SaphireLattice - changes: - - message: Added a warning about unrevivability in the health analyzer UI. - type: Add - id: 7555 - time: '2024-10-26T17:22:09.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32636 -- author: slarticodefast - changes: - - message: Fixed pie throwing sound not playing. - type: Fix - id: 7556 - time: '2024-10-27T04:25:55.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33017 -- author: stalengd - changes: - - message: Fixed playtime labels not being able to correctly display time greater - than 24 hours - type: Fix - id: 7557 - time: '2024-10-28T18:00:00.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32974 -- author: august-sun - changes: - - message: Extended the minimum round time for meteor swarm events. - type: Tweak - id: 7558 - time: '2024-10-28T21:25:34.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32876 -- author: deltanedas - changes: - - message: Fixed lava planet expeditions not working. - type: Fix - id: 7559 - time: '2024-10-29T05:00:29.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33042 -- author: metalgearsloth - changes: - - message: Fix separated game screen bumping slightly. - type: Fix - id: 7560 - time: '2024-10-29T05:07:57.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33046 - author: Blackern5000 changes: - message: Proto-kitentic crushers, glaives, and daggers now have more accurate @@ -3890,3 +3832,67 @@ id: 8052 time: '2025-03-12T01:21:59.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/35751 +- author: Winkarst-cpu + changes: + - message: The warden job now rolls before security officer/cadet/detective. + type: Tweak + id: 8053 + time: '2025-03-12T22:36:41.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35313 +- author: Firewars763 + changes: + - message: Added gold and coal rock anomalies and ore crab enemies. + type: Add + - message: Added yellow and black crystals, crystal shards, and light tubes. + type: Add + id: 8054 + time: '2025-03-12T22:45:22.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/34809 +- author: onesch + changes: + - message: Added handheld sprites to some items + type: Add + id: 8055 + time: '2025-03-12T22:46:58.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/33689 +- author: K-Dynamic + changes: + - message: Nitrous oxide canisters now begin locked like other dangerous gas canisters. + type: Fix + id: 8056 + time: '2025-03-13T00:30:17.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35785 +- author: Plykiya + changes: + - message: A message now pops up when attempting to fire a gun with no ammo. + type: Add + id: 8057 + time: '2025-03-13T07:21:24.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/34816 +- author: Crude Oil + changes: + - message: You can now pet the mail teleporter + type: Add + id: 8058 + time: '2025-03-13T12:49:25.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35819 +- author: ScarKy0 + changes: + - message: Singularity and Whitehole grenades are now in the Disruption category + of uplinks. + type: Tweak + - message: Singularity and Whitehole grenades have had their prices reduced to 2TC + each. + type: Tweak + - message: Whitehole grenade no longer has extremely overtuned strength. + type: Fix + id: 8059 + time: '2025-03-13T13:12:27.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35821 +- author: slarticodefast, ps3moira + changes: + - message: Added the paradox clone as a ghostrole antagonist. + type: Add + id: 8060 + time: '2025-03-13T17:09:07.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35794 diff --git a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl index b2f0932787676..3e89f912a9971 100644 --- a/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl +++ b/Resources/Locale/en-US/ghost/roles/ghost-role-component.ftl @@ -211,6 +211,9 @@ ghost-role-information-BreadDog-description = You are the chef's favorite child. ghost-role-information-space-ninja-name = Space Ninja ghost-role-information-space-ninja-description = Use stealth and deception to sabotage the station. +ghost-role-information-paradox-clone-name = Paradox Clone +ghost-role-information-paradox-clone-description = A freak space-time anomaly has teleported you into another reality! Now you have to find your counterpart and kill and replace them. + ghost-role-information-syndicate-reinforcement-name = Syndicate Agent ghost-role-information-syndicate-reinforcement-description = Someone needs reinforcements. You, the first person the syndicate could find, will help them. ghost-role-information-syndicate-reinforcement-rules = You are a [color=red][bold]Team Antagonist[/bold][/color] with the agent who summoned you. diff --git a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl index 7996db570be07..fa0a1fb655d5d 100644 --- a/Resources/Locale/en-US/interaction/interaction-popup-component.ftl +++ b/Resources/Locale/en-US/interaction/interaction-popup-component.ftl @@ -103,3 +103,6 @@ hugging-success-generic-target = { CAPITALIZE(THE($user)) } hugs you. petting-success-tesla = You pet {THE($target)}, violating the laws of nature and physics. petting-failure-tesla = You reach out towards {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BASIC($target, "zap", "zaps")} your hand away. + +petting-success-mail-teleporter = You pet {THE($target)} on {POSS-ADJ($target)} dutiful, cold exterior. +petting-failure-mail-teleporter = You reach out to pet {THE($target)}, but {SUBJECT($target)} {CONJUGATE-BE($target)} busy sorting mail! diff --git a/Resources/Locale/en-US/paradox-clone/role.ftl b/Resources/Locale/en-US/paradox-clone/role.ftl new file mode 100644 index 0000000000000..0a73ab4c3bacb --- /dev/null +++ b/Resources/Locale/en-US/paradox-clone/role.ftl @@ -0,0 +1,5 @@ +paradox-clone-round-end-agent-name = paradox clone + +objective-issuer-paradox = [color=lightblue]Paradox[/color] + +paradox-clone-role-greeting = A freak space-time anomaly has teleported you into another reality! Now you have to find your counterpart and kill and replace them. Only one of you two can survive. diff --git a/Resources/Locale/en-US/polymorph/polymorph.ftl b/Resources/Locale/en-US/polymorph/polymorph.ftl index 0066196b031c4..ac78eb6bb4761 100644 --- a/Resources/Locale/en-US/polymorph/polymorph.ftl +++ b/Resources/Locale/en-US/polymorph/polymorph.ftl @@ -1,5 +1,5 @@ polymorph-self-action-name = Polymorph ({CAPITALIZE($target)}) polymorph-self-action-description = Instantly polymorph yourself into {$target}. -polymorph-popup-generic = {CAPITALIZE($parent)} turned into {$child}. -polymorph-revert-popup-generic = {CAPITALIZE($parent)} reverted back into {$child}. +polymorph-popup-generic = {CAPITALIZE(THE($parent))} turned into {$child}. +polymorph-revert-popup-generic = {CAPITALIZE(THE($parent))} reverted back into {$child}. diff --git a/Resources/Locale/en-US/prototypes/roles/antags.ftl b/Resources/Locale/en-US/prototypes/roles/antags.ftl index ba43d4ff85bc5..ada25a4a0ac57 100644 --- a/Resources/Locale/en-US/prototypes/roles/antags.ftl +++ b/Resources/Locale/en-US/prototypes/roles/antags.ftl @@ -31,6 +31,9 @@ roles-antag-subverted-silicon-objective = Follow your new laws and do bad unto t roles-antag-space-ninja-name = Space Ninja roles-antag-space-ninja-objective = Use your stealth to sabotage the station, nom on electrical wires. +roles-antag-paradox-clone-name = Paradox Clone +roles-antag-paradox-clone-objective = A freak space-time anomaly has teleported you into another reality! Now you have to find your counterpart and kill and replace them. + roles-antag-thief-name = Thief roles-antag-thief-objective = Add some NT property to your personal collection without using violence. diff --git a/Resources/Locale/en-US/weapons/ranged/gun.ftl b/Resources/Locale/en-US/weapons/ranged/gun.ftl index 31ac7d5bf23b9..10bc9e6c78f02 100644 --- a/Resources/Locale/en-US/weapons/ranged/gun.ftl +++ b/Resources/Locale/en-US/weapons/ranged/gun.ftl @@ -7,6 +7,7 @@ gun-disabled = You can't use guns! gun-clumsy = The gun blows up in your face! gun-set-fire-mode = Set to {$mode} gun-magazine-whitelist-fail = That won't fit into the gun! +gun-magazine-fired-empty = No ammo left! # SelectiveFire gun-SemiAuto = semi-auto diff --git a/Resources/Prototypes/Catalog/Fills/Items/toolboxes.yml b/Resources/Prototypes/Catalog/Fills/Items/toolboxes.yml index ac9ab4deaa615..a4813fe41a4a8 100644 --- a/Resources/Prototypes/Catalog/Fills/Items/toolboxes.yml +++ b/Resources/Prototypes/Catalog/Fills/Items/toolboxes.yml @@ -22,7 +22,7 @@ prob: 0.15 - id: HarmonicaInstrument prob: 0.15 - + - type: entity id: ToolboxElectricalFilled name: electrical toolbox @@ -82,7 +82,7 @@ - id: MysteryFigureBox prob: 0.5 - id: BookRandom - amount: 2 + amount: 2 - id: CrayonMime - id: CrayonRainbow @@ -105,6 +105,21 @@ - id: ClothingHeadHatHardhatBlue prob: 0.5 +- type: entity + id: ToolboxMechanicalFilledAllTools + name: mechanical toolbox + suffix: Filled, all tools + parent: ToolboxMechanical + components: + - type: StorageFill + contents: + - id: Crowbar + - id: Wrench + - id: Screwdriver + - id: Wirecutter + - id: Welder + - id: Multitool + - type: entity parent: ToolboxSyndicate id: ToolboxSyndicateFilled diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index 340083f40416c..33b0ab1a509e2 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -54,6 +54,22 @@ entity_storage: !type:NestedSelector tableId: FillLockerEmergencyStandard +- type: entityTable + id: FillLockerEmergencyN2Standard + table: !type:AllSelector + children: + - id: ClothingMaskBreath + - id: ClothingOuterSuitEmergency + - id: BoxMRE + prob: 0.1 + - id: ToolboxEmergencyFilled + prob: 0.05 + - !type:GroupSelector + children: + - id: EmergencyNitrogenTankFilled + weight: 4 + - id: NitrogenTankFilled + - type: entity id: ClosetEmergencyN2FilledRandom parent: ClosetEmergencyN2 @@ -61,14 +77,18 @@ components: - type: EntityTableContainerFill containers: - entity_storage: !type:AllSelector - children: - - id: ClothingMaskBreath - - id: ClothingOuterSuitEmergency - - !type:GroupSelector - children: - - id: EmergencyNitrogenTankFilled - - id: NitrogenTankFilled + entity_storage: !type:NestedSelector + tableId: FillLockerEmergencyN2Standard + +- type: entity + parent: ClosetWallEmergencyN2 + id: ClosetWallEmergencyN2FilledRandom + suffix: Filled, Random + components: + - type: EntityTableContainerFill + containers: + entity_storage: !type:NestedSelector + tableId: FillLockerEmergencyN2Standard - type: entityTable id: FillLockerFireStandard diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 61f9a009ce858..d3338a2acdc4c 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -269,11 +269,11 @@ productEntity: SingularityGrenade discountCategory: usualDiscounts discountDownTo: - Telecrystal: 3 + Telecrystal: 1 cost: - Telecrystal: 6 + Telecrystal: 2 categories: - - UplinkExplosives + - UplinkDisruption - type: listing id: UplinkWhiteholeGrenade @@ -284,9 +284,9 @@ discountDownTo: Telecrystal: 1 cost: - Telecrystal: 3 + Telecrystal: 2 categories: - - UplinkExplosives + - UplinkDisruption - type: listing id: UplinkGrenadePenguin diff --git a/Resources/Prototypes/Chemistry/mixing_types.yml b/Resources/Prototypes/Chemistry/mixing_types.yml index fd73256410830..86239fce069cf 100644 --- a/Resources/Prototypes/Chemistry/mixing_types.yml +++ b/Resources/Prototypes/Chemistry/mixing_types.yml @@ -12,15 +12,15 @@ id: DummyGrind verbText: mixing-verb-default-grind icon: - sprite: Structures/Machines/juicer.rsi - state: juicer0 + sprite: Structures/Machines/grinder.rsi + state: grinder_empty - type: mixingCategory id: DummyJuice verbText: mixing-verb-default-juice icon: - sprite: Structures/Machines/juicer.rsi - state: juicer0 + sprite: Structures/Machines/grinder.rsi + state: grinder_empty - type: mixingCategory id: DummyCondense diff --git a/Resources/Prototypes/Entities/Effects/mobspawn.yml b/Resources/Prototypes/Entities/Effects/mobspawn.yml index 1489c26493abd..a043fe97d6561 100644 --- a/Resources/Prototypes/Entities/Effects/mobspawn.yml +++ b/Resources/Prototypes/Entities/Effects/mobspawn.yml @@ -39,6 +39,18 @@ - type: SpawnOnDespawn prototype: MobIronCrab +- type: entity + id: MobSpawnCrabCoal + parent: MobSpawnCrabQuartz + name: mobspawner coalcrab + categories: [ HideSpawnMenu, Spawner ] + components: + - type: Sprite + sprite: /Textures/Effects/mobspawn.rsi + state: crab_coal + - type: SpawnOnDespawn + prototype: MobCoalCrab + - type: entity id: MobSpawnCrabSilver parent: MobSpawnCrabQuartz @@ -51,6 +63,18 @@ - type: SpawnOnDespawn prototype: MobSilverCrab +- type: entity + id: MobSpawnCrabGold + parent: MobSpawnCrabQuartz + name: mobspawner goldcrab + categories: [ HideSpawnMenu, Spawner ] + components: + - type: Sprite + sprite: /Textures/Effects/mobspawn.rsi + state: crab_gold + - type: SpawnOnDespawn + prototype: MobGoldCrab + - type: entity id: MobSpawnCrabUranium parent: MobSpawnCrabQuartz diff --git a/Resources/Prototypes/Entities/Effects/wallspawn.yml b/Resources/Prototypes/Entities/Effects/wallspawn.yml index 44afe80e978ac..155ef10bb5e50 100644 --- a/Resources/Prototypes/Entities/Effects/wallspawn.yml +++ b/Resources/Prototypes/Entities/Effects/wallspawn.yml @@ -74,6 +74,22 @@ - type: SpawnOnDespawn prototype: AsteroidRockSilver +- type: entity + id: WallSpawnAsteroidGoldCrab + parent: WallSpawnAsteroid + categories: [ HideSpawnMenu ] + components: + - type: SpawnOnDespawn + prototype: AsteroidRockGoldCrab + +- type: entity + id: WallSpawnAsteroidGold + parent: WallSpawnAsteroid + categories: [ HideSpawnMenu ] + components: + - type: SpawnOnDespawn + prototype: AsteroidRockGold + - type: entity id: WallSpawnAsteroidIronCrab parent: WallSpawnAsteroid @@ -88,4 +104,20 @@ categories: [ HideSpawnMenu ] components: - type: SpawnOnDespawn - prototype: AsteroidRockTin \ No newline at end of file + prototype: AsteroidRockTin + +- type: entity + id: WallSpawnAsteroidCoalCrab + parent: WallSpawnAsteroid + categories: [ HideSpawnMenu ] + components: + - type: SpawnOnDespawn + prototype: AsteroidRockCoalCrab + +- type: entity + id: WallSpawnAsteroidCoal + parent: WallSpawnAsteroid + categories: [ HideSpawnMenu ] + components: + - type: SpawnOnDespawn + prototype: AsteroidRockCoal diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/anomaly.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/anomaly.yml index d12709341243d..40f37c737fe52 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/anomaly.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/anomaly.yml @@ -22,7 +22,7 @@ - AnomalyFlora - AnomalyShadow - AnomalyTech - #- AnomalySanta + #- AnomalySanta rareChance: 0.3 rarePrototypes: - RandomAnomalyInjectorSpawner @@ -40,7 +40,9 @@ - type: RandomSpawner prototypes: - AnomalyRockIron + - AnomalyRockCoal - AnomalyRockSilver + - AnomalyRockGold - AnomalyRockQuartz - AnomalyRockUranium chance: 1 @@ -69,4 +71,4 @@ - AnomalyTrapRock #- AnomalyTrapSanta chance: 1 - + diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/crystal.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/crystal.yml index 84adf28e2d9f8..fccc04b495771 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/crystal.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/crystal.yml @@ -16,5 +16,7 @@ - CrystalOrange - CrystalBlue - CrystalCyan + - CrystalYellow + - CrystalBlack - CrystalGrey chance: 0.7 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml index bd3c76cd84c5e..c78686f5efe11 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml @@ -191,6 +191,26 @@ - sprite: Objects/Weapons/Melee/energykatana.rsi state: icon +- type: entity + categories: [ HideSpawnMenu, Spawner ] + parent: BaseAntagSpawner + id: SpawnPointGhostParadoxClone + components: + - type: GhostRole + name: ghost-role-information-paradox-clone-name + description: ghost-role-information-paradox-clone-description + rules: ghost-role-information-antagonist-rules + mindRoles: + - MindRoleGhostRoleSoloAntagonist + raffle: + settings: default + - type: Sprite + layers: + - sprite: Markers/jobs.rsi + state: green + - sprite: Mobs/Aliens/paradox_clone.rsi + state: preview + - type: entity categories: [ HideSpawnMenu, Spawner ] parent: BaseAntagSpawner diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml b/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml index a26b1511dd6ee..1a2c13460769a 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml @@ -157,6 +157,37 @@ - !type:DoActsBehavior acts: [ "Destruction" ] +- type: entity + parent: MobOreCrab + id: MobCoalCrab + description: An ore crab made from coal. + components: + - type: Sprite + state: coal_crab + - type: MeleeWeapon + damage: + types: + Blunt: 2.5 + - type: MovementSpeedModifier + baseWalkSpeed : 1.0 + baseSprintSpeed : 1.5 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + Coal1: + min: 2 + max: 4 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: entity parent: MobOreCrab id: MobUraniumCrab @@ -221,6 +252,35 @@ - !type:DoActsBehavior acts: [ "Destruction" ] +- type: entity + parent: MobOreCrab + id: MobGoldCrab + name: ore crab + description: An ore crab made from gold. + components: + - type: Sprite + state: gold_crab + - type: MeleeWeapon + damage: + types: + Blunt: 5 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 70 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + GoldOre1: + min: 1 + max: 3 + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: entity name: reagent slime id: ReagentSlime diff --git a/Resources/Prototypes/Entities/Mobs/Player/clone.yml b/Resources/Prototypes/Entities/Mobs/Player/clone.yml index 64ae4f9ad8109..b7bd8a0d549ac 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/clone.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/clone.yml @@ -56,6 +56,7 @@ blacklist: components: - AttachedClothing # helmets, which are part of the suit + - VirtualItem - type: cloningSettings id: Antag @@ -78,7 +79,7 @@ suffix: Non-Antag components: - type: Sprite - sprite: Markers/paradox_clone.rsi + sprite: Mobs/Aliens/paradox_clone.rsi state: preview - type: RandomCloneSpawner settings: BaseClone diff --git a/Resources/Prototypes/Entities/Mobs/Player/human.yml b/Resources/Prototypes/Entities/Mobs/Player/human.yml index 2355c0ffb4049..5ed2c52427257 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/human.yml @@ -97,7 +97,7 @@ roleLoadout: [ RoleSurvivalSyndicate ] - type: RandomMetadata nameSegments: - - SyndicateNamesPrefix + - NamesSyndicatePrefix - NamesSyndicateNormal - type: NpcFactionMember factions: diff --git a/Resources/Prototypes/Entities/Mobs/Player/paradox_clone.yml b/Resources/Prototypes/Entities/Mobs/Player/paradox_clone.yml new file mode 100644 index 0000000000000..1516843379050 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Player/paradox_clone.yml @@ -0,0 +1,37 @@ +# Mob +- type: entity + parent: BaseMob + id: MobParadox + name: space-time paradox + description: A big ball of wibbly wobbly, timey wimey stuff. + components: + - type: Sprite + drawdepth: Mobs + layers: + - sprite: Mobs/Aliens/paradox_clone.rsi + state: paradox + shader: unshaded + - type: RotationVisuals + horizontalRotation: 90 + - type: Pullable + - type: Tag + tags: + - DoorBumpOpener + +- type: entity + parent: MobParadox + id: MobParadoxTimed # visual effect when gibbing on round end + components: + - type: TimedDespawn + lifetime: 10 + +# guidebook dummy + +- type: entity + id: ParadoxCloneDummy + categories: [ HideSpawnMenu ] + name: Paradox Clone + components: + - type: Sprite + sprite: Mobs/Aliens/paradox_clone.rsi + state: preview diff --git a/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml b/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml index 47828ed8f5881..3b2b441aabedc 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/crystal_shard.yml @@ -73,6 +73,22 @@ - Trash - CrystalBlue +- type: entity + parent: ShardCrystalBase + name: yellow crystal shard + id: ShardCrystalYellow + components: + - type: Sprite + color: "#ffde46" + - type: PointLight + radius: 2 + energy: 2.5 + color: "#ffde46" + - type: Tag + tags: + - Trash + - CrystalYellow + - type: entity parent: ShardCrystalBase id: ShardCrystalOrange @@ -89,6 +105,22 @@ - Trash - CrystalOrange +- type: entity + parent: ShardCrystalBase + name: black crystal shard + id: ShardCrystalBlack + components: + - type: Sprite + color: "#363636" + - type: PointLight + radius: 2 + energy: 2.5 + color: "#363636" + - type: Tag + tags: + - Trash + - CrystalBlack + - type: entity parent: ShardCrystalBase id: ShardCrystalPink diff --git a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml index 34263ccdc006f..9b27ad0bc6596 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml @@ -191,9 +191,11 @@ - type: Item size: Normal - type: Sprite + sprite: Objects/Misc/Lights/lights.rsi layers: - state: floodlight - - state: floodlight_on + map: [ "base" ] + - state: floodlight-on shader: unshaded visible: false map: [ "light" ] @@ -244,9 +246,13 @@ parent: BaseItem description: A pole with powerful mounted lights on it. It's broken. components: + - type: Item + size: Normal + sprite: Objects/Misc/Lights/lights.rsi + heldPrefix: floodlight-broken - type: Sprite sprite: Objects/Misc/Lights/lights.rsi - state: floodlight_broken + state: floodlight-broken - type: Anchorable - type: Damageable damageContainer: StructuralInorganic diff --git a/Resources/Prototypes/Entities/Objects/Power/lights.yml b/Resources/Prototypes/Entities/Objects/Power/lights.yml index 66fec56eeb70e..29f6b7142c98f 100644 --- a/Resources/Prototypes/Entities/Objects/Power/lights.yml +++ b/Resources/Prototypes/Entities/Objects/Power/lights.yml @@ -347,6 +347,21 @@ - id: SheetGlass1 - id: ShardCrystalBlue +- type: entity + parent: BaseLightTubeCrystal + name: yellow crystal light tube + id: LightTubeCrystalYellow + components: + - type: LightBulb + color: "#ffde46" + - type: Construction + graph: YellowLight + node: icon + - type: ToolRefinable + refineResult: + - id: SheetGlass1 + - id: ShardCrystalYellow + - type: entity parent: BaseLightTubeCrystal name: pink crystal light tube @@ -377,6 +392,24 @@ - id: SheetGlass1 - id: ShardCrystalOrange +- type: entity + parent: BaseLightTubeCrystal + name: black crystal light tube + description: A high power high energy bulb which has a small colored crystal inside. It isn't very bright. + id: LightTubeCrystalBlack + components: + - type: LightBulb + color: "#363636" + lightEnergy: 1 + lightRadius: 8 + - type: Construction + graph: BlackLight + node: icon + - type: ToolRefinable + refineResult: + - id: SheetGlass1 + - id: ShardCrystalBlack + - type: entity parent: BaseLightTubeCrystal name: red crystal light tube @@ -454,6 +487,21 @@ - id: SheetGlass1 - id: ShardCrystalBlue +- type: entity + parent: BaseLightbulbCrystal + name: yellow crystal light bulb + id: LightBulbCrystalYellow + components: + - type: LightBulb + color: "#ffde46" + - type: Construction + graph: YellowLightBulb + node: icon + - type: ToolRefinable + refineResult: + - id: SheetGlass1 + - id: ShardCrystalYellow + - type: entity parent: BaseLightbulbCrystal name: pink crystal light bulb @@ -484,6 +532,24 @@ - id: SheetGlass1 - id: ShardCrystalOrange +- type: entity + parent: BaseLightbulbCrystal + name: black crystal light bulb + description: A high power high energy bulb which has a small colored crystal inside. It isn't very bright. + id: LightBulbCrystalBlack + components: + - type: LightBulb + color: "#363636" + lightEnergy: 1 + lightRadius: 8 + - type: Construction + graph: BlackLightBulb + node: icon + - type: ToolRefinable + refineResult: + - id: SheetGlass1 + - id: ShardCrystalBlack + - type: entity parent: BaseLightbulbCrystal name: red crystal light bulb diff --git a/Resources/Prototypes/Entities/Objects/Tools/decoys.yml b/Resources/Prototypes/Entities/Objects/Tools/decoys.yml index a8346675e2ae8..8bacfe96af0cb 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/decoys.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/decoys.yml @@ -31,7 +31,7 @@ size: Normal - type: RandomMetadata # No metagaming these, jimbo. nameSegments: - - SyndicateNamesPrefix + - NamesSyndicatePrefix - NamesSyndicateNormal - type: Damageable damageContainer: Inorganic diff --git a/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml b/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml index e10c5b84af5f2..31e5f99752bd9 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml @@ -5,6 +5,8 @@ description: A hand-held mass scanner. components: - type: Item + size: Normal + sprite: Objects/Tools/handheld_mass_scanner.rsi - type: Sprite sprite: Objects/Tools/handheld_mass_scanner.rsi state: icon @@ -47,6 +49,9 @@ slots: cell_slot: name: power-cell-slot-component-slot-name-default + - type: Item + size: Normal + sprite: Objects/Tools/handheld_mass_scanner.rsi - type: Sprite sprite: Objects/Tools/handheld_mass_scanner.rsi state: icon diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml index 041aa0edc2097..8be5b5f8aa061 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml @@ -201,7 +201,7 @@ sound: path: /Audio/Effects/Grenades/Supermatter/supermatter_loop.ogg - type: GravityWell - maxRange: 10 + maxRange: 7 baseRadialAcceleration: 5 baseTangentialAcceleration: .5 gravPulsePeriod: 0.03 @@ -248,10 +248,10 @@ sound: path: /Audio/Effects/Grenades/Supermatter/whitehole_loop.ogg - type: GravityWell - maxRange: 10 - baseRadialAcceleration: -180 - baseTangentialAcceleration: -5 - gravPulsePeriod: 0.01 + maxRange: 7 + baseRadialAcceleration: -10 + baseTangentialAcceleration: -1 + gravPulsePeriod: 0.03 - type: SingularityDistortion intensity: -200 falloffPower: 1.5 diff --git a/Resources/Prototypes/Entities/Structures/Decoration/crystals.yml b/Resources/Prototypes/Entities/Structures/Decoration/crystals.yml index 3e5f9ca18a9be..d68d163b5ebf1 100644 --- a/Resources/Prototypes/Entities/Structures/Decoration/crystals.yml +++ b/Resources/Prototypes/Entities/Structures/Decoration/crystals.yml @@ -140,6 +140,34 @@ min: 1 max: 2 +- type: entity + id: CrystalBlack + parent: CrystalGreen + suffix: black + components: + - type: Sprite + color: "#363636" + - type: PointLight + radius: 3 + energy: 3 + color: "#363636" + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardCrystalBlack: + min: 1 + max: 2 + - type: entity id: CrystalBlue parent: CrystalGreen @@ -168,6 +196,34 @@ min: 1 max: 2 +- type: entity + id: CrystalYellow + parent: CrystalGreen + suffix: yellow + components: + - type: Sprite + color: "#ffde46" + - type: PointLight + radius: 3 + energy: 3 + color: "#ffde46" + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 20 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - !type:PlaySoundBehavior + sound: + collection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardCrystalYellow: + min: 1 + max: 2 + - type: entity id: CrystalCyan parent: CrystalGreen diff --git a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml index baa90189cfc5f..0cfb57dca12ce 100644 --- a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml +++ b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml @@ -564,6 +564,35 @@ softness: 0.5 color: "#39a1ff" +- type: entity + id: PoweredlightYellow + suffix: Yellow + parent: Poweredlight + components: + - type: PoweredLight + hasLampOnSpawn: LightTubeCrystalYellow + - type: PointLight + radius: 8 + energy: 3 + softness: 0.5 + color: "#ffde46" + - type: DamageOnInteract + damage: + types: + Heat: 2 + popupText: powered-light-component-burn-hand + +- type: entity + id: AlwaysPoweredlightYellow + suffix: Always Powered, Yellow + parent: AlwaysPoweredWallLight + components: + - type: PointLight + radius: 8 + energy: 3 + softness: 0.5 + color: "#ffde46" + - type: entity id: PoweredlightPink suffix: Pink @@ -622,6 +651,36 @@ softness: 0.5 color: "#ff8227" +- type: entity + id: PoweredlightBlack + suffix: Black + parent: Poweredlight + description: + components: + - type: PoweredLight + hasLampOnSpawn: LightTubeCrystalBlack + - type: PointLight + radius: 8 + energy: 1 + softness: 0.5 + color: "#363636" + - type: DamageOnInteract + damage: + types: + Heat: 2 + popupText: powered-light-component-burn-hand + +- type: entity + id: AlwaysPoweredlightBlack + suffix: Always Powered, Black + parent: AlwaysPoweredWallLight + components: + - type: PointLight + radius: 8 + energy: 1 + softness: 0.5 + color: "#363636" + - type: entity id: PoweredlightRed suffix: Red diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml index 3bc00fb1b61f1..00b60c56ba71b 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml @@ -1,11 +1,11 @@ -- type: entity +- type: entity parent: [BaseStructure, ConstructibleMachine] # Not a BaseMachinePowered since we don't want the anchorable component id: CryoPod name: cryo pod description: A special machine intended to create a safe environment for the use of chemicals that react in cold environments. components: - type: Sprite - sprite: Structures/Machines/cryogenics.rsi + sprite: Structures/Machines/Medical/cryopod.rsi drawdepth: Mobs noRot: true offset: 0, 0.5 diff --git a/Resources/Prototypes/Entities/Structures/Machines/mail_teleporter.yml b/Resources/Prototypes/Entities/Structures/Machines/mail_teleporter.yml index 4356921ed6603..a03f166bbed59 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/mail_teleporter.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/mail_teleporter.yml @@ -39,6 +39,9 @@ - type: Machine board: MailTeleporterMachineCircuitboard - type: PowerSwitch + - type: InteractionPopup + interactSuccessString: petting-success-mail-teleporter + interactFailureString: petting-failure-mail-teleporter - type: DeliverySpawner table: !type:NestedSelector tableId: RandomDeliveryBasic diff --git a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml index 72d6b28efa025..6456e043b8cc9 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/reagent_grinder.yml @@ -5,8 +5,6 @@ description: From BlenderTech. Will It Blend? Let's find out! suffix: grinder/juicer components: - - type: Transform - anchored: true - type: ReagentGrinder - type: ActivatableUI key: enum.ReagentGrinderUiKey.Key @@ -19,8 +17,8 @@ visuals: enum.ReagentGrinderVisualState.BeakerAttached: grinder: - True: {state: "juicer1"} - False: {state: "juicer0"} + True: {state: "grinder_beaker_attached"} + False: {state: "grinder_empty"} - type: Physics - type: Fixtures fixtures: @@ -33,13 +31,13 @@ layer: - TabletopMachineLayer - type: Sprite - sprite: Structures/Machines/juicer.rsi + sprite: Structures/Machines/grinder.rsi drawdepth: SmallObjects snapCardinals: true offset: "0.0,0.4" layers: - map: [ "grinder" ] - state: "juicer0" + state: "grinder_empty" - type: ApcPowerReceiver powerLoad: 300 - type: ItemSlots diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml index 60980cb6fcf2b..09bf879ca85af 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml @@ -553,6 +553,56 @@ spawns: - MobSpawnCrabSilver +- type: entity + id: AnomalyRockGold + parent: AnomalyRockBase + suffix: Rock, Gold + components: + - type: Sprite + color: "#e3ba70" + - type: PointLight + radius: 2.0 + energy: 7.5 + color: "#e3ba70" + - type: EntitySpawnAnomaly + entries: + - settings: + spawnOnPulse: true + minAmount: 8 + maxAmount: 15 + minRange: 4.5 + maxRange: 7.5 + spawns: + - WallSpawnAsteroid + - WallSpawnAsteroid + - WallSpawnAsteroidGold + - WallSpawnAsteroidGoldCrab + - settings: + spawnOnPulse: true + maxAmount: 3 + minRange: 2.5 + maxRange: 4.5 + spawns: + - CrystalYellow + - settings: + spawnOnSuperCritical: true + minAmount: 30 + maxAmount: 40 + minRange: 5 + maxRange: 15 + spawns: + - CrystalYellow + - WallSpawnAsteroid + - WallSpawnAsteroid + - WallSpawnAsteroidGoldCrab + - settings: + spawnOnSuperCritical: true + minAmount: 6 + maxAmount: 10 + maxRange: 5 + spawns: + - MobSpawnCrabGold + - type: entity id: AnomalyRockIron parent: AnomalyRockBase @@ -603,6 +653,56 @@ spawns: - MobSpawnCrabIron +- type: entity + id: AnomalyRockCoal + parent: AnomalyRockBase + suffix: Rock, Coal + components: + - type: Sprite + color: "#484848" + - type: PointLight + radius: 2.0 + energy: 7.5 + color: "#484848" + - type: EntitySpawnAnomaly + entries: + - settings: + spawnOnPulse: true + minAmount: 8 + maxAmount: 15 + minRange: 4.5 + maxRange: 7.5 + spawns: + - WallSpawnAsteroid + - WallSpawnAsteroid + - WallSpawnAsteroidCoal + - WallSpawnAsteroidCoalCrab + - settings: + spawnOnPulse: true + maxAmount: 3 + minRange: 2.5 + maxRange: 4.5 + spawns: + - CrystalBlack + - settings: + spawnOnSuperCritical: true + minAmount: 30 + maxAmount: 40 + minRange: 5 + maxRange: 15 + spawns: + - CrystalBlack + - WallSpawnAsteroid + - WallSpawnAsteroid + - WallSpawnAsteroidCoalCrab + - settings: + spawnOnSuperCritical: true + minAmount: 6 + maxAmount: 10 + maxRange: 5 + spawns: + - MobSpawnCrabCoal + - type: entity id: AnomalyFlora parent: BaseAnomaly diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml index b76791cba69ce..33c4dfbe178eb 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml @@ -592,6 +592,8 @@ - !type:DoActsBehavior acts: [ "Destruction" ] - !type:DumpCanisterBehavior + - type: Lock + locked: true - type: entity parent: GasCanister diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/wall_lockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/wall_lockers.yml index 4280b960b4c38..02b9fa8271de2 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/wall_lockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/wall_lockers.yml @@ -22,6 +22,18 @@ stateDoorOpen: emergency_open stateDoorClosed: emergency_door +- type: entity + parent: BaseWallCloset + id: ClosetWallEmergencyN2 + name: emergency nitrogen wall closet + description: It's full of life-saving equipment. Assuming, that is, that you breathe nitrogen. + components: + - type: Appearance + - type: EntityStorageVisuals + stateBaseClosed: n2 + stateDoorOpen: n2_open + stateDoorClosed: n2_door + - type: entity id: ClosetWallFire name: fire-safety wall closet @@ -163,4 +175,4 @@ stateDoorOpen: med_open stateDoorClosed: med_door - type: AccessReader - access: [["Medical"]] \ No newline at end of file + access: [["Medical"]] diff --git a/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml b/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml index 54f553ab9cd70..e83b4c5f071be 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml @@ -73,6 +73,29 @@ - state: rock_coal map: [ "enum.MiningScannerVisualLayers.Overlay" ] +- type: entity + id: AsteroidRockCoalCrab + parent: AsteroidRock + description: An ore vein rich with coal. + suffix: Coal Crab + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreCoalCrab + - type: Sprite + layers: + - state: rock_asteroid + - map: [ "enum.EdgeLayer.South" ] + state: rock_asteroid_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_asteroid_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_asteroid_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_asteroid_west + - state: rock_coal + map: [ "enum.MiningScannerVisualLayers.Overlay" ] + - type: entity id: AsteroidRockGold parent: AsteroidRock @@ -96,6 +119,29 @@ - state: rock_gold map: [ "enum.MiningScannerVisualLayers.Overlay" ] +- type: entity + id: AsteroidRockGoldCrab + parent: AsteroidRock + description: An ore vein rich with gold. + suffix: Gold Crab + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreGoldCrab + - type: Sprite + layers: + - state: rock_asteroid + - map: [ "enum.EdgeLayer.South" ] + state: rock_asteroid_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_asteroid_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_asteroid_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_asteroid_west + - state: rock_gold + map: [ "enum.MiningScannerVisualLayers.Overlay" ] + - type: entity id: AsteroidRockDiamond parent: AsteroidRock diff --git a/Resources/Prototypes/Entities/Structures/Walls/walls.yml b/Resources/Prototypes/Entities/Structures/Walls/walls.yml index 57b8aa915e72d..97823cec28777 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/walls.yml @@ -81,7 +81,7 @@ acts: [ "Destruction" ] - type: IconSmooth - key: bricks + key: walls base: brick - type: entity diff --git a/Resources/Prototypes/Entities/Tiles/chasm.yml b/Resources/Prototypes/Entities/Tiles/chasm.yml index 23f3ad8395002..a0dc308d17efc 100644 --- a/Resources/Prototypes/Entities/Tiles/chasm.yml +++ b/Resources/Prototypes/Entities/Tiles/chasm.yml @@ -27,6 +27,8 @@ state: full - type: IconSmooth key: chasm + additionalKeys: + - walls base: chasm - type: Physics bodyType: Static diff --git a/Resources/Prototypes/Entities/Tiles/lava.yml b/Resources/Prototypes/Entities/Tiles/lava.yml index 68dd5671a06dc..10a09116ade4f 100644 --- a/Resources/Prototypes/Entities/Tiles/lava.yml +++ b/Resources/Prototypes/Entities/Tiles/lava.yml @@ -36,6 +36,8 @@ state: full - type: IconSmooth key: floor + additionalKeys: + - walls base: lava - type: Physics bodyType: Static diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 6ce47543b4807..953a7ad1beca8 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -232,6 +232,39 @@ mindRoles: - MindRoleNinja +- type: entity + parent: BaseGameRule + id: ParadoxCloneSpawn + components: + - type: StationEvent + weight: 4 # should not happen every round + duration: null + earliestStart: 30 + reoccurrenceDelay: 20 + minimumPlayers: 15 + - type: ParadoxCloneRule + - type: AntagObjectives + objectives: + - ParadoxCloneKillObjective + - ParadoxCloneLivingObjective + - type: AntagRandomSpawn # TODO: improve spawning so they only start in maints + - type: AntagSelection + agentName: paradox-clone-round-end-agent-name + definitions: + - spawnerPrototype: SpawnPointGhostParadoxClone + min: 1 + max: 1 + pickPlayer: false + startingGear: ParadoxCloneGear + roleLoadout: + - RoleSurvivalStandard # give vox something to breath in case they don't get a copy + briefing: + text: paradox-clone-role-greeting + color: lightblue + sound: /Audio/Misc/paradox_clone_greeting.ogg + mindRoles: + - MindRoleParadoxClone + - type: entity parent: BaseGameRule id: RevenantSpawn @@ -509,7 +542,7 @@ - type: NukeOperative - type: RandomMetadata nameSegments: - - SyndicateNamesPrefix + - NamesSyndicatePrefix - NamesSyndicateNormal - type: NpcFactionMember factions: diff --git a/Resources/Prototypes/Objectives/paradoxClone.yml b/Resources/Prototypes/Objectives/paradoxClone.yml new file mode 100644 index 0000000000000..80aa4f15df370 --- /dev/null +++ b/Resources/Prototypes/Objectives/paradoxClone.yml @@ -0,0 +1,38 @@ +- type: entity + abstract: true + parent: BaseObjective + id: BaseParadoxCloneObjective + components: + - type: Objective + # required but not used + difficulty: 1 + issuer: objective-issuer-paradox + - type: RoleRequirement + roles: + mindRoles: + - ParadoxCloneRole + +- type: entity + parent: [BaseParadoxCloneObjective, BaseLivingObjective] + id: ParadoxCloneLivingObjective + name: Escape to centcomm alive and unrestrained. + description: Return to your old life. + components: + - type: Objective + icon: + sprite: Structures/Furniture/chairs.rsi + state: shuttle + - type: EscapeShuttleCondition + +- type: entity + parent: [BaseParadoxCloneObjective, BaseKillObjective] + id: ParadoxCloneKillObjective + name: Fix the space-time paradox. + description: Replace your original to fix the paradox. Remember, your mission is to blend in, do not kill anyone else unless you have to! + components: + - type: PickSpecificPerson + - type: KillPersonCondition + requireDead: true # don't count missing evac as killing + - type: TargetObjective + title: objective-condition-kill-head-title # kill , + diff --git a/Resources/Prototypes/Procedural/biome_templates.yml b/Resources/Prototypes/Procedural/biome_templates.yml index 588d95f40da5d..d630ebad489fb 100644 --- a/Resources/Prototypes/Procedural/biome_templates.yml +++ b/Resources/Prototypes/Procedural/biome_templates.yml @@ -513,7 +513,9 @@ - CrystalGreen - CrystalPink - CrystalOrange + - CrystalBlack - CrystalBlue + - CrystalYellow - CrystalCyan - !type:BiomeEntityLayer threshold: 0.95 @@ -561,7 +563,9 @@ - CrystalGreen - CrystalPink - CrystalOrange + - CrystalBlack - CrystalBlue + - CrystalYellow - CrystalCyan - !type:BiomeEntityLayer threshold: 0.95 diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/lighting.yml b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/lighting.yml index d7e48b664669d..ef461eda9b64f 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/lighting.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/lighting.yml @@ -39,6 +39,26 @@ - node: icon entity: LightTubeCrystalBlue +- type: constructionGraph + id: YellowLight + start: start + graph: + - node: start + edges: + - to: icon + steps: + - material: Glass + amount: 1 + doAfter: 1 + - tag: CrystalYellow + name: yellow crystal shard + icon: + sprite: Objects/Materials/Shards/crystal.rsi + state: shard1 + doAfter: 1 + - node: icon + entity: LightTubeCrystalYellow + - type: constructionGraph id: PinkLight start: start @@ -79,6 +99,26 @@ - node: icon entity: LightTubeCrystalOrange +- type: constructionGraph + id: BlackLight + start: start + graph: + - node: start + edges: + - to: icon + steps: + - material: Glass + amount: 1 + doAfter: 1 + - tag: CrystalBlack + name: black crystal shard + icon: + sprite: Objects/Materials/Shards/crystal.rsi + state: shard1 + doAfter: 1 + - node: icon + entity: LightTubeCrystalBlack + - type: constructionGraph id: RedLight start: start @@ -160,6 +200,26 @@ - node: icon entity: LightBulbCrystalBlue +- type: constructionGraph + id: YellowLightBulb + start: start + graph: + - node: start + edges: + - to: icon + steps: + - material: Glass + amount: 1 + doAfter: 1 + - tag: CrystalYellow + name: yellow crystal shard + icon: + sprite: Objects/Materials/Shards/crystal.rsi + state: shard1 + doAfter: 1 + - node: icon + entity: LightBulbCrystalYellow + - type: constructionGraph id: PinkLightBulb start: start @@ -200,6 +260,26 @@ - node: icon entity: LightBulbCrystalOrange +- type: constructionGraph + id: BlackLightBulb + start: start + graph: + - node: start + edges: + - to: icon + steps: + - material: Glass + amount: 1 + doAfter: 1 + - tag: CrystalBlack + name: black crystal shard + icon: + sprite: Objects/Materials/Shards/crystal.rsi + state: shard1 + doAfter: 1 + - node: icon + entity: LightBulbCrystalBlack + - type: constructionGraph id: RedLightBulb start: start diff --git a/Resources/Prototypes/Recipes/Construction/lighting.yml b/Resources/Prototypes/Recipes/Construction/lighting.yml index d6aeddc7e8f45..0651fc4572b1b 100644 --- a/Resources/Prototypes/Recipes/Construction/lighting.yml +++ b/Resources/Prototypes/Recipes/Construction/lighting.yml @@ -20,6 +20,17 @@ icon: { sprite: Objects/Power/light_tube.rsi, state: normal } objectType: Item +- type: construction + name: yellow light tube + id: YellowLight + graph: YellowLight + startNode: start + targetNode: icon + category: construction-category-utilities + description: A high powered light tube containing a yellow crystal + icon: { sprite: Objects/Power/light_tube.rsi, state: normal } + objectType: Item + - type: construction name: pink light tube id: PinkLight @@ -42,6 +53,17 @@ icon: { sprite: Objects/Power/light_tube.rsi, state: normal } objectType: Item +- type: construction + name: black light tube + id: BlackLight + graph: BlackLight + startNode: start + targetNode: icon + category: construction-category-utilities + description: A high powered light tube containing a black crystal. It won't be very bright. + icon: { sprite: Objects/Power/light_tube.rsi, state: normal } + objectType: Item + - type: construction name: red light tube id: RedLight @@ -86,6 +108,17 @@ icon: { sprite: Objects/Power/light_bulb.rsi, state: normal } objectType: Item +- type: construction + name: yellow light bulb + id: YellowLightBulb + graph: YellowLightBulb + startNode: start + targetNode: icon + category: construction-category-utilities + description: A high powered light bulb containing a yellow crystal. + icon: { sprite: Objects/Power/light_bulb.rsi, state: normal } + objectType: Item + - type: construction name: pink light bulb id: PinkLightBulb @@ -108,6 +141,17 @@ icon: { sprite: Objects/Power/light_bulb.rsi, state: normal } objectType: Item +- type: construction + name: black light bulb + id: BlackLightBulb + graph: BlackLightBulb + startNode: start + targetNode: icon + category: construction-category-utilities + description: A high powered light bulb containing a black crystal. It won't be very bright. + icon: { sprite: Objects/Power/light_bulb.rsi, state: normal } + objectType: Item + - type: construction name: red light bulb id: RedLightBulb diff --git a/Resources/Prototypes/Roles/Antags/paradoxClone.yml b/Resources/Prototypes/Roles/Antags/paradoxClone.yml new file mode 100644 index 0000000000000..6e8daf16b4467 --- /dev/null +++ b/Resources/Prototypes/Roles/Antags/paradoxClone.yml @@ -0,0 +1,16 @@ +- type: antag + id: ParadoxClone + name: roles-antag-paradox-clone-name + antagonist: true + objective: roles-antag-paradox-clone-objective + setPreference: false + +# they need to be able to espace when spawning inside a locked room +# TODO: remove this once we got a better spawning loacation selection +- type: startingGear + id: ParadoxCloneGear + inhand: + - ToolboxMechanicalFilledAllTools + storage: + back: + - ClothingHandsGlovesColorYellow diff --git a/Resources/Prototypes/Roles/Jobs/Security/warden.yml b/Resources/Prototypes/Roles/Jobs/Security/warden.yml index 6d79d74804900..856c8660382c4 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/warden.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/warden.yml @@ -7,6 +7,7 @@ - !type:RoleTimeRequirement role: JobSecurityOfficer time: 36000 #10 hrs + weight: 5 startingGear: WardenGear icon: "JobIconWarden" supervisors: job-supervisors-hos diff --git a/Resources/Prototypes/Roles/MindRoles/mind_roles.yml b/Resources/Prototypes/Roles/MindRoles/mind_roles.yml index ea6780eb50742..a690ab0c0db3e 100644 --- a/Resources/Prototypes/Roles/MindRoles/mind_roles.yml +++ b/Resources/Prototypes/Roles/MindRoles/mind_roles.yml @@ -143,6 +143,17 @@ exclusiveAntag: true - type: NinjaRole +# Paradox Clone +- type: entity + parent: BaseMindRoleAntag + id: MindRoleParadoxClone + name: Paradox Clone Role + components: + - type: MindRole + antagPrototype: ParadoxClone + roleType: SoloAntagonist + - type: ParadoxCloneRole + # Nukies - type: entity parent: BaseMindRoleAntag diff --git a/Resources/Prototypes/ore.yml b/Resources/Prototypes/ore.yml index 30ac6a537342a..a7f4ef11e5526 100644 --- a/Resources/Prototypes/ore.yml +++ b/Resources/Prototypes/ore.yml @@ -72,10 +72,18 @@ id: OreIronCrab oreEntity: MobSpawnCrabIron +- type: ore + id: OreCoalCrab + oreEntity: MobSpawnCrabCoal + - type: ore id: OreSilverCrab oreEntity: MobSpawnCrabSilver +- type: ore + id: OreGoldCrab + oreEntity: MobSpawnCrabGold + - type: ore id: OreUraniumCrab oreEntity: MobSpawnCrabUranium diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 0a25304c2c0cf..179651a1132a3 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -419,6 +419,9 @@ - type: Tag id: Cryobeaker +- type: Tag + id: CrystalBlack + - type: Tag id: CrystalBlue @@ -437,6 +440,9 @@ - type: Tag id: CrystalRed +- type: Tag + id: CrystalYellow + - type: Tag id: CubanCarp diff --git a/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml b/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml index 97574c4021538..a938cc4040c36 100644 --- a/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml +++ b/Resources/ServerInfo/Guidebook/Antagonist/MinorAntagonists.xml @@ -3,6 +3,14 @@ Most if not all Minor Antagonists are ghost-controlled roles that gives dead people new ways to cause chaos around the station. They are spawned by random events. + # Paradox Clone + + + + + + A perfect copy of a random crewmember that was thrown into our universe by a space-time anomaly. To fix the paradox and survive they have to find and kill their original counterpart. They have no special abilities, but are have identical appearance, DNA, fingerprints, clothing, equipment and IDs, making it hard to figure out who is the orginal and who is the clone, unless you know your fellow crewmembers well. + # Revenant diff --git a/Resources/Textures/Effects/mobspawn.rsi/crab_coal.png b/Resources/Textures/Effects/mobspawn.rsi/crab_coal.png new file mode 100644 index 0000000000000..660de64908559 Binary files /dev/null and b/Resources/Textures/Effects/mobspawn.rsi/crab_coal.png differ diff --git a/Resources/Textures/Effects/mobspawn.rsi/crab_gold.png b/Resources/Textures/Effects/mobspawn.rsi/crab_gold.png new file mode 100644 index 0000000000000..aeb8bd1aef3ed Binary files /dev/null and b/Resources/Textures/Effects/mobspawn.rsi/crab_gold.png differ diff --git a/Resources/Textures/Effects/mobspawn.rsi/meta.json b/Resources/Textures/Effects/mobspawn.rsi/meta.json index 720bc8a32124b..ca0d1fe0fc089 100644 --- a/Resources/Textures/Effects/mobspawn.rsi/meta.json +++ b/Resources/Textures/Effects/mobspawn.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by brainfood1183 (github)", + "copyright": "Made by brainfood1183 (github), Gold and Coal Crab pallet swapped by Firewars763 (github)", "size": { "x": 32, "y": 32 @@ -35,6 +35,20 @@ ] ] }, + { + "name": "crab_gold", + "directions": 1, + "delays": [ + [ + 0.6, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3 + ] + ] + }, { "name": "crab_uranium", "directions": 1, @@ -62,6 +76,20 @@ 0.3 ] ] + }, + { + "name": "crab_coal", + "directions": 1, + "delays": [ + [ + 0.6, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3 + ] + ] } ] } diff --git a/Resources/Textures/Markers/paradox_clone.rsi/meta.json b/Resources/Textures/Markers/paradox_clone.rsi/meta.json deleted file mode 100644 index e58634703115c..0000000000000 --- a/Resources/Textures/Markers/paradox_clone.rsi/meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "preview combined from Mobs/Species/Human/parts.rsi, Clothing/Uniforms/Jumpsuit/janitor.rsi, Clothing/Shoes/Specific/galoshes.rsi, Clothing/Belt/janitor.rsi, Clothing/Hands/Gloves/janitor.rsi and Clothing/Head/Soft/purplesoft.rsi by slarticodefast", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "preview" - } - ] -} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Aliens/paradox_clone.rsi/meta.json b/Resources/Textures/Mobs/Aliens/paradox_clone.rsi/meta.json new file mode 100644 index 0000000000000..942900ec3ea07 --- /dev/null +++ b/Resources/Textures/Mobs/Aliens/paradox_clone.rsi/meta.json @@ -0,0 +1,108 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "preview combined from Mobs/Species/Human/parts.rsi, Clothing/Uniforms/Jumpsuit/janitor.rsi, Clothing/Shoes/Specific/galoshes.rsi, Clothing/Belt/janitor.rsi, Clothing/Hands/Gloves/janitor.rsi and Clothing/Head/Soft/purplesoft.rsi by slarticodefast, paradox made by ps3moira (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "preview" + }, + { + "name": "paradox", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/Mobs/Aliens/paradox_clone.rsi/paradox.png b/Resources/Textures/Mobs/Aliens/paradox_clone.rsi/paradox.png new file mode 100644 index 0000000000000..f1cb35df5988b Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/paradox_clone.rsi/paradox.png differ diff --git a/Resources/Textures/Markers/paradox_clone.rsi/preview.png b/Resources/Textures/Mobs/Aliens/paradox_clone.rsi/preview.png similarity index 100% rename from Resources/Textures/Markers/paradox_clone.rsi/preview.png rename to Resources/Textures/Mobs/Aliens/paradox_clone.rsi/preview.png diff --git a/Resources/Textures/Mobs/Elemental/orecrab.rsi/coal_crab.png b/Resources/Textures/Mobs/Elemental/orecrab.rsi/coal_crab.png new file mode 100644 index 0000000000000..a1e877ee1ea0d Binary files /dev/null and b/Resources/Textures/Mobs/Elemental/orecrab.rsi/coal_crab.png differ diff --git a/Resources/Textures/Mobs/Elemental/orecrab.rsi/gold_crab.png b/Resources/Textures/Mobs/Elemental/orecrab.rsi/gold_crab.png new file mode 100644 index 0000000000000..420d96b6c55a3 Binary files /dev/null and b/Resources/Textures/Mobs/Elemental/orecrab.rsi/gold_crab.png differ diff --git a/Resources/Textures/Mobs/Elemental/orecrab.rsi/meta.json b/Resources/Textures/Mobs/Elemental/orecrab.rsi/meta.json index a85de36617156..c3a0bb3080d3b 100644 --- a/Resources/Textures/Mobs/Elemental/orecrab.rsi/meta.json +++ b/Resources/Textures/Mobs/Elemental/orecrab.rsi/meta.json @@ -5,12 +5,16 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "Made by brainfood1183 (github)", + "copyright": "Made by brainfood1183 (github), Gold and Coal Crab pallet swapped by Firewars763 (github),", "states": [ { "name": "iron_crab", "directions": 4 }, + { + "name": "coal_crab", + "directions": 4 + }, { "name": "uranium_crab", "directions": 4 @@ -19,6 +23,10 @@ "name": "silver_crab", "directions": 4 }, + { + "name": "gold_crab", + "directions": 4 + }, { "name": "quartz_crab", "directions": 4 diff --git a/Resources/Textures/Objects/Devices/gps.rsi/inhand-left.png b/Resources/Textures/Objects/Devices/gps.rsi/inhand-left.png new file mode 100644 index 0000000000000..c1d8c3974f7ec Binary files /dev/null and b/Resources/Textures/Objects/Devices/gps.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Devices/gps.rsi/inhand-right.png b/Resources/Textures/Objects/Devices/gps.rsi/inhand-right.png new file mode 100644 index 0000000000000..80f4553ecc0a7 Binary files /dev/null and b/Resources/Textures/Objects/Devices/gps.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Devices/gps.rsi/meta.json b/Resources/Textures/Objects/Devices/gps.rsi/meta.json index a59c0b00129f6..72a8e19a4ece4 100644 --- a/Resources/Textures/Objects/Devices/gps.rsi/meta.json +++ b/Resources/Textures/Objects/Devices/gps.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "tgstation at 3ddd840268e33bbcf316f242e6a972b84e6b773c", + "copyright": "tgstation at 3ddd840268e33bbcf316f242e6a972b84e6b773c, Inhand sprites by onesch", "size": { "x": 32, "y": 32 @@ -10,6 +10,14 @@ { "name": "icon" }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, { "name": "active", "delays": [ diff --git a/Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight-broken-inhand-left.png b/Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight-broken-inhand-left.png new file mode 100644 index 0000000000000..b31b949eee296 Binary files /dev/null and b/Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight-broken-inhand-left.png differ diff --git a/Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight-broken-inhand-right.png b/Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight-broken-inhand-right.png new file mode 100644 index 0000000000000..c03d65a422543 Binary files /dev/null and b/Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight-broken-inhand-right.png differ diff --git a/Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight_broken.png b/Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight-broken.png similarity index 100% rename from Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight_broken.png rename to Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight-broken.png diff --git a/Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight_on.png b/Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight-on.png similarity index 100% rename from Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight_on.png rename to Resources/Textures/Objects/Misc/Lights/lights.rsi/floodlight-on.png diff --git a/Resources/Textures/Objects/Misc/Lights/lights.rsi/meta.json b/Resources/Textures/Objects/Misc/Lights/lights.rsi/meta.json index 38b29d5d407d1..04005a4a79807 100644 --- a/Resources/Textures/Objects/Misc/Lights/lights.rsi/meta.json +++ b/Resources/Textures/Objects/Misc/Lights/lights.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/e1142f20f5e4661cb6845cfcf2dd69f864d67432, Floodlight inhand sprites by onesch", "size": { "x": 32, "y": 32 @@ -19,13 +19,37 @@ "name": "floodlight" }, { - "name": "floodlight_on" + "name": "off-inhand-left", + "directions": 4 + }, + { + "name": "off-inhand-right", + "directions": 4 + }, + { + "name": "floodlight-on" + }, + { + "name": "on-inhand-left", + "directions": 4 + }, + { + "name": "on-inhand-right", + "directions": 4 }, { "name": "floodlight_burned" }, { - "name": "floodlight_broken" + "name": "floodlight-broken" + }, + { + "name": "floodlight-broken-inhand-left", + "directions": 4 + }, + { + "name": "floodlight-broken-inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Misc/Lights/lights.rsi/off-inhand-left.png b/Resources/Textures/Objects/Misc/Lights/lights.rsi/off-inhand-left.png new file mode 100644 index 0000000000000..b23c998256c1e Binary files /dev/null and b/Resources/Textures/Objects/Misc/Lights/lights.rsi/off-inhand-left.png differ diff --git a/Resources/Textures/Objects/Misc/Lights/lights.rsi/off-inhand-right.png b/Resources/Textures/Objects/Misc/Lights/lights.rsi/off-inhand-right.png new file mode 100644 index 0000000000000..2f0b15c048be5 Binary files /dev/null and b/Resources/Textures/Objects/Misc/Lights/lights.rsi/off-inhand-right.png differ diff --git a/Resources/Textures/Objects/Misc/Lights/lights.rsi/on-inhand-left.png b/Resources/Textures/Objects/Misc/Lights/lights.rsi/on-inhand-left.png new file mode 100644 index 0000000000000..8e43cbde0a4a0 Binary files /dev/null and b/Resources/Textures/Objects/Misc/Lights/lights.rsi/on-inhand-left.png differ diff --git a/Resources/Textures/Objects/Misc/Lights/lights.rsi/on-inhand-right.png b/Resources/Textures/Objects/Misc/Lights/lights.rsi/on-inhand-right.png new file mode 100644 index 0000000000000..7f0f817970b9c Binary files /dev/null and b/Resources/Textures/Objects/Misc/Lights/lights.rsi/on-inhand-right.png differ diff --git a/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/inhand-left.png b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/inhand-left.png new file mode 100644 index 0000000000000..5a9d7b31b6814 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/inhand-right.png b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/inhand-right.png new file mode 100644 index 0000000000000..917c22422c7a2 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/meta.json b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/meta.json index 083f15f073fc0..d563b8fed29f4 100644 --- a/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Atmos/gasanalyzer.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from baystation at https://github.com/Baystation12/Baystation12/commit/ded74aff42136a7953c551a2a94cebc81f62f9fb", + "copyright": "Taken from baystation at https://github.com/Baystation12/Baystation12/commit/ded74aff42136a7953c551a2a94cebc81f62f9fb, Inhand sprites by onesch", "size": { "x": 32, "y": 32 @@ -10,6 +10,14 @@ { "name": "icon" }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, { "name": "working", "delays": [ diff --git a/Resources/Textures/Objects/Tools/handheld_mass_scanner.rsi/inhand-left.png b/Resources/Textures/Objects/Tools/handheld_mass_scanner.rsi/inhand-left.png new file mode 100644 index 0000000000000..0eec62723dff0 Binary files /dev/null and b/Resources/Textures/Objects/Tools/handheld_mass_scanner.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Tools/handheld_mass_scanner.rsi/inhand-right.png b/Resources/Textures/Objects/Tools/handheld_mass_scanner.rsi/inhand-right.png new file mode 100644 index 0000000000000..e727b4e7a13a4 Binary files /dev/null and b/Resources/Textures/Objects/Tools/handheld_mass_scanner.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Tools/handheld_mass_scanner.rsi/meta.json b/Resources/Textures/Objects/Tools/handheld_mass_scanner.rsi/meta.json index edae9c38c4147..24fdcb71cc2a4 100644 --- a/Resources/Textures/Objects/Tools/handheld_mass_scanner.rsi/meta.json +++ b/Resources/Textures/Objects/Tools/handheld_mass_scanner.rsi/meta.json @@ -5,7 +5,7 @@ "y": 32 }, "license": "CC-BY-SA-3.0", - "copyright": "Taken from Frontier Station, created by Tem Armoff https://github.com/new-frontiers-14/frontier-station-14/pull/484/files", + "copyright": "Taken from Frontier Station, created by Tem Armoff https://github.com/new-frontiers-14/frontier-station-14/pull/484/files, Inhand sprites by onesch", "states": [ { "name": "scanner", @@ -20,6 +20,14 @@ { "name": "icon", "directions": 1 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Tools/scissors.rsi/inhand-left.png b/Resources/Textures/Objects/Tools/scissors.rsi/inhand-left.png new file mode 100644 index 0000000000000..3d078c811dfd8 Binary files /dev/null and b/Resources/Textures/Objects/Tools/scissors.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Tools/scissors.rsi/inhand-right.png b/Resources/Textures/Objects/Tools/scissors.rsi/inhand-right.png new file mode 100644 index 0000000000000..d9c5f33fc035b Binary files /dev/null and b/Resources/Textures/Objects/Tools/scissors.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Tools/scissors.rsi/meta.json b/Resources/Textures/Objects/Tools/scissors.rsi/meta.json index b544b3bc1568f..c431ad7f01d27 100644 --- a/Resources/Textures/Objects/Tools/scissors.rsi/meta.json +++ b/Resources/Textures/Objects/Tools/scissors.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC0-1.0", - "copyright": "Created by TheShuEd(github) for Space Station 14", + "copyright": "Created by TheShuEd(github) for Space Station 14, Inhand sprites by onesch", "size": { "x": 32, "y": 32 @@ -9,6 +9,14 @@ "states": [ { "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ] } diff --git a/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-left.png b/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-left.png new file mode 100644 index 0000000000000..34787a11f5f09 Binary files /dev/null and b/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-right.png b/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-right.png new file mode 100644 index 0000000000000..ce4df846069df Binary files /dev/null and b/Resources/Textures/Objects/Tools/spray_painter.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Tools/spray_painter.rsi/meta.json b/Resources/Textures/Objects/Tools/spray_painter.rsi/meta.json index 056ba0a8563e6..8a19107208efa 100644 --- a/Resources/Textures/Objects/Tools/spray_painter.rsi/meta.json +++ b/Resources/Textures/Objects/Tools/spray_painter.rsi/meta.json @@ -1,5 +1,5 @@ { - "copyright" : "Taken from https://github.com/tgstation/tgstation at commit a21274e56ae84b2c96e8b6beeca805df3d5402e8.", + "copyright" : "Taken from https://github.com/tgstation/tgstation at commit a21274e56ae84b2c96e8b6beeca805df3d5402e8, Inhand sprites by onesch", "license" : "CC-BY-SA-3.0", "size" : { "x" : 32, @@ -8,6 +8,14 @@ "states" : [ { "name" : "spray_painter" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 } ], "version" : 1 diff --git a/Resources/Textures/Structures/Machines/cryogenics.rsi/cover-off.png b/Resources/Textures/Structures/Machines/Medical/cryopod.rsi/cover-off.png similarity index 100% rename from Resources/Textures/Structures/Machines/cryogenics.rsi/cover-off.png rename to Resources/Textures/Structures/Machines/Medical/cryopod.rsi/cover-off.png diff --git a/Resources/Textures/Structures/Machines/cryogenics.rsi/cover-on.png b/Resources/Textures/Structures/Machines/Medical/cryopod.rsi/cover-on.png similarity index 100% rename from Resources/Textures/Structures/Machines/cryogenics.rsi/cover-on.png rename to Resources/Textures/Structures/Machines/Medical/cryopod.rsi/cover-on.png diff --git a/Resources/Textures/Structures/Machines/cryogenics.rsi/meta.json b/Resources/Textures/Structures/Machines/Medical/cryopod.rsi/meta.json similarity index 100% rename from Resources/Textures/Structures/Machines/cryogenics.rsi/meta.json rename to Resources/Textures/Structures/Machines/Medical/cryopod.rsi/meta.json diff --git a/Resources/Textures/Structures/Machines/cryogenics.rsi/pod-off.png b/Resources/Textures/Structures/Machines/Medical/cryopod.rsi/pod-off.png similarity index 100% rename from Resources/Textures/Structures/Machines/cryogenics.rsi/pod-off.png rename to Resources/Textures/Structures/Machines/Medical/cryopod.rsi/pod-off.png diff --git a/Resources/Textures/Structures/Machines/cryogenics.rsi/pod-on.png b/Resources/Textures/Structures/Machines/Medical/cryopod.rsi/pod-on.png similarity index 100% rename from Resources/Textures/Structures/Machines/cryogenics.rsi/pod-on.png rename to Resources/Textures/Structures/Machines/Medical/cryopod.rsi/pod-on.png diff --git a/Resources/Textures/Structures/Machines/cryogenics.rsi/pod-open.png b/Resources/Textures/Structures/Machines/Medical/cryopod.rsi/pod-open.png similarity index 100% rename from Resources/Textures/Structures/Machines/cryogenics.rsi/pod-open.png rename to Resources/Textures/Structures/Machines/Medical/cryopod.rsi/pod-open.png diff --git a/Resources/Textures/Structures/Machines/cryogenics.rsi/pod-panel.png b/Resources/Textures/Structures/Machines/Medical/cryopod.rsi/pod-panel.png similarity index 100% rename from Resources/Textures/Structures/Machines/cryogenics.rsi/pod-panel.png rename to Resources/Textures/Structures/Machines/Medical/cryopod.rsi/pod-panel.png diff --git a/Resources/Textures/Structures/Machines/juicer.rsi/juicer1.png b/Resources/Textures/Structures/Machines/grinder.rsi/grinder_beaker_attached.png similarity index 100% rename from Resources/Textures/Structures/Machines/juicer.rsi/juicer1.png rename to Resources/Textures/Structures/Machines/grinder.rsi/grinder_beaker_attached.png diff --git a/Resources/Textures/Structures/Machines/juicer.rsi/juicer0.png b/Resources/Textures/Structures/Machines/grinder.rsi/grinder_empty.png similarity index 100% rename from Resources/Textures/Structures/Machines/juicer.rsi/juicer0.png rename to Resources/Textures/Structures/Machines/grinder.rsi/grinder_empty.png diff --git a/Resources/Textures/Structures/Machines/grinder.rsi/meta.json b/Resources/Textures/Structures/Machines/grinder.rsi/meta.json new file mode 100644 index 0000000000000..9b9ed1dbe9e9d --- /dev/null +++ b/Resources/Textures/Structures/Machines/grinder.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "license":"CC-BY-SA-3.0", + "copyright":"https://github.com/tgstation/tgstation/commit/59f2a4e10e5ba36033c9734ddebfbbdc6157472d", + "version":1, + "size":{ + "x":32, + "y":32 + }, + "states":[ + { + "name":"grinder_empty" + }, + { + "name":"grinder_beaker_attached" + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Structures/Machines/juicer.rsi/meta.json b/Resources/Textures/Structures/Machines/juicer.rsi/meta.json deleted file mode 100644 index 6048f25646ef5..0000000000000 --- a/Resources/Textures/Structures/Machines/juicer.rsi/meta.json +++ /dev/null @@ -1 +0,0 @@ -{"license": "CC-BY-SA-3.0", "copyright": "https://github.com/tgstation/tgstation/commit/59f2a4e10e5ba36033c9734ddebfbbdc6157472d","version": 1, "size": {"x": 32, "y": 32}, "states": [{"name": "juicer0", "delays": [[1.0]]}, {"name": "juicer1", "delays": [[1.0]]}]} diff --git a/Resources/Textures/Structures/Storage/wall_locker.rsi/meta.json b/Resources/Textures/Structures/Storage/wall_locker.rsi/meta.json index b6ed63c7a3ed3..2466cc53d744c 100644 --- a/Resources/Textures/Structures/Storage/wall_locker.rsi/meta.json +++ b/Resources/Textures/Structures/Storage/wall_locker.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from shiptest at commmit https://github.com/shiptest-ss13/Shiptest/commit/440a15fb476a20d77ba28c1fe315c1b659032ce8, edited by Alekshhh", + "copyright": "Taken from shiptest at commmit https://github.com/shiptest-ss13/Shiptest/commit/440a15fb476a20d77ba28c1fe315c1b659032ce8, edited by Alekshhh, N2 lockers edited by Lamrr", "size": { "x": 32, "y": 32 @@ -27,6 +27,9 @@ { "name": "med_door" }, { "name": "med_open" }, { "name": "mixed_door" }, + { "name": "n2" }, + { "name": "n2_door" }, + { "name": "n2_open" }, { "name": "orange_door" }, { "name": "pink_door" }, { "name": "red_door" }, diff --git a/Resources/Textures/Structures/Storage/wall_locker.rsi/n2.png b/Resources/Textures/Structures/Storage/wall_locker.rsi/n2.png new file mode 100644 index 0000000000000..34d71d0586e52 Binary files /dev/null and b/Resources/Textures/Structures/Storage/wall_locker.rsi/n2.png differ diff --git a/Resources/Textures/Structures/Storage/wall_locker.rsi/n2_door.png b/Resources/Textures/Structures/Storage/wall_locker.rsi/n2_door.png new file mode 100644 index 0000000000000..e4d95b30d8d10 Binary files /dev/null and b/Resources/Textures/Structures/Storage/wall_locker.rsi/n2_door.png differ diff --git a/Resources/Textures/Structures/Storage/wall_locker.rsi/n2_open.png b/Resources/Textures/Structures/Storage/wall_locker.rsi/n2_open.png new file mode 100644 index 0000000000000..5d3050e30c732 Binary files /dev/null and b/Resources/Textures/Structures/Storage/wall_locker.rsi/n2_open.png differ