diff --git a/Content.Client/Backmen/AirDrop/AirDropSystem.cs b/Content.Client/Backmen/AirDrop/AirDropSystem.cs index 4fa93b902eb..b3e0ebd9290 100644 --- a/Content.Client/Backmen/AirDrop/AirDropSystem.cs +++ b/Content.Client/Backmen/AirDrop/AirDropSystem.cs @@ -240,7 +240,7 @@ private void Apply(Entity ent) if (compName == "Sprite") continue; - if (EntityManager.TryGetComponent(renderedItem, comp.Component.GetType(), out var sourceComp)) + if (TryComp(renderedItem, comp.Component.GetType(), out var sourceComp)) CopyComp(renderedItem, ent, sourceComp); } diff --git a/Content.Server/Administration/Commands/SetAdminOOC.cs b/Content.Server/Administration/Commands/SetAdminOOC.cs index f889ddc15d7..d2087f2b575 100644 --- a/Content.Server/Administration/Commands/SetAdminOOC.cs +++ b/Content.Server/Administration/Commands/SetAdminOOC.cs @@ -28,8 +28,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) if (string.IsNullOrEmpty(colorArg)) return; - var color = Color.TryFromHex(colorArg); - if (!color.HasValue) + if (!Color.TryFromHex(colorArg, out var color)) { shell.WriteError(Loc.GetString("shell-invalid-color-hex")); return; @@ -37,10 +36,10 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) var userId = shell.Player.UserId; // Save the DB - _dbManager.SaveAdminOOCColorAsync(userId, color.Value); + _dbManager.SaveAdminOOCColorAsync(userId, color); // Update the cached preference var prefs = _preferenceManager.GetPreferences(userId); - prefs.AdminOOCColor = color.Value; + prefs.AdminOOCColor = color; } } } diff --git a/Content.Server/Administration/Logs/AdminLogManager.cs b/Content.Server/Administration/Logs/AdminLogManager.cs index b8c2e2ff056..9933f7b9ad9 100644 --- a/Content.Server/Administration/Logs/AdminLogManager.cs +++ b/Content.Server/Administration/Logs/AdminLogManager.cs @@ -251,18 +251,42 @@ private async Task SaveLogs() _sawmill.Debug($"Saving {copy.Count} admin logs."); - if (_metricsEnabled) + try { - LogsSent.Inc(copy.Count); + if (_metricsEnabled) + { + LogsSent.Inc(copy.Count); - using (DatabaseUpdateTime.NewTimer()) + using (DatabaseUpdateTime.NewTimer()) + { + await task; + } + } + else { await task; - return; } } + catch (Exception ex) + { + _sawmill.Error($"Failed to save logs: {ex.Message}"); + _sawmill.Warning("Re-enqueueing logs and retrying at the next update."); - await task; + foreach (var log in copy) + { + if (log.RoundId == _currentRoundId) + { + _logQueue.Enqueue(log); + } + else + { + _preRoundLogQueue.Enqueue(log); + } + } + + Queue.Set(_logQueue.Count); + PreRoundQueue.Set(_preRoundLogQueue.Count); + } } public void RoundStarting(int id) diff --git a/Content.Server/Administration/UI/PermissionsEui.cs b/Content.Server/Administration/UI/PermissionsEui.cs index 149d20d5fb9..028662902db 100644 --- a/Content.Server/Administration/UI/PermissionsEui.cs +++ b/Content.Server/Administration/UI/PermissionsEui.cs @@ -240,6 +240,12 @@ private async Task HandleUpdateAdmin(UpdateAdmin ua) return; } + var (bad, rankName) = await FetchAndCheckRank(ua.RankId); + if (bad) + { + return; + } + var admin = await _db.GetAdminDataForAsync(ua.UserId); if (admin == null) { @@ -261,12 +267,6 @@ private async Task HandleUpdateAdmin(UpdateAdmin ua) await _db.UpdateAdminAsync(admin); var playerRecord = await _db.GetPlayerRecordByUserId(ua.UserId); - var (bad, rankName) = await FetchAndCheckRank(ua.RankId); - if (bad) - { - return; - } - var name = playerRecord?.LastSeenUserName ?? ua.UserId.ToString(); var title = ua.Title ?? ""; var flags = AdminFlagsHelper.PosNegFlagsText(ua.PosFlags, ua.NegFlags); diff --git a/Content.Server/Antag/AntagSelectionSystem.API.Assignment.cs b/Content.Server/Antag/AntagSelectionSystem.API.Assignment.cs index 060382fb6ec..36d5c1f18e5 100644 --- a/Content.Server/Antag/AntagSelectionSystem.API.Assignment.cs +++ b/Content.Server/Antag/AntagSelectionSystem.API.Assignment.cs @@ -341,7 +341,7 @@ public void SpawnGhostRoles(List antagRules, bool assert = false) } } - /// + /// [PublicAPI] public void SpawnGhostRoles(Entity gameRule, int playerCount, bool assert = false) { @@ -355,7 +355,7 @@ public void SpawnGhostRoles(Entity gameRule, int player /// Antags we want to make into ghost roles, with paired counts we need to spawn /// Whether we should throw if the spawner prototype doesn't exist. [PublicAPI] - public void SpawnGhostRoles(Entity gameRule, AntagCount[] antagRules, bool assert = false) + public void SpawnGhostRoles(Entity gameRule, List antagRules, bool assert = false) { foreach (var rule in antagRules) { diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index 5193b7cb1ae..5f03e92467c 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -312,24 +312,22 @@ private void AddGameRuleDefinitions(Entity gameRule, } } - private AntagCount[] GetAntags(Entity gameRule, + private List GetAntags(Entity gameRule, int playerCount) { var runningCount = 0; - var antags = new AntagCount[gameRule.Comp.Antags.Length]; + var antags = new List(gameRule.Comp.Antags.Length); // We assume that antag definitions are prioritized by order, and take up slots that other roles may take. // I.E for Nukies, it selects 1 commander which takes up 10 players, then one corpsman which takes up another 10, then we select X nukies based on the remaining player count. // This is how the system worked when I got here, and I decided not to change it to avoid fucking with team antag balance - var i = 0; foreach (var antag in gameRule.Comp.Antags) { if (!Proto.Resolve(antag.Proto, out var definition)) continue; // We do it this way in case our resolve fails. - antags[i] = (definition, GetTargetAntagCount(antag, playerCount, ref runningCount)); - i++; + antags.Add((definition, GetTargetAntagCount(antag, playerCount, ref runningCount))); } return antags; @@ -364,19 +362,19 @@ private void AssignAntags(Entity gameRule, IList gameRule, IList players, AntagCount[] antags) + private void AssignAntags(Entity gameRule, IList players, List antags) { AssignAntags(gameRule, GetWeightedPlayerPool(players), antags); } - private void AssignAntags(Entity gameRule, Dictionary weightedPool, AntagCount[] antags) + private void AssignAntags(Entity gameRule, Dictionary weightedPool, List antags) { while (RobustRandom.TryPickAndTake(weightedPool, out var session)) { AssignAntag(gameRule, session, ref antags); // Assignment complete, return early. - if (antags.Length == 0) + if (antags.Count == 0) return; } @@ -500,13 +498,13 @@ private bool AssignAntag(ICommonSession player, ref List antags) /// Selects and assigns antags from a list. /// Is private because it has it should only ever be run in very specific scenarios. /// - private bool AssignAntag(Entity gameRule, ICommonSession player, ref AntagCount[] antags) + private bool AssignAntag(Entity gameRule, ICommonSession player, ref List antags) { // If this session cannot be an antag, then get the next session! if (!TryGetValidAntagPreferences(player, out var prefs)) return false; - for (var i = antags.Length - 1; i >= 0; i--) + for (var i = antags.Count - 1; i >= 0; i--) { var antag = antags[i]; diff --git a/Content.Server/Backmen/Abilities/Psionics/PsionicAbilitiesSystem.cs b/Content.Server/Backmen/Abilities/Psionics/PsionicAbilitiesSystem.cs index 3e128eeb645..028ddd632f9 100644 --- a/Content.Server/Backmen/Abilities/Psionics/PsionicAbilitiesSystem.cs +++ b/Content.Server/Backmen/Abilities/Psionics/PsionicAbilitiesSystem.cs @@ -115,7 +115,7 @@ public void RemovePsionics(EntityUid uid, bool noEffect = false) { // component moment var comp = _componentFactory.GetComponent(compName); - if (EntityManager.TryGetComponent(uid, comp.GetType(), out var psionicPower)) + if (TryComp(uid, comp.GetType(), out var psionicPower)) RemComp(uid, psionicPower); } if (psionic.PsionicAbility != null) diff --git a/Content.Server/Backmen/Sponsors/SponsorsManager.cs b/Content.Server/Backmen/Sponsors/SponsorsManager.cs index d15b6150139..968eaa13aa3 100644 --- a/Content.Server/Backmen/Sponsors/SponsorsManager.cs +++ b/Content.Server/Backmen/Sponsors/SponsorsManager.cs @@ -57,8 +57,14 @@ public bool TryGetServerOocColor(NetUserId userId, [NotNullWhen(true)] out Color { if (_cachedSponsors.TryGetValue(userId, out var sponsor)) { - color = Color.TryFromHex(sponsor.OOCColor); - return color != null; + if (sponsor.OOCColor != null && Color.TryFromHex(sponsor.OOCColor, out var parsed)) + { + color = parsed; + return true; + } + + color = null; + return false; } color = null; @@ -214,9 +220,14 @@ public bool TryGetOocColor(NetUserId userId, [NotNullWhen(true)] out Color? colo return false; } - color = Color.TryFromHex(_cachedSponsors[userId].OOCColor); + if (Color.TryFromHex(_cachedSponsors[userId].OOCColor, out var parsed)) + { + color = parsed; + return true; + } - return color != null; + color = null; + return false; } public int GetExtraCharSlots(NetUserId userId) diff --git a/Content.Server/Backmen/Surgery/Pain/Systems/ServerPainSystem.cs b/Content.Server/Backmen/Surgery/Pain/Systems/ServerPainSystem.cs index 6554571053d..530581cd818 100644 --- a/Content.Server/Backmen/Surgery/Pain/Systems/ServerPainSystem.cs +++ b/Content.Server/Backmen/Surgery/Pain/Systems/ServerPainSystem.cs @@ -416,6 +416,7 @@ public override void RefreshNerveSystem(EntityUid nerveSystemUid, EntityUid body private void UpdateNerveSystemNerves(EntityUid uid, EntityUid body, NerveSystemComponent component) { + var previousNerves = component.Nerves.Keys.ToHashSet(); component.Nerves.Clear(); foreach (var bodyPartId in _body.GetWoundableTargets(body)) { @@ -423,12 +424,21 @@ private void UpdateNerveSystemNerves(EntityUid uid, EntityUid body, NerveSystemC continue; component.Nerves.Add(bodyPartId, nerve); + previousNerves.Remove(bodyPartId); nerve.ParentedNerveSystem = uid; - DirtyField(bodyPartId, nerve, nameof(NerveOrganComponent.ParentedNerveSystem)); UpdatePainFeels(bodyPartId, nerve); } + foreach (var orphan in previousNerves) + { + if (!NerveQuery.TryComp(orphan, out var nerve)) + continue; + + if (nerve.ParentedNerveSystem == uid) + nerve.ParentedNerveSystem = EntityUid.Invalid; + } + CleanupOrphanPainModifiers(uid, body, component); } @@ -516,7 +526,9 @@ private void UpdateNerveSystemPain(EntityUid uid, NerveSystemComponent? nerveSys nerveSys.ReactionUpdateTime = Timing.CurTime + nerveSys.PainReactionTime; nerveSys.Pain = newPain; - DirtyField(uid, nerveSys, nameof(NerveSystemComponent.Pain)); + // Full Dirty (not DirtyField): RT 283.1 (#6685) can emit field deltas when the client's + // fromTick <= CreationTick, which PVS asserts against. Unclassified dirty forces a full state. + Dirty(uid, nerveSys); if (!_consciousness.SetConsciousnessModifier( organ.Body.Value, diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index 6e4738fc514..615f2ed5efd 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -132,7 +132,7 @@ public override void CloneComponents( // If the original does not have the component, then the clone shouldn't have it either. RemComp(clone, componentRegistration.Type); - if (EntityManager.TryGetComponent(original, componentRegistration.Type, out var sourceComp)) // Does the original have this component? + if (TryComp(original, componentRegistration.Type, out var sourceComp)) // Does the original have this component? { CopyComp(original, clone, sourceComp); } diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index c4d7066b9d0..1c84924916e 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -856,7 +856,7 @@ public async Task AddAdminLogs(List logs) if (attempt >= maxRetryAttempts) { _opsLog.Error($"Max retry attempts reached. Failed to save {logs.Count} admin logs."); - return; + throw; } _opsLog.Warning($"Retrying in {retryDelay.TotalSeconds} seconds..."); diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index b59ac40f22c..100d2b67926 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Administration.Logs; +using Content.Server.Doors.Systems; using Content.Server.NodeContainer.EntitySystems; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -79,6 +80,8 @@ public override void Initialize() SubscribeLocalEvent(OnElectrifiedAttacked); SubscribeLocalEvent(OnElectrifiedHandInteract); SubscribeLocalEvent(OnElectrifiedInteractUsing); + SubscribeLocalEvent(OnElectrifiedActivateInWorld, before: [typeof(AirlockSystem), typeof(DoorSystem)]); + SubscribeLocalEvent(OnRandomInsulationMapInit); SubscribeLocalEvent(OnLightAttacked); @@ -198,6 +201,15 @@ private void OnElectrifiedInteractUsing(EntityUid uid, ElectrifiedComponent elec TryDoElectrifiedAct(uid, args.User, siemens, electrified); } + private void OnElectrifiedActivateInWorld(EntityUid uid, ElectrifiedComponent electrified, ActivateInWorldEvent args) + { + if (!electrified.OnActivateInWorld || args.Handled) + return; + + if (TryDoElectrifiedAct(uid, args.User, 1, electrified)) + args.Handled = true; + } + public bool TryDoElectrifiedAct(EntityUid uid, EntityUid targetUid, float siemens = 1, ElectrifiedComponent? electrified = null, diff --git a/Content.Server/Materials/MaterialReclaimerSystem.cs b/Content.Server/Materials/MaterialReclaimerSystem.cs index 332177b667a..8e30a4d7755 100644 --- a/Content.Server/Materials/MaterialReclaimerSystem.cs +++ b/Content.Server/Materials/MaterialReclaimerSystem.cs @@ -200,6 +200,8 @@ public override void Reclaim(EntityUid uid, SpawnChemicalsFromComposition(uid, item, completion, true, component, xform); } + var eventArgs = new DestructionEventArgs(); + RaiseLocalEvent(item, eventArgs); QueueDel(item); } diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs index a8be65daa99..483e53f73ff 100644 --- a/Content.Server/Mind/MindSystem.cs +++ b/Content.Server/Mind/MindSystem.cs @@ -241,6 +241,11 @@ public override void TransferTo(EntityUid mindId, EntityUid? entity, bool ghostC // Yes this control flow sucks. mind.VisitingEntity = null; RemComp(entity!.Value); + // If you are transferring to your own ghost then you can no longer return to body + if (TryComp(entity.Value, out GhostComponent? ghostComponent)) + { + _ghosts.SetCanReturnToBody((entity.Value, ghostComponent), false); + } } else if (mind.VisitingEntity != null && (ghostCheckOverride // to force mind transfer, for example from ControlMobVerb @@ -261,6 +266,10 @@ public override void TransferTo(EntityUid mindId, EntityUid? entity, bool ghostC if (entity != null) { + if (component!.Mind.HasValue && TryComp(component!.Mind.Value, out MindComponent? newMind)) + { + TransferTo(component!.Mind.Value, newMind?.VisitingEntity); + } component!.Mind = mindId; component.HasMind = true; mind.OwnedEntity = entity; diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index f4da30bf170..8b0f91ee931 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -60,20 +60,6 @@ private void OnEntityUnpaused(Entity ent, ref EntityUnpause UpdateMoverStatus((ent, ent.Comp)); } - protected override void OnInputMoverCanMoveUpdated(Entity ent, ref CanMoveUpdatedEvent args) - { - base.OnInputMoverCanMoveUpdated(ent, ref args); - - if (!args.CanMove) - { - // Remove from active mover query when entity cannot move - RemCompDeferred(ent); - return; - } - - UpdateMoverStatus((ent, ent.Comp)); - } - protected override void OnMoverStartup(Entity ent, ref ComponentStartup args) { base.OnMoverStartup(ent, ref args); diff --git a/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs b/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs index 98e72b6a05a..a7e3efb0832 100644 --- a/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs +++ b/Content.Server/Sandbox/Commands/ColorNetworkCommand.cs @@ -56,14 +56,13 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) return; } - var color = Color.TryFromHex(args[2]); - if (!color.HasValue) + if (!Color.TryFromHex(args[2], out var color)) { shell.WriteError(Loc.GetString("shell-invalid-color-hex")); return; } - PaintNodes(nodeContainerComponent, nodeGroupId, color.Value); + PaintNodes(nodeContainerComponent, nodeGroupId, color); } private void PaintNodes(NodeContainerComponent nodeContainerComponent, NodeGroupID nodeGroupId, Color color) diff --git a/Content.Server/Station/Systems/StationSystem.cs b/Content.Server/Station/Systems/StationSystem.cs index f7b877cd364..2596aafd7c7 100644 --- a/Content.Server/Station/Systems/StationSystem.cs +++ b/Content.Server/Station/Systems/StationSystem.cs @@ -196,7 +196,7 @@ public Filter GetInOwningStation(EntityUid source, float range = 32f) if (TryComp(station, out var data)) { - return GetInStation(data); + return GetInStation(data, range); } return Filter.Empty(); @@ -233,11 +233,11 @@ public Filter GetInStation(StationDataComponent dataComponent, float range = 32f } var (worldPos, worldRot) = _transform.GetWorldPositionRotation(gridXform); - var localBounds = grid.LocalAABB.Enlarged(range); + var worldBounds = grid.LocalAABB.Enlarged(range).Translated(worldPos); // Create a rotated box using the grid's transform var rotatedBounds = new Box2Rotated( - localBounds, + worldBounds, worldRot, worldPos); diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index 0aec402ecd1..05e0f57464f 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -308,13 +308,7 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) if (!HasComp(target) && !hasMind) //this specific component gives build test trouble so pop off, ig { - //yet more hardcoding. Visit zombie.ftl for more information. - var ghostRole = EnsureComp(target); - EnsureComp(target); - ghostRole.RoleName = Loc.GetString("zombie-generic"); - ghostRole.RoleDescription = Loc.GetString("zombie-role-desc"); - ghostRole.RoleRules = Loc.GetString("zombie-role-rules"); - ghostRole.MindRoles.Add(MindRoleZombie); + MakeGhostRole(target); } if (TryComp(target, out var handsComp)) diff --git a/Content.Server/Zombies/ZombieSystem.cs b/Content.Server/Zombies/ZombieSystem.cs index ec0051e9a9a..b7b46c08a09 100644 --- a/Content.Server/Zombies/ZombieSystem.cs +++ b/Content.Server/Zombies/ZombieSystem.cs @@ -29,6 +29,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; +using Content.Server.Ghost.Roles.Components; namespace Content.Server.Zombies { @@ -334,14 +335,31 @@ private void OnMindAdded(Entity ent, ref MindAddedMessage args) } // Remove the role when getting cloned, getting gibbed and borged, or leaving the body via any other method. + // We also need to make sure the zombie is a ghost role because zombies with minds do not get a ghostrolecomponent private void OnMindRemoved(Entity ent, ref MindRemovedMessage args) { _role.MindRemoveRole((args.Mind.Owner, args.Mind.Comp)); + MakeGhostRole(ent.Owner); } private void OnAttemptConvert(Entity ent, ref AttemptConvertRevolutionaryEvent args) { args.Cancelled = true; } + + /// + /// Makes the target entity a zombie ghost role. Should only be fired when the entity does not have a mind. + /// + private void MakeGhostRole(EntityUid ent) + { + //yet more hardcoding. Visit zombie.ftl for more information. + var ghostRole = EnsureComp(ent); + EnsureComp(ent); + + ghostRole.RoleName = Loc.GetString("zombie-generic"); + ghostRole.RoleDescription = Loc.GetString("zombie-role-desc"); + ghostRole.RoleRules = Loc.GetString("zombie-role-rules"); + ghostRole.MindRoles.Add(MindRoleZombie); + } } } diff --git a/Content.Shared/Backmen/Surgery/Body/Organs/OrganEffectSystem.cs b/Content.Shared/Backmen/Surgery/Body/Organs/OrganEffectSystem.cs index 1bb4b6e618f..1eb56d8475c 100644 --- a/Content.Shared/Backmen/Surgery/Body/Organs/OrganEffectSystem.cs +++ b/Content.Shared/Backmen/Surgery/Body/Organs/OrganEffectSystem.cs @@ -83,7 +83,6 @@ private void AddComponents(EntityUid body, continue; var newComp = (Component) _serManager.CreateCopy(comp.Component, notNullableOverride: true); - newComp.Owner = body; AddComp(body, newComp, true); effectComp.Active[key] = comp; if (newComp.NetSyncEnabled) diff --git a/Content.Shared/Backmen/Surgery/Pain/Components/NerveOrganComponent.cs b/Content.Shared/Backmen/Surgery/Pain/Components/NerveOrganComponent.cs index e04888c6984..f3f612771be 100644 --- a/Content.Shared/Backmen/Surgery/Pain/Components/NerveOrganComponent.cs +++ b/Content.Shared/Backmen/Surgery/Pain/Components/NerveOrganComponent.cs @@ -26,8 +26,9 @@ public sealed partial class NerveOrganComponent : Component /// /// Nerve system, to which this nerve is parented. + /// Server-only: networking a deleted brain EntityUid trips MetaData Resolve errors in PVS GetState. /// - [AutoNetworkedField, ViewVariables(VVAccess.ReadOnly)] + [ViewVariables(VVAccess.ReadOnly)] public EntityUid ParentedNerveSystem; } diff --git a/Content.Shared/Backmen/Surgery/Pain/Systems/PainSystem.cs b/Content.Shared/Backmen/Surgery/Pain/Systems/PainSystem.cs index fca99c95e0c..cddd9411d0d 100644 --- a/Content.Shared/Backmen/Surgery/Pain/Systems/PainSystem.cs +++ b/Content.Shared/Backmen/Surgery/Pain/Systems/PainSystem.cs @@ -40,13 +40,17 @@ private void OnPainImmuneHealthExamined(Entity ent, ref Hea private void OnNerveSystemTerminating(Entity ent, ref EntityTerminatingEvent args) { - foreach (var (nerveUid, _) in ent.Comp.Nerves.ToArray()) + // Clear every nerve that still points here — Nerves may be incomplete after amputations/rebuilds. + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out var nerve)) { - if (!NerveQuery.TryComp(nerveUid, out var nerve)) + if (nerve.ParentedNerveSystem != ent.Owner) continue; nerve.ParentedNerveSystem = EntityUid.Invalid; } + + ent.Comp.Nerves.Clear(); } private void OnNerveSystemAfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args) diff --git a/Content.Shared/Backmen/Surgery/SharedSurgerySystem.Steps.cs b/Content.Shared/Backmen/Surgery/SharedSurgerySystem.Steps.cs index 365d16f43d1..27430162a49 100644 --- a/Content.Shared/Backmen/Surgery/SharedSurgerySystem.Steps.cs +++ b/Content.Shared/Backmen/Surgery/SharedSurgerySystem.Steps.cs @@ -1632,7 +1632,7 @@ private bool AnyHaveComp(List tools, IComponent component, out Entity { foreach (var tool in tools) { - if (EntityManager.TryGetComponent(tool, component.GetType(), out var found) && found is ISurgeryToolComponent toolComp) + if (TryComp(tool, component.GetType(), out var found) && found is ISurgeryToolComponent toolComp) { withComp = tool; speed = toolComp.Speed; diff --git a/Content.Shared/CCVar/CCVars.Shuttle.cs b/Content.Shared/CCVar/CCVars.Shuttle.cs index ad8e9b2d70c..23193e2cfb5 100644 --- a/Content.Shared/CCVar/CCVars.Shuttle.cs +++ b/Content.Shared/CCVar/CCVars.Shuttle.cs @@ -111,8 +111,9 @@ public sealed partial class CCVars /// The maximum a grid can have before it becomes unable to FTL. /// Any value equal to or less than zero will disable this check. /// + [CVarControl(AdminFlags.VarEdit)] public static readonly CVarDef FTLMassLimit = - CVarDef.Create("shuttle.mass_limit", 300f, CVar.SERVERONLY); + CVarDef.Create("shuttle.mass_limit", 480000f, CVar.SERVERONLY); /// /// How long to knock down entities for if they aren't buckled when FTL starts and stops. diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index dfe02c9cd0e..3e8bd6db280 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Content.Shared.Materials; using Content.Shared.Popups; using Content.Shared.Verbs; using Content.Shared.Whitelist; @@ -622,6 +623,29 @@ public bool TryEjectToHands(EntityUid uid, ItemSlot slot, EntityUid? user, bool return true; } + /// + /// Unlocks all slots and ejects items from them on the floor. + /// + public void EjectFromAllSlots(Entity entity) + { + EjectFromAllSlots(entity, _ => true); + } + + /// + /// Unlocks all slots and ejects items from them on the floor. Works only while returns true. + /// + private void EjectFromAllSlots(Entity entity, Func shouldEject) + { + foreach (var slot in entity.Comp.Slots.Values) + { + if (slot.HasItem && shouldEject(slot)) + { + SetLock(entity.Owner, slot, false, entity.Comp); + TryEject(entity.Owner, slot, null, out _); + } + } + } + #endregion #region Verbs @@ -827,14 +851,7 @@ private void HandleButtonPressed(EntityUid uid, ItemSlotsComponent component, It /// private void OnBreak(EntityUid uid, ItemSlotsComponent component, EntityEventArgs args) { - foreach (var slot in component.Slots.Values) - { - if (slot.EjectOnBreak && slot.HasItem) - { - SetLock(uid, slot, false, component); - TryEject(uid, slot, null, out var _); - } - } + EjectFromAllSlots((uid, component), slot => slot.EjectOnBreak); } /// diff --git a/Content.Shared/Electrocution/Components/ElectrifiedComponent.cs b/Content.Shared/Electrocution/Components/ElectrifiedComponent.cs index c780a263dea..10c8814d3ed 100644 --- a/Content.Shared/Electrocution/Components/ElectrifiedComponent.cs +++ b/Content.Shared/Electrocution/Components/ElectrifiedComponent.cs @@ -42,6 +42,12 @@ public sealed partial class ElectrifiedComponent : Component [DataField, AutoNetworkedField] public bool OnInteractUsing = true; + /// + /// Whether the entity should electrocute on activation in world. + /// + [DataField, AutoNetworkedField] + public bool OnActivateInWorld = false; + /// /// Indicates if the entity requires power to function /// diff --git a/Content.Shared/Fluids/SharedPuddleSystem.cs b/Content.Shared/Fluids/SharedPuddleSystem.cs index 99bc19d5002..7de50af86df 100644 --- a/Content.Shared/Fluids/SharedPuddleSystem.cs +++ b/Content.Shared/Fluids/SharedPuddleSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.FixedPoint; using Content.Shared.Fluids.Components; using Content.Shared.Friction; +using Content.Shared.Maps; using Content.Shared.Movement.Components; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; @@ -43,10 +44,13 @@ public abstract partial class SharedPuddleSystem : EntitySystem [Dependency] private StepTriggerSystem _stepTrigger = default!; [Dependency] private TileFrictionController _tile = default!; [Dependency] private INetManager _net = default!; + [Dependency] private SharedMapSystem _map = default!; + [Dependency] private TurfSystem _turf = default!; [Dependency] private EntityQuery _stepTriggerQuery = default!; [Dependency] private EntityQuery _reactiveQuery = default!; [Dependency] private EntityQuery _evaporationQuery = default!; + [Dependency] private EntityQuery _puddleQuery = default!; private ProtoId[] _standoutReagents = []; @@ -185,6 +189,25 @@ private void OnEntRemoved(Entity ent, ref EntRemovedFromContain ent.Comp.Solution = null; } + [SubscribeLocalEvent] + private void OnTileChanged(ref TileChangedEvent ev) + { + foreach (var change in ev.Changes) + { + if (!_turf.IsSpace(change.NewTile)) + continue; + + var anchored = _map.GetAnchoredEntitiesEnumerator(ev.Entity, ev.Entity.Comp, change.GridIndices); + while (anchored.MoveNext(out var ent)) + { + if (!_puddleQuery.HasComponent(ent)) + continue; + + PredictedQueueDel(ent); + } + } + } + private void UpdateAppearance(Entity ent) { var (uid, puddle, appearance) = ent; diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs index b6584cfdc80..70f9476dec1 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs @@ -114,12 +114,12 @@ public virtual void RemoveHand(Entity ent, string handName) TryDrop(ent, handName, null, false); - if (!ent.Comp.Hands.Remove(handName)) - return; - if (ContainerSystem.TryGetContainer(ent, handName, out var container)) ContainerSystem.ShutdownContainer(container); + if (!ent.Comp.Hands.Remove(handName)) + return; + ent.Comp.SortedHands.Remove(handName); if (ent.Comp.ActiveHandId == handName) TrySetActiveHand(ent, ent.Comp.SortedHands.FirstOrDefault()); diff --git a/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs b/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs index 5d747f6e8ae..ebc0a16365d 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs @@ -34,7 +34,13 @@ private void OnRelayCanMoveUpdated(Entity ent, ref Can protected virtual void OnInputMoverCanMoveUpdated(Entity ent, ref CanMoveUpdatedEvent args) { if (!args.CanMove) - SetMoveInput(ent, MoveButtons.None); + { + // Remove from active mover query when entity cannot move + RemCompDeferred(ent); + return; + } + + UpdateMoverStatus((ent, ent.Comp)); } /// diff --git a/Content.Shared/ParcelWrap/Systems/ParcelWrappingSystem.ParcelWrap.cs b/Content.Shared/ParcelWrap/Systems/ParcelWrappingSystem.ParcelWrap.cs index 410970147a6..20062f2f995 100644 --- a/Content.Shared/ParcelWrap/Systems/ParcelWrappingSystem.ParcelWrap.cs +++ b/Content.Shared/ParcelWrap/Systems/ParcelWrappingSystem.ParcelWrap.cs @@ -97,7 +97,7 @@ private bool TryStartWrapDoAfter(EntityUid user, Entity wra if (target == user) { var selfMsg = Loc.GetString("parcel-wrap-popup-being-wrapped-self"); - _popup.PopupClient(selfMsg, user, user); + _popup.PopupEntity(selfMsg, user, user); } else { @@ -143,7 +143,7 @@ private void WrapInternal(EntityUid user, Entity wrapper, E // Spawn the actual parcel entity. var targetTransform = Transform(target); - var spawned = Spawn(GetParcelPrototype(wrapper, target), targetTransform.Coordinates); + var spawned = SpawnAtPosition(GetParcelPrototype(wrapper, target), targetTransform.Coordinates); _transform.SetLocalRotation(spawned, targetTransform.LocalRotation); // If the target is in a container, try to put the parcel in its place in the container. diff --git a/Content.Shared/Placeable/PlaceableSurfaceSystem.cs b/Content.Shared/Placeable/PlaceableSurfaceSystem.cs index 0d2152bf1e0..84eea2be6cf 100644 --- a/Content.Shared/Placeable/PlaceableSurfaceSystem.cs +++ b/Content.Shared/Placeable/PlaceableSurfaceSystem.cs @@ -1,15 +1,17 @@ using System.Numerics; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; +using Content.Shared.Random.Helpers; using Content.Shared.Storage; using Content.Shared.Storage.Components; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Shared.Placeable; public sealed partial class PlaceableSurfaceSystem : EntitySystem { - [Dependency] private IRobustRandom _random = default!; + [Dependency] private IGameTiming _timing = default!; [Dependency] private SharedHandsSystem _handsSystem = default!; [Dependency] private SharedTransformSystem _transformSystem = default!; @@ -109,7 +111,9 @@ private void OnDump(Entity ent, ref DumpEvent args) foreach (var entity in args.DumpQueue) { - _transformSystem.SetWorldPositionRotation(entity, targetPos + _random.NextVector2Box() / 4, targetRot); + var rand = SharedRandomExtensions.PredictedRandom(_timing, GetNetEntity(entity)); + var offset = new Vector2(rand.NextFloat(-1f, 1f), rand.NextFloat(-1f, 1f)) / 4; + _transformSystem.SetWorldPositionRotation(entity, targetPos + offset, targetRot); } } } diff --git a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs index 236ae15dd81..abe97d6671f 100644 --- a/Content.Shared/Storage/EntitySystems/DumpableSystem.cs +++ b/Content.Shared/Storage/EntitySystems/DumpableSystem.cs @@ -1,12 +1,15 @@ using System.Linq; +using System.Numerics; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared.Item; +using Content.Shared.Random.Helpers; using Content.Shared.Storage.Components; using Content.Shared.Verbs; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Shared.Storage.EntitySystems; @@ -14,7 +17,7 @@ namespace Content.Shared.Storage.EntitySystems; public sealed partial class DumpableSystem : EntitySystem { [Dependency] private IPrototypeManager _prototypeManager = default!; - [Dependency] private IRobustRandom _random = default!; + [Dependency] private IGameTiming _timing = default!; [Dependency] private SharedAudioSystem _audio = default!; [Dependency] private SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private SharedTransformSystem _transformSystem = default!; @@ -66,7 +69,7 @@ private void AddDumpVerb(EntityUid uid, DumpableComponent dumpable, GetVerbsEven StartDoAfter(uid, args.Target, args.User, dumpable);//Had multiplier of 0.6f }, Text = Loc.GetString("dump-verb-name"), - Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/drop.svg.192dpi.png")), + Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/drop.svg.192dpi.png")), }; args.Verbs.Add(verb); } @@ -115,7 +118,7 @@ private void StartDoAfter(EntityUid storageUid, EntityUid targetUid, EntityUid u delay += itemSize.Weight; } - delay *= (float) dumpable.DelayPerItem.TotalSeconds * dumpable.Multiplier; + delay *= (float)dumpable.DelayPerItem.TotalSeconds * dumpable.Multiplier; _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, userUid, delay, new DumpableDoAfterEvent(), storageUid, target: targetUid, used: storageUid) { @@ -129,7 +132,8 @@ private void OnDoAfter(EntityUid uid, DumpableComponent component, DumpableDoAft if (args.Handled || args.Cancelled || !TryComp(uid, out var storage) || storage.Container.ContainedEntities.Count == 0 || args.Args.Target is not { } target) return; - var dumpQueue = new Queue(storage.Container.ContainedEntities); + // TODO: Remove OrderBy when this issue is fixed in RT https://github.com/space-wizards/RobustToolbox/issues/6241 + var dumpQueue = new Queue(storage.Container.ContainedEntities.OrderBy(e => GetNetEntity(e))); var evt = new DumpEvent(dumpQueue, args.Args.User, false, false); RaiseLocalEvent(target, ref evt); @@ -141,7 +145,9 @@ private void OnDoAfter(EntityUid uid, DumpableComponent component, DumpableDoAft foreach (var entity in dumpQueue) { var transform = Transform(entity); - _transformSystem.SetWorldPositionRotation(entity, targetPos + _random.NextVector2Box() / 4, _random.NextAngle(), transform); + var rand = SharedRandomExtensions.PredictedRandom(_timing, GetNetEntity(entity)); + var offset = new Vector2(rand.NextFloat(-1f, 1f), rand.NextFloat(-1f, 1f)) / 4; + _transformSystem.SetWorldPositionRotation(entity, targetPos + offset, rand.NextAngle(), transform); } return; diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 4b56fce9dbd..b6d9e0ce7ab 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -568,7 +568,8 @@ private void AfterInteract(EntityUid uid, StorageComponent storageComp, AfterInt _entityLookupSystem.GetEntitiesInRange(args.ClickLocation, storageComp.AreaInsertRadius, _entSet, LookupFlags.Dynamic | LookupFlags.Sundries); var delay = 0f; - foreach (var entity in _entSet) + // TODO: Remove OrderBy when this issue is fixed in RT https://github.com/space-wizards/RobustToolbox/issues/6241 + foreach (var entity in _entSet.OrderBy(e => GetNetEntity(e))) { if (entity == args.User || !_itemQuery.TryGetComponent(entity, out var itemComp) // Need comp to get item size to get weight @@ -1008,7 +1009,8 @@ public void TransferEntities(EntityUid source, EntityUid target, EntityUid? user || Resolve(target, ref targetLock, false) && targetLock.Locked) return; - foreach (var entity in entities.ToArray()) + // TODO: Remove OrderBy when this issue is fixed in RT https://github.com/space-wizards/RobustToolbox/issues/6241 + foreach (var entity in entities.ToArray().OrderBy(e => GetNetEntity(e))) { Insert(target, entity, out _, user: user, targetComp, playSound: false); } @@ -1207,7 +1209,8 @@ public bool Insert( var toInsertCount = insertStack.Count; - foreach (var ent in storageComp.Container.ContainedEntities) + // TODO: Remove OrderBy when this issue is fixed in RT https://github.com/space-wizards/RobustToolbox/issues/6241 + foreach (var ent in storageComp.Container.ContainedEntities.OrderBy(e => GetNetEntity(e))) { if (!_stackQuery.TryGetComponent(ent, out var containedStack)) continue; diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs index a6f7f049d9d..50f7c24d406 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Revolver.cs @@ -147,7 +147,7 @@ public bool TryRevolverInsert(Entity ent, EntityU ent.Comp.AmmoSlots[index] = ammoEnt.Value; Containers.Insert(ammoEnt.Value, ent.Comp.AmmoContainer); - SetChamber(ent, insertEnt, index); + SetChamber(ent, ammoEnt.Value, index); if (ev.Ammo.Count == 0) break; diff --git a/Resources/Changelog/ChangelogBkm.yml b/Resources/Changelog/ChangelogBkm.yml index fbc26e44060..a4ffa19fb4d 100644 --- a/Resources/Changelog/ChangelogBkm.yml +++ b/Resources/Changelog/ChangelogBkm.yml @@ -3703,3 +3703,11 @@ type: Fix id: 247 time: '2026-07-17T14:13:00.0000000+00:00' +- author: Rxup + changes: + - message: "Обновлён игровой движок до RobustToolbox 283.1.0." + type: Tweak + - message: "Cherry-pick ключевых upstream-фиксов: permissions, antag selection, morgue exploit, admin logs, slip/shock, recycling, storage mispredict, mind take-over, cloning, GetInStation и др." + type: Fix + id: 248 + time: '2026-07-18T14:30:00.0000000+00:00' diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 161536fa5b6..8a9b9b5c2c7 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -213,14 +213,6 @@ uplink-freedom-implanter-name = Freedom Implanter uplink-freedom-implanter-desc = Get away from those nasty sec officers with this three use implant! uplink-combat-training-name = Combat Training Implanter uplink-combat-training-desc = A Syndicate implant that improves aimed-shot accuracy to Security-tier levels (~35–40%). -combat-training-implanter-name = Combat Training Implanter -combat-training-implanter-desc = A Syndicate implanter that installs a combat training implant. -combat-training-implant-name = Combat Training Implant -combat-training-implant-desc = Improves aimed-shot accuracy to Security-tier levels (~35–40%). -combat-marksmanship-implanter-admeme-name = Combat Marksmanship Implanter -combat-marksmanship-implanter-admeme-desc = Admin implanter that grants 100% accuracy on the targeted body part. -combat-marksmanship-implant-admeme-name = Combat Marksmanship Implant -combat-marksmanship-implant-admeme-desc = Admin implant that guarantees hits on the aimed body part. uplink-scram-implanter-name = Scram Implanter uplink-scram-implanter-desc = A 2-use implant which teleports you within a large radius. Attempts to teleport you onto an unobstructed tile. May sometimes fail to do so. Life insurance not included. diff --git a/Resources/Locale/ru-RU/store/uplink-catalog.ftl b/Resources/Locale/ru-RU/store/uplink-catalog.ftl index 48201fc8ef6..2c76f03a3b1 100644 --- a/Resources/Locale/ru-RU/store/uplink-catalog.ftl +++ b/Resources/Locale/ru-RU/store/uplink-catalog.ftl @@ -151,14 +151,6 @@ uplink-freedom-implanter-name = Имплантер Свобода uplink-freedom-implanter-desc = Сбегите от этих противных сотрудников СБ при помощи этого импланта, который можно использовать аж три раза! uplink-combat-training-name = Имплантер боевой подготовки uplink-combat-training-desc = Синдикатский имплант, повышающий точность стрельбы по выбранной части тела до уровня СБ (~35–40%). -combat-training-implanter-name = Имплантер боевой подготовки -combat-training-implanter-desc = Синдикатский имплантер, повышающий точность стрельбы. -combat-training-implant-name = Имплант боевой подготовки -combat-training-implant-desc = Повышает точность стрельбы по выбранной части тела до уровня СБ. -combat-marksmanship-implanter-admeme-name = Имплантер меткой стрельбы -combat-marksmanship-implanter-admeme-desc = Админский имплантер — 100% точность по выбранной части тела. -combat-marksmanship-implant-admeme-name = Имплант меткой стрельбы -combat-marksmanship-implant-admeme-desc = Админский имплант — 100% попадание в прицеленную часть тела. uplink-scram-implanter-name = Имплантер Побег uplink-scram-implanter-desc = Двухразовый имплант, который телепортирует вас в большом радиусе. Попытается телепортировать вас на незанятое место. Иногда сбоит. Страхование жизни не прилагается. uplink-dna-scrambler-implanter-name = Имплантер Миксер ДНК diff --git a/Resources/Prototypes/Entities/Clothing/Belt/belts.yml b/Resources/Prototypes/Entities/Clothing/Belt/belts.yml index 9c627741854..4fb2f706804 100644 --- a/Resources/Prototypes/Entities/Clothing/Belt/belts.yml +++ b/Resources/Prototypes/Entities/Clothing/Belt/belts.yml @@ -21,6 +21,7 @@ ejectVerbText: sheath-eject-verb insertSound: /Audio/Items/sheath.ogg ejectSound: /Audio/Items/unsheath.ogg + ejectOnBreak: true whitelist: tags: - CaptainSabre diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/base_clothingshoes.yml b/Resources/Prototypes/Entities/Clothing/Shoes/base_clothingshoes.yml index 220ab4f1b15..bc85b251109 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/base_clothingshoes.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/base_clothingshoes.yml @@ -78,6 +78,7 @@ item: name: clothing-boots-sidearm priority: 4 + ejectOnBreak: true whitelist: tags: - Knife diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml b/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml index 99e6117e258..1ad8dc1ce8f 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/specific.yml @@ -19,6 +19,7 @@ slots: item: name: clothing-boots-sidearm + ejectOnBreak: true whitelist: tags: - Knife diff --git a/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml b/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml index 85f7486d3f5..161ee19ea6c 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml @@ -54,7 +54,6 @@ name: id-card-console-privileged-id ejectSound: /Audio/Machines/id_swipe.ogg insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg - ejectOnBreak: true swap: false whitelist: components: @@ -136,7 +135,6 @@ startingItem: UniversalIDCard ejectSound: /Audio/Machines/id_swipe.ogg insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg - ejectOnBreak: true swap: false whitelist: components: @@ -164,7 +162,6 @@ startingItem: XenoborgIDCard ejectSound: /Audio/Machines/id_swipe.ogg insertSound: /Audio/Weapons/Guns/MagIn/batrifle_magin.ogg - ejectOnBreak: true disableEject: true swap: false whitelist: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index 5f8daccfc77..2c02da2eacb 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -104,6 +104,7 @@ - type: Electrified enabled: false usesApcPower: true + onActivateInWorld: true - type: WiresPanel - type: WiresPanelSecurity - type: Wires diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml index e4c8293fcb3..c2aaa3e0b9e 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/base_structurecomputers.yml @@ -47,10 +47,6 @@ computerLayerKeys: True: { visible: true, shader: unshaded } False: { visible: true, shader: shaded } - enum.WiresVisuals.MaintenancePanelState: - enum.WiresVisualLayers.MaintenancePanel: - True: { visible: false } - False: { visible: true } - type: LitOnPowered - type: PointLight radius: 1.5 diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 591cd954cfb..82a547a244f 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -34,10 +34,6 @@ 2: { state: alert-1 } 3: { state: alert-2 } 4: { state: alert-2 } - enum.WiresVisuals.MaintenancePanelState: - enum.WiresVisualLayers.MaintenancePanel: - True: { visible: false } - False: { visible: true } - type: AtmosAlertsComputer - type: ActivatableUI singleUser: true @@ -1319,10 +1315,6 @@ computerLayerKeys: True: { visible: true, shader: unshaded } False: { visible: true } - enum.WiresVisuals.MaintenancePanelState: - enum.WiresVisualLayers.MaintenancePanel: - True: { visible: false } - False: { visible: true } - type: SalvageExpeditionConsole - type: ActivatableUI key: enum.SalvageConsoleUiKey.Expedition @@ -1748,10 +1740,6 @@ Occupied: { state: ai-fixer-full } Rebooting: { state: ai-fixer-404 } Dead: { state: ai-fixer-404 } - enum.WiresVisuals.MaintenancePanelState: - enum.WiresVisualLayers.MaintenancePanel: - True: { visible: false } - False: { visible: true } - type: ApcPowerReceiver powerLoad: 50 - type: PowerState diff --git a/Resources/Prototypes/Entities/Structures/Machines/holopad.yml b/Resources/Prototypes/Entities/Structures/Machines/holopad.yml index 5c9c5c21b67..e377ccdc750 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/holopad.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/holopad.yml @@ -49,10 +49,6 @@ enum.PowerDeviceVisualLayers.Powered: False: { visible: true } True: { visible: false } - enum.WiresVisuals.MaintenancePanelState: - enum.WiresVisualLayers.MaintenancePanel: - True: { visible: false } - False: { visible: true } - type: Machine board: HolopadMachineCircuitboard - type: StationAiWhitelist diff --git a/Resources/Prototypes/Entities/Structures/Storage/morgue.yml b/Resources/Prototypes/Entities/Structures/Storage/morgue.yml index 6071476d5f7..71fdc330a3d 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/morgue.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/morgue.yml @@ -34,7 +34,7 @@ mask: - MachineMask layer: - - HalfWallLayer + - MachineLayer - type: EntityStorage isCollidableWhenOpen: true showContents: false @@ -72,6 +72,7 @@ - type: AntiRottingContainer - type: StaticPrice price: 200 + - type: RequireProjectileTarget - type: entity id: Crematorium @@ -141,3 +142,4 @@ False: { visible: false } - type: Transform anchored: true + - type: RequireProjectileTarget diff --git a/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml b/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml index 9e8a61a5b06..f6e58a19f36 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/fence_metal.yml @@ -56,6 +56,7 @@ noWindowInTile: true highVoltageNode: high mediumVoltageNode: medium + onActivateInWorld: true lowVoltageNode: low - type: NodeContainer nodes: diff --git a/Resources/Prototypes/_Backmen/Entities/Objects/Misc/implanters.yml b/Resources/Prototypes/_Backmen/Entities/Objects/Misc/implanters.yml index f9c6522b6b5..087277ea471 100644 --- a/Resources/Prototypes/_Backmen/Entities/Objects/Misc/implanters.yml +++ b/Resources/Prototypes/_Backmen/Entities/Objects/Misc/implanters.yml @@ -10,8 +10,8 @@ - type: entity id: CombatTrainingImplanter parent: BaseImplantOnlyImplanterSyndi - name: combat-training-implanter-name - description: combat-training-implanter-desc + name: combat training implanter + description: A Syndicate implanter that installs a combat training implant. components: - type: Implanter implant: CombatTrainingImplant @@ -20,8 +20,8 @@ id: CombatTrainingImplanterAdmeme parent: BaseImplantOnlyImplanter suffix: Admeme - name: combat-marksmanship-implanter-admeme-name - description: combat-marksmanship-implanter-admeme-desc + name: combat marksmanship implanter + description: Admin implanter that grants 100% accuracy on the targeted body part. components: - type: Implanter implant: CombatTrainingImplantAdmeme diff --git a/Resources/Prototypes/_Backmen/Entities/Objects/Misc/subdermal_implants.yml b/Resources/Prototypes/_Backmen/Entities/Objects/Misc/subdermal_implants.yml index 0ca4904da29..2f08b0294dd 100644 --- a/Resources/Prototypes/_Backmen/Entities/Objects/Misc/subdermal_implants.yml +++ b/Resources/Prototypes/_Backmen/Entities/Objects/Misc/subdermal_implants.yml @@ -61,8 +61,8 @@ - type: entity parent: BaseSubdermalImplant id: CombatTrainingImplant - name: combat-training-implant-name - description: combat-training-implant-desc + name: combat training implant + description: Improves aimed-shot accuracy to Security-tier levels (~35–40%). categories: [ HideSpawnMenu ] components: - type: SubdermalImplant @@ -73,8 +73,8 @@ parent: BaseSubdermalImplant id: CombatTrainingImplantAdmeme suffix: Admeme - name: combat-marksmanship-implant-admeme-name - description: combat-marksmanship-implant-admeme-desc + name: combat marksmanship implant + description: Admin implant that guarantees hits on the aimed body part. categories: [ HideSpawnMenu ] components: - type: SubdermalImplant diff --git a/RobustToolbox b/RobustToolbox index 960edb32c4d..b7fb6cd70d6 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 960edb32c4dd417496e4667177625d8c3cb14f7e +Subproject commit b7fb6cd70d6ba2d5d73886776a409e6444fd0093 diff --git a/SpaceStation14.slnx b/SpaceStation14.slnx index 5fdcaafd735..d7be0827f6c 100644 --- a/SpaceStation14.slnx +++ b/SpaceStation14.slnx @@ -42,11 +42,8 @@ - - - @@ -61,8 +58,10 @@ + + @@ -73,6 +72,7 @@ + @@ -110,6 +110,10 @@ + + + +